diff --git a/frontend/src/components/AppShell.tsx b/frontend/src/components/AppShell.tsx index 8ab36a4..a1c01b4 100644 --- a/frontend/src/components/AppShell.tsx +++ b/frontend/src/components/AppShell.tsx @@ -1,35 +1,54 @@ -import React, { useState } from 'react' -import { Layout, ListTodo, PlusCircle, Columns, LogOut, Settings, Building2, Clock, ChevronDown, Shield, FolderKanban } from 'lucide-react' -import { NavLink, Outlet, Navigate } from 'react-router-dom' -import { useAuth } from '../contexts/AuthContext' -import { OnboardingModal } from './OnboardingModal' +import React, { useState } from "react"; +import { + Layout, + ListTodo, + PlusCircle, + Columns, + LogOut, + Settings, + Building2, + Clock, + ChevronDown, + Shield, + FolderKanban, +} from "lucide-react"; +import { NavLink, Outlet, Navigate, Link } from "react-router-dom"; +import { useAuth } from "../contexts/AuthContext"; +import { OnboardingModal } from "./OnboardingModal"; -import { cn } from '../lib/utils' -import { motion } from 'framer-motion' +import { cn } from "../lib/utils"; +import { motion } from "framer-motion"; const navLinkClassName = ({ isActive }: { isActive: boolean }) => cn( - 'inline-flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-semibold transition-all duration-200', + "inline-flex items-center gap-2 rounded-xl px-4 py-2 text-sm font-semibold transition-all duration-200", isActive - ? 'bg-brand text-white shadow-md shadow-brand/20' - : 'text-text-muted hover:bg-surface-muted hover:text-text-main', - ) + ? "bg-brand text-white shadow-md shadow-brand/20" + : "text-text-muted hover:bg-surface-muted hover:text-text-main", + ); export function AppShell() { - const { user, isAuthenticated, isLoading, logout, activeOrg, setActiveOrg } = useAuth() - const [showOrgModal, setShowOrgModal] = useState(false) - const [showOrgSwitcher, setShowOrgSwitcher] = useState(false) + const { user, isAuthenticated, isLoading, logout, activeOrg, setActiveOrg } = + useAuth(); + const [showOrgModal, setShowOrgModal] = useState(false); + const [showOrgSwitcher, setShowOrgSwitcher] = useState(false); + + React.useEffect(() => { + if (isAuthenticated && user && user.organizations.length === 0) { + setShowOrgModal(true); + } + }, [isAuthenticated, user]); if (isLoading) { return (
- ) + ); } if (!isAuthenticated) { - return + return ; } return ( @@ -65,20 +84,26 @@ export function AppShell() {
- {activeOrg?.name || 'Selecionar Org'} + {activeOrg?.name || "Selecionar Org"} - {activeOrg?.roles[0] || 'Visitante'} + {activeOrg?.roles[0] || "Visitante"}
- + {showOrgSwitcher && ( <> -
setShowOrgSwitcher(false)} +
setShowOrgSwitcher(false)} />
-
+
{org.name.substring(0, 2).toUpperCase()}
- {org.name} + + {org.name} + - {org.roles.join(', ')} + {org.roles.join(", ")}
- {org.id === activeOrg?.id &&
} + {org.id === activeOrg?.id && ( +
+ )} ))}
- +
@@ -159,17 +190,14 @@ export function AppShell() { Nova Tarefa - - {user?.organizations.some(org => org.roles.includes('OWNER') || org.roles.includes('ADMIN')) && ( + + {user?.organizations.some( + (org) => + org.roles.includes("OWNER") || org.roles.includes("ADMIN"), + ) && ( Gestão @@ -181,15 +209,15 @@ export function AppShell() {
- {user?.name} {user?.name} - - {user?.organizations.some(org => org.roles.includes('OWNER') || org.roles.includes('ADMIN')) && ( + + {user?.organizations.some( + (org) => org.roles.includes("OWNER") || org.roles.includes("ADMIN"), + ) && ( Gestão @@ -249,7 +273,10 @@ export function AppShell() { )} - setShowOrgModal(false)} /> + setShowOrgModal(false)} + />
- ) + ); } diff --git a/frontend/src/components/OnboardingModal.tsx b/frontend/src/components/OnboardingModal.tsx index 24fd18e..fca570f 100644 --- a/frontend/src/components/OnboardingModal.tsx +++ b/frontend/src/components/OnboardingModal.tsx @@ -55,7 +55,10 @@ export const OnboardingModal: React.FC = ({ isOpen, onClos
+ ) : ( +
+ Selecionada +
+ )} + + {(isOwner || isAdmin) && ( + + + + )} +
+
+ + {/* Decorations */} +
+ + ) + })} + + {/* Quick Create Card */} + +
+ +
+
+

Criar nova organização

+

Inicie um novo projeto com sua equipe.

+
+ +
+ + + {/* Discovery Section */} +
+
+

+ Procurando uma equipe? +

+

+ Explore organizações públicas ou use um código de convite para se juntar aos seus colegas de trabalho. +

+
+ + + Buscar Organizações + + +
+
+ + {/* Abstract shapes */} +
+
+ +
+ + {/* Info Section */} +
+
+
+ +
+

Como funciona?

+

+ Organizações são espaços isolados para seus projetos e tarefas. Você pode ser dono de várias ou participar como colaborador em outras. Toda a sua atividade é filtrada pela organização ativa. +

+ + Saiba mais sobre permissões + + +
+ +
+
+ +
+

Segurança & Funções

+

+ Cada organização define seus próprios papéis (OWNER, ADMIN, MEMBER). Apenas administradores e donos podem gerenciar membros e configurações do espaço. +

+ + Ver detalhes de segurança + + +
+
+
+ ) +} diff --git a/frontend/src/router.tsx b/frontend/src/router.tsx index dc76e8b..82a8c11 100644 --- a/frontend/src/router.tsx +++ b/frontend/src/router.tsx @@ -11,6 +11,7 @@ import { JoinOrganizationPage } from "./pages/JoinOrganizationPage"; import { OrganizationAdminPage } from "./pages/OrganizationAdminPage"; import { MyRequestsPage } from "./pages/MyRequestsPage"; import { ProjectsPage } from "./pages/ProjectsPage"; +import { OrganizationsPage } from "./pages/OrganizationsPage"; export const router = createBrowserRouter([ { @@ -57,6 +58,10 @@ export const router = createBrowserRouter([ path: "projects", element: , }, + { + path: "organizations", + element: , + }, ], }, ]);