fix: offcava
This commit is contained in:
@@ -14,7 +14,7 @@ import { PiCheckCircleFill } from "react-icons/pi";
|
|||||||
import { useGetWorkshopsSearch } from "../../hooks/useGetWorkshopsSearch";
|
import { useGetWorkshopsSearch } from "../../hooks/useGetWorkshopsSearch";
|
||||||
import { imageSkeletonFadeStyle, onImageSkeletonLoad } from "../../utils/imageSkeleton";
|
import { imageSkeletonFadeStyle, onImageSkeletonLoad } from "../../utils/imageSkeleton";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
/* import { useGetCurrentUser } from "../../hooks/useGetCurrentUser"; */
|
import { useGetCurrentUser } from "../../hooks/useGetCurrentUser";
|
||||||
|
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
@@ -30,23 +30,21 @@ export default function Header() {
|
|||||||
const { getVideos } = useGetVideos();
|
const { getVideos } = useGetVideos();
|
||||||
const { getVideosSearch } = useGetVideosSearch();
|
const { getVideosSearch } = useGetVideosSearch();
|
||||||
const { getWorkshopsSearch } = useGetWorkshopsSearch();
|
const { getWorkshopsSearch } = useGetWorkshopsSearch();
|
||||||
/* const { getCurrentUser } = useGetCurrentUser(); */
|
const { getCurrentUser } = useGetCurrentUser();
|
||||||
const [role, _setRole] = useState(0);
|
|
||||||
const [videosWatched, setVideosWatched] = useState(0);
|
const [videosWatched, setVideosWatched] = useState(0);
|
||||||
const [videosCount, setVideosCount] = useState(0);
|
const [videosCount, setVideosCount] = useState(0);
|
||||||
|
const [role, setRole] = useState(0);
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchAll = async () => {
|
const fetchCurrentUser = async () => {
|
||||||
const videosWatched = localStorage.getItem("videosWatched");
|
const currentUser = await getCurrentUser();
|
||||||
setVideosWatched(videosWatched ? parseInt(videosWatched) : 0);
|
setRole(currentUser.data.role_id);
|
||||||
const videosCount = localStorage.getItem("videosCount");
|
setVideosWatched(currentUser.data.videosWatched);
|
||||||
setVideosCount(videosCount ? parseInt(videosCount) : 0);
|
setVideosCount(currentUser.data.videosCount);
|
||||||
/* const userData = await getCurrentUser();
|
|
||||||
setRole(userData.data.role_id); */
|
|
||||||
};
|
};
|
||||||
fetchAll();
|
fetchCurrentUser();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleCloseMenu = () => setShowMenu(false);
|
const handleCloseMenu = () => setShowMenu(false);
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ export default function Sidebar() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<nav className={`${styles.nav}`}>
|
<nav className={`${styles.nav}`}>
|
||||||
{role === 1 ? (
|
|
||||||
<ul className={styles.navList}>
|
<ul className={styles.navList}>
|
||||||
<li className={`${styles.navItem} text-start`}>
|
<li className={`${styles.navItem} text-start`}>
|
||||||
<NavLink className={({ isActive }) => isActive ? `${styles.navLink} ${styles.navLinkActive}` : styles.navLink} to="/dashboard"> <LuLayoutDashboard className="me-2" size={24} /> {sideMenu ? "Dashboard" : ""} </NavLink>
|
<NavLink className={({ isActive }) => isActive ? `${styles.navLink} ${styles.navLinkActive}` : styles.navLink} to="/dashboard"> <LuLayoutDashboard className="me-2" size={24} /> {sideMenu ? "Dashboard" : ""} </NavLink>
|
||||||
@@ -45,29 +44,15 @@ export default function Sidebar() {
|
|||||||
<li className={`${styles.navItem} text-start`}>
|
<li className={`${styles.navItem} text-start`}>
|
||||||
<NavLink className={({ isActive }) => isActive ? `${styles.navLink} ${styles.navLinkActive}` : styles.navLink} to="/workshops"> <LuGraduationCap className="me-2" size={24} /> {sideMenu ? "Workshops" : ""} </NavLink>
|
<NavLink className={({ isActive }) => isActive ? `${styles.navLink} ${styles.navLinkActive}` : styles.navLink} to="/workshops"> <LuGraduationCap className="me-2" size={24} /> {sideMenu ? "Workshops" : ""} </NavLink>
|
||||||
</li>
|
</li>
|
||||||
|
{role === 1 ? (
|
||||||
<li className={`${styles.navItem} text-start`}>
|
<li className={`${styles.navItem} text-start`}>
|
||||||
<NavLink className={({ isActive }) => isActive ? `${styles.navLink} ${styles.navLinkActive}` : styles.navLink} to="/admin/users"> <LuUsers className="me-2" size={24} /> {sideMenu ? "Utilizadores" : ""} </NavLink>
|
<NavLink className={({ isActive }) => isActive ? `${styles.navLink} ${styles.navLinkActive}` : styles.navLink} to="/admin/users"> <LuUsers className="me-2" size={24} /> {sideMenu ? "Utilizadores" : ""} </NavLink>
|
||||||
</li>
|
</li>
|
||||||
|
) : null}
|
||||||
<li className={`${styles.navItem} text-start`}>
|
<li className={`${styles.navItem} text-start`}>
|
||||||
<NavLink className={({ isActive }) => isActive ? `${styles.navLink} ${styles.navLinkActive}` : styles.navLink} to="/contactos"> <LuMail className="me-2" size={24} /> {sideMenu ? "Contactos" : ""} </NavLink>
|
<NavLink className={({ isActive }) => isActive ? `${styles.navLink} ${styles.navLinkActive}` : styles.navLink} to="/contactos"> <LuMail className="me-2" size={24} /> {sideMenu ? "Contactos" : ""} </NavLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
|
||||||
<ul className={styles.navList}>
|
|
||||||
<li className={`${styles.navItem} text-start`}>
|
|
||||||
<NavLink className={({ isActive }) => isActive ? `${styles.navLink} ${styles.navLinkActive}` : styles.navLink} to="/dashboard"> <LuLayoutDashboard className="me-2" size={24} /> {sideMenu ? "Dashboard" : ""} </NavLink>
|
|
||||||
</li>
|
|
||||||
<li className={`${styles.navItem} text-start`}>
|
|
||||||
<NavLink className={({ isActive }) => isActive ? `${styles.navLink} ${styles.navLinkActive}` : styles.navLink} to="/videos"> <LuTvMinimalPlay className="me-2" size={24} />Videos</NavLink>
|
|
||||||
</li>
|
|
||||||
<li className={`${styles.navItem} text-start`}>
|
|
||||||
<NavLink className={({ isActive }) => isActive ? `${styles.navLink} ${styles.navLinkActive}` : styles.navLink} to="/workshops"> <LuGraduationCap className="me-2" size={24} /> {sideMenu ? "Workshops" : ""} </NavLink>
|
|
||||||
</li>
|
|
||||||
<li className={`${styles.navItem} text-start`}>
|
|
||||||
<NavLink className={({ isActive }) => isActive ? `${styles.navLink} ${styles.navLinkActive}` : styles.navLink} to="/contactos"> <LuMail className="me-2" size={24} /> {sideMenu ? "Contactos" : ""} </NavLink>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
)}
|
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className={styles.logoutWrapper}>
|
<div className={styles.logoutWrapper}>
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ export function useGetVideos() {
|
|||||||
meta: data.meta,
|
meta: data.meta,
|
||||||
role: data.role,
|
role: data.role,
|
||||||
categories: data.categories as Category[],
|
categories: data.categories as Category[],
|
||||||
|
videosActive: data.videosActive,
|
||||||
|
videosWatched: data.videosWatched,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return data as ApiErrorResponse;
|
return data as ApiErrorResponse;
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ export default function Videos() {
|
|||||||
setCurrentPage(videosData.meta.current_page);
|
setCurrentPage(videosData.meta.current_page);
|
||||||
setRole(videosData.role);
|
setRole(videosData.role);
|
||||||
setCategories(videosData.categories);
|
setCategories(videosData.categories);
|
||||||
|
localStorage.setItem("videosActive", videosData.videosActive.toString());
|
||||||
|
localStorage.setItem("videosWatched", videosData.videosWatched.toString());
|
||||||
} else {
|
} else {
|
||||||
setVideos([]);
|
setVideos([]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import API_URL from "../../../config/api";
|
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Link, useParams } from "react-router";
|
import { Link, useParams } from "react-router";
|
||||||
import { CgSpinner } from "react-icons/cg";
|
import { CgSpinner } from "react-icons/cg";
|
||||||
@@ -7,32 +6,27 @@ import { LuArrowLeft, LuCalendar, LuClock3, LuUsers } from "react-icons/lu";
|
|||||||
import "react-datepicker/dist/react-datepicker.css";
|
import "react-datepicker/dist/react-datepicker.css";
|
||||||
import Swal from "sweetalert2";
|
import Swal from "sweetalert2";
|
||||||
import styles from "./styles.module.css";
|
import styles from "./styles.module.css";
|
||||||
import { useGetCurrentUser } from "../../../hooks/useGetCurrentUser";
|
|
||||||
import { imageSkeletonFadeStyle, onImageSkeletonLoad } from "../../../utils/imageSkeleton";
|
import { imageSkeletonFadeStyle, onImageSkeletonLoad } from "../../../utils/imageSkeleton";
|
||||||
|
|
||||||
export default function Workshop() {
|
export default function Workshop() {
|
||||||
const user = JSON.parse(localStorage.getItem("user") as unknown as string) as User;
|
|
||||||
const isAdmin = user.role_id === 1;
|
|
||||||
|
|
||||||
|
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [workshop, setWorkshop] = useState<Workshop | null>(null);
|
const [workshop, setWorkshop] = useState<Workshop | null>(null);
|
||||||
const [error, setError] = useState<ApiErrorResponse | null>(null);
|
const [error, setError] = useState<ApiErrorResponse | null>(null);
|
||||||
const { getCurrentUser } = useGetCurrentUser();
|
const [role, setRole] = useState(0);
|
||||||
const [currentUserData, setCurrentUserData] = useState<User | null>(null);
|
const [userId, setUserId] = useState(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchAll = async () => {
|
const fetchAll = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
const [workshopData, currentUserData] = await Promise.all([
|
const [workshopData] = await Promise.all([
|
||||||
getWorkshop(),
|
getWorkshop(),
|
||||||
getCurrentUser(),
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
setWorkshop(workshopData as Workshop);
|
setWorkshop(workshopData?.workshop as Workshop);
|
||||||
setCurrentUserData(currentUserData.data);
|
setRole(workshopData?.role as number);
|
||||||
|
setUserId(workshopData?.userId as number);
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
};
|
};
|
||||||
@@ -42,7 +36,7 @@ export default function Workshop() {
|
|||||||
|
|
||||||
async function getWorkshop() {
|
async function getWorkshop() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${API_URL}/api/workshop/${id}`, {
|
const response = await fetch(`http://127.0.0.1:8000/api/workshop/${id}`, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
@@ -54,7 +48,11 @@ export default function Workshop() {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
return data.data as Workshop;
|
return {
|
||||||
|
workshop: data.data as Workshop,
|
||||||
|
role: data.role as number,
|
||||||
|
userId: data.userId as number,
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
setError(data as ApiErrorResponse);
|
setError(data as ApiErrorResponse);
|
||||||
return null;
|
return null;
|
||||||
@@ -67,7 +65,7 @@ export default function Workshop() {
|
|||||||
|
|
||||||
/* Inscrever num workshop */
|
/* Inscrever num workshop */
|
||||||
async function inscrever(workshopId: number) {
|
async function inscrever(workshopId: number) {
|
||||||
const response = await fetch(`${API_URL}/api/inscrever/${workshopId}`, {
|
const response = await fetch(`http://127.0.0.1:8000/api/inscrever/${workshopId}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
@@ -86,7 +84,7 @@ export default function Workshop() {
|
|||||||
showCloseButton: true,
|
showCloseButton: true,
|
||||||
});
|
});
|
||||||
const workshop = await getWorkshop();
|
const workshop = await getWorkshop();
|
||||||
setWorkshop(workshop as Workshop);
|
setWorkshop(workshop?.workshop as Workshop);
|
||||||
} else {
|
} else {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: data.message,
|
title: data.message,
|
||||||
@@ -99,7 +97,7 @@ export default function Workshop() {
|
|||||||
|
|
||||||
/* Cancelar inscrição num workshop */
|
/* Cancelar inscrição num workshop */
|
||||||
async function cancelarInscricao(workshopId: number) {
|
async function cancelarInscricao(workshopId: number) {
|
||||||
const response = await fetch(`${API_URL}/api/cancelar-inscricao/${workshopId}`, {
|
const response = await fetch(`http://127.0.0.1:8000/api/cancelar-inscricao/${workshopId}`, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers: {
|
headers: {
|
||||||
Accept: "application/json",
|
Accept: "application/json",
|
||||||
@@ -118,7 +116,7 @@ export default function Workshop() {
|
|||||||
showCloseButton: true,
|
showCloseButton: true,
|
||||||
});
|
});
|
||||||
const workshop = await getWorkshop();
|
const workshop = await getWorkshop();
|
||||||
setWorkshop(workshop as Workshop);
|
setWorkshop(workshop?.workshop as Workshop);
|
||||||
} else {
|
} else {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: data.message,
|
title: data.message,
|
||||||
@@ -133,6 +131,7 @@ export default function Workshop() {
|
|||||||
return (
|
return (
|
||||||
<div className="text-center mt-5 d-flex flex-column gap-2 align-items-center">
|
<div className="text-center mt-5 d-flex flex-column gap-2 align-items-center">
|
||||||
<CgSpinner className={`${styles.animateSpin} text-2xl fs-3`} />
|
<CgSpinner className={`${styles.animateSpin} text-2xl fs-3`} />
|
||||||
|
<span>A carregar dados do workshop...</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -159,7 +158,7 @@ export default function Workshop() {
|
|||||||
<div className="row mt-4 g-3 gx-md-4 gx-lg-5 ms-0">
|
<div className="row mt-4 g-3 gx-md-4 gx-lg-5 ms-0">
|
||||||
<div className={`${styles.thumbnailWrapper} col-12 col-lg-3 align-self-center align-self-sm-center px-0 mt-0`}>
|
<div className={`${styles.thumbnailWrapper} col-12 col-lg-3 align-self-center align-self-sm-center px-0 mt-0`}>
|
||||||
<img
|
<img
|
||||||
src={`${API_URL}/storage/${workshop.image}`}
|
src={`http://127.0.0.1:8000/storage/${workshop.image}`}
|
||||||
alt={workshop.title}
|
alt={workshop.title}
|
||||||
className={`${styles.thumbnail} w-100`}
|
className={`${styles.thumbnail} w-100`}
|
||||||
style={imageSkeletonFadeStyle}
|
style={imageSkeletonFadeStyle}
|
||||||
@@ -190,20 +189,20 @@ export default function Workshop() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{role !== 1 ? (
|
||||||
<div className="col-12 mx-auto ms-sm-0 mt-5 pt-3">
|
<div className="col-12 mx-auto ms-sm-0 mt-5 pt-3">
|
||||||
{!isAdmin ? (
|
{workshop.users.some((user: User) => user.id === userId) ? (
|
||||||
currentUserData && workshop.users.some((user: User) => user.id === currentUserData.id) ? (
|
<button type="button" className={`${styles.btncancelarInscricao} btn text-center mx-auto py-2 text-decoration-none`} onClick={() => cancelarInscricao(workshop.id)} key={workshop.id} style={{ width: '180px' }}>
|
||||||
<button type="button" className={`${styles.btncancelarInscricao} btn text-center mx-auto py-2 text-decoration-none`} onClick={() => { cancelarInscricao(workshop.id); getWorkshop(); }} key={workshop.id} style={{ width: '180px' }}>
|
|
||||||
Anular inscrição
|
Anular inscrição
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button type="button" className={`${styles.btnInscrever} btn text-center mx-auto py-2 px-4 text-decoration-none`} onClick={() => { inscrever(workshop.id); getWorkshop(); }} key={workshop.id} style={{ width: '180px' }}>
|
<button type="button" className={`${styles.btnInscrever} btn text-center mx-auto py-2 px-4 text-decoration-none`} onClick={() => inscrever(workshop.id)} key={workshop.id} style={{ width: '180px' }}>
|
||||||
Inscrever-me
|
Inscrever-me
|
||||||
</button>
|
</button>
|
||||||
)
|
)}
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ export type User = {
|
|||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
};
|
};
|
||||||
|
videosWatched: number;
|
||||||
|
videosCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UpdateUserResponse = {
|
export type UpdateUserResponse = {
|
||||||
@@ -125,6 +127,7 @@ export type Workshop = {
|
|||||||
is_active: boolean;
|
is_active: boolean;
|
||||||
users: User[];
|
users: User[];
|
||||||
role: number;
|
role: number;
|
||||||
|
userId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NextWorkshopsResponse = {
|
export type NextWorkshopsResponse = {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Hash;
|
|||||||
use Tymon\JWTAuth\Facades\JWTAuth;
|
use Tymon\JWTAuth\Facades\JWTAuth;
|
||||||
use Tymon\JWTAuth\Exceptions\JWTException;
|
use Tymon\JWTAuth\Exceptions\JWTException;
|
||||||
use App\Http\Requests\LoginRequest;
|
use App\Http\Requests\LoginRequest;
|
||||||
|
use App\Models\Video;
|
||||||
|
|
||||||
class AuthController extends Controller
|
class AuthController extends Controller
|
||||||
{
|
{
|
||||||
@@ -47,7 +48,7 @@ class AuthController extends Controller
|
|||||||
"password" => $request->password
|
"password" => $request->password
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if(!$login) {
|
if (!$login) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => 'Credenciais inválidas',
|
'message' => 'Credenciais inválidas',
|
||||||
'errors' => null,
|
'errors' => null,
|
||||||
@@ -103,12 +104,20 @@ class AuthController extends Controller
|
|||||||
public function me()
|
public function me()
|
||||||
{
|
{
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
$videosWatched = Video::select('id')
|
||||||
|
->with('views')
|
||||||
|
->whereHas('views', function ($q) use ($user) {
|
||||||
|
$q->where('user_id', $user->id);
|
||||||
|
})->count();
|
||||||
|
$videosCount = Video::select('id')->where('is_active', true)->count();
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => 'Utilizador obtido com sucesso',
|
'message' => 'Utilizador obtido com sucesso',
|
||||||
'data' => [
|
'data' => [
|
||||||
'id' => $user->id,
|
'id' => $user->id,
|
||||||
'role_id' => $user->role_id,
|
'role_id' => $user->role_id,
|
||||||
|
'videosWatched' => $videosWatched,
|
||||||
|
'videosCount' => $videosCount,
|
||||||
],
|
],
|
||||||
'errors' => null,
|
'errors' => null,
|
||||||
], 200);
|
], 200);
|
||||||
|
|||||||
Reference in New Issue
Block a user