diff --git a/App.tsx b/App.tsx index a4f6728..70acd7a 100644 --- a/App.tsx +++ b/App.tsx @@ -1,34 +1,48 @@ -import React, { useState, useEffect } from 'react'; -import { HashRouter as Router, Routes, Route, Navigate, useLocation } from 'react-router-dom'; -import { Layout } from './components/Layout'; -import { Dashboard } from './pages/Dashboard'; -import { UserDetail } from './pages/UserDetail'; -import { AttendanceDetail } from './pages/AttendanceDetail'; -import { SuperAdmin } from './pages/SuperAdmin'; -import { ApiKeys } from './pages/ApiKeys'; -import { TeamManagement } from './pages/TeamManagement'; -import { Teams } from './pages/Teams'; -import { Funnels } from './pages/Funnels'; -import { Origins } from './pages/Origins'; -import { Login } from './pages/Login'; -import { ForgotPassword } from './pages/ForgotPassword'; -import { ResetPassword } from './pages/ResetPassword'; -import { SetupAccount } from './pages/SetupAccount'; -import { UserProfile } from './pages/UserProfile'; -import { getUserById, logout } from './services/dataService'; -import { User } from './types'; +import React, { useState, useEffect } from "react"; +import { + HashRouter as Router, + Routes, + Route, + Navigate, + useLocation, +} from "react-router-dom"; +import { Layout } from "./components/Layout"; +import { Dashboard } from "./pages/Dashboard"; +import { UserDetail } from "./pages/UserDetail"; +import { AttendanceDetail } from "./pages/AttendanceDetail"; +import { SuperAdmin } from "./pages/SuperAdmin"; +import { ApiKeys } from "./pages/ApiKeys"; +import { TeamManagement } from "./pages/TeamManagement"; +import { Teams } from "./pages/Teams"; +import { Funnels } from "./pages/Funnels"; +import { Origins } from "./pages/Origins"; +import { Login } from "./pages/Login"; +import { ForgotPassword } from "./pages/ForgotPassword"; +import { ResetPassword } from "./pages/ResetPassword"; +import { SetupAccount } from "./pages/SetupAccount"; +import { UserProfile } from "./pages/UserProfile"; +import { getUserById, logout } from "./services/dataService"; +import { User } from "./types"; -const AuthGuard: React.FC<{ children: React.ReactNode, roles?: string[] }> = ({ children, roles }) => { +const AuthGuard: React.FC<{ children: React.ReactNode; roles?: string[] }> = ({ + children, + roles, +}) => { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const location = useLocation(); useEffect(() => { const checkAuth = async () => { - const storedUserId = localStorage.getItem('ctms_user_id'); - const storedToken = localStorage.getItem('ctms_token'); + const storedUserId = localStorage.getItem("ctms_user_id"); + const storedToken = localStorage.getItem("ctms_token"); - if (!storedUserId || !storedToken || storedToken === 'undefined' || storedToken === 'null') { + if ( + !storedUserId || + !storedToken || + storedToken === "undefined" || + storedToken === "null" + ) { if (storedToken) logout(); // Limpar se for "undefined" string setLoading(false); return; @@ -37,7 +51,7 @@ const AuthGuard: React.FC<{ children: React.ReactNode, roles?: string[] }> = ({ try { const fetchedUser = await getUserById(storedUserId); if (fetchedUser) { - if (fetchedUser.status === 'active') { + if (fetchedUser.status === "active") { setUser(fetchedUser); } else { // User explicitly marked inactive or deleted @@ -53,7 +67,7 @@ const AuthGuard: React.FC<{ children: React.ReactNode, roles?: string[] }> = ({ } } catch (err) { console.error("Auth check failed (network/server error):", err); - // DO NOT logout() here. If the server is offline or restarting, + // DO NOT logout() here. If the server is offline or restarting, // we shouldn't wipe the user's local storage tokens. // We just leave the user as null, which will redirect them to login, // but their tokens remain so they can auto-login when the server is back. @@ -66,7 +80,11 @@ const AuthGuard: React.FC<{ children: React.ReactNode, roles?: string[] }> = ({ }, [location.pathname]); if (loading) { - return
Carregando...
; + return ( +
+ Carregando... +
+ ); } if (!user) { @@ -78,7 +96,7 @@ const AuthGuard: React.FC<{ children: React.ReactNode, roles?: string[] }> = ({ } // Auto-redirect Super Admins away from the standard dashboard to their specific panel - if (location.pathname === '/' && user.role === 'super_admin') { + if (location.pathname === "/" && user.role === "super_admin") { return ; } @@ -93,16 +111,86 @@ const App: React.FC = () => { } /> } /> } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> } /> diff --git a/GEMINI.md b/GEMINI.md index 974bc1b..af37a0c 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -7,38 +7,44 @@ Fasto is a commercial team management system built with React (Vite) on the fron We have transitioned from a mock-based prototype to a **secure, multi-tenant production architecture**: - **Multi-Tenancy & Data Isolation:** All backend routes (Users, Teams, Attendances) now strictly enforce `tenant_id` checks. It is technically impossible for one organization to query data from another. -- **God Mode (Tenant Impersonation):** Super Admins can securely impersonate Tenant Admins via a specialized, temporary JWT (`/api/impersonate/:tenantId`). This allows seamless cross-domain support without storing passwords, while strictly maintaining state isolation through forced React reloads and locking mechanisms. -- **Dynamic Funnel Customization:** - - Funnel stages are no longer hardcoded ENUMs. Each tenant can create multiple dynamic funnels via the `funnels` and `funnel_stages` tables. - - Managers can assign specific Teams to specific Funnels. - - The Dashboard, User Detail, and Attendance Detail pages now dynamically map and color-code stages based on the active team's assigned funnel. -- **Real-Time Notification System:** - - Built a persistent notification tray (`/api/notifications`) with real-time polling (10s intervals) and a hidden HTML5 `