diff --git a/frontend/src/components/AppShell.tsx b/frontend/src/components/AppShell.tsx index 31a4907..90963cb 100644 --- a/frontend/src/components/AppShell.tsx +++ b/frontend/src/components/AppShell.tsx @@ -251,10 +251,13 @@ export function AppShell() { )} - - - Projetos - + {(activeOrg?.roles.includes("OWNER") || + activeOrg?.roles.includes("ADMIN")) && ( + + + Projetos + + )} {/* Organizações Dropdown */}
@@ -318,10 +321,8 @@ export function AppShell() { Minhas Solicitações - {user?.organizations.some( - (org) => - org.roles.includes("OWNER") || - org.roles.includes("ADMIN"), + {activeOrg?.roles.some( + (role) => role === "OWNER" || role === "ADMIN", ) && ( )}
- - - Projetos - + {(activeOrg?.roles.includes("OWNER") || + activeOrg?.roles.includes("ADMIN")) && ( + + + Projetos + + )} {/* Mobile Organizações Group */}
@@ -479,10 +483,8 @@ export function AppShell() { Pedidos - {user?.organizations.some( - (org) => - org.roles.includes("OWNER") || - org.roles.includes("ADMIN"), + {activeOrg?.roles.some( + (role) => role === "OWNER" || role === "ADMIN", ) && ( +
+
+ ); + } + + if (!activeOrg) { + return ( +
+
+ +
+

+ Nenhuma Organização Ativa +

+

+ Selecione uma organização no menu superior para gerenciar seus + recursos. +

+
+ ); + } + + const isAuthorized = activeOrg.roles.some((role) => + allowedRoles.includes(role), + ); + + if (!isAuthorized) { + return ( +
+
+ +
+

+ Acesso Restrito +

+

+ {message || "Você não tem permissão para acessar esta área nesta organização."} +

+
+ ); + } + + return <>{children}; +} diff --git a/frontend/src/pages/OrganizationAdminPage.tsx b/frontend/src/pages/OrganizationAdminPage.tsx index 9ff292e..380d7a1 100644 --- a/frontend/src/pages/OrganizationAdminPage.tsx +++ b/frontend/src/pages/OrganizationAdminPage.tsx @@ -1,61 +1,53 @@ -import React, { useEffect, useState } from 'react' -import { motion } from 'framer-motion' -import { Users, Check, X, ShieldAlert, Mail } from 'lucide-react' -import { organizationService, type JoinRequest } from '../services/organizationService' -import { useAuth } from '../contexts/AuthContext' +import React, { useEffect, useState } from "react"; +import { motion } from "framer-motion"; +import { Users, Check, X, ShieldAlert, Mail } from "lucide-react"; +import { + organizationService, + type JoinRequest, +} from "../services/organizationService"; +import { useAuth } from "../contexts/AuthContext"; export const OrganizationAdminPage: React.FC = () => { - const { user } = useAuth() - const [requests, setRequests] = useState([]) - const [isLoading, setIsLoading] = useState(true) + const { activeOrg } = useAuth(); + const [requests, setRequests] = useState([]); + const [isLoading, setIsLoading] = useState(true); - // For this simplified version, we manage the first organization where the user is OWNER - const orgToManage = user?.organizations.find(org => org.roles.includes('OWNER') || org.roles.includes('ADMIN')) + const orgToManage = activeOrg!; useEffect(() => { if (orgToManage) { - loadRequests() + loadRequests(); } - }, [orgToManage]) + }, [orgToManage]); const loadRequests = async () => { - if (!orgToManage) return + if (!orgToManage) return; try { - const data = await organizationService.listPendingRequests(orgToManage.id) - setRequests(data) + const data = await organizationService.listPendingRequests( + orgToManage.id, + ); + setRequests(data); } catch (error) { - console.error('Failed to load requests', error) + console.error("Failed to load requests", error); } finally { - setIsLoading(false) + setIsLoading(false); } - } + }; const handleAction = async (userId: number, approved: boolean) => { - if (!orgToManage) return + if (!orgToManage) return; try { if (approved) { - await organizationService.approveRequest(orgToManage.id, userId) + await organizationService.approveRequest(orgToManage.id, userId); } else { - await organizationService.rejectRequest(orgToManage.id, userId) + await organizationService.rejectRequest(orgToManage.id, userId); } - setRequests(prev => prev.filter(r => r.userId !== userId)) + setRequests((prev) => prev.filter((r) => r.userId !== userId)); } catch (error) { - console.error('Action failed', error) - alert('Erro ao processar solicitação.') + console.error("Action failed", error); + alert("Erro ao processar solicitação."); } - } - - if (!orgToManage) { - return ( -
-
- -
-

Acesso Negado

-

Você não tem permissão para gerenciar nenhuma organização.

-
- ) - } + }; return (
@@ -76,7 +68,9 @@ export const OrganizationAdminPage: React.FC = () => {
-

Solicitações Pendentes

+

+ Solicitações Pendentes +

{requests.length} novos @@ -102,12 +96,17 @@ export const OrganizationAdminPage: React.FC = () => { >
{request.name}
-

{request.name}

+

+ {request.name} +

{request.email} @@ -137,5 +136,5 @@ export const OrganizationAdminPage: React.FC = () => {
- ) -} + ); +}; diff --git a/frontend/src/pages/ProjectsPage.tsx b/frontend/src/pages/ProjectsPage.tsx index 40058c4..209c7d2 100644 --- a/frontend/src/pages/ProjectsPage.tsx +++ b/frontend/src/pages/ProjectsPage.tsx @@ -72,23 +72,6 @@ export const ProjectsPage: React.FC = () => { p.name.toLowerCase().includes(searchQuery.toLowerCase()), ); - if (!activeOrg) { - return ( -
-
- -
-

- Nenhuma Organização Ativa -

-

- Selecione uma organização no menu superior para gerenciar seus - projetos. -

-
- ); - } - return (
{/* Header Section */} diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index 9a83c86..32e455f 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -13,6 +13,7 @@ import { MyRequestsPage } from "./pages/MyRequestsPage"; import { ProjectsPage } from "./pages/ProjectsPage"; import { OrganizationsPage } from "./pages/OrganizationsPage"; import { DashboardPage } from "./pages/DashboardPage"; +import { RoleGate } from "./components/RoleGate"; export const router = createBrowserRouter([ { @@ -53,7 +54,11 @@ export const router = createBrowserRouter([ }, { path: "admin", - element: , + element: ( + + + + ), }, { path: "requests", @@ -61,7 +66,11 @@ export const router = createBrowserRouter([ }, { path: "projects", - element: , + element: ( + + + + ), }, { path: "organizations",