- Estamos construindo recursos incríveis de CRM e ERP para sua agência.
- Em breve você terá acesso a análises detalhadas, gestão completa de clientes e muito mais.
+
+
+ {/* Header */}
+
+
+ Visão Geral da Agência
+
+
+ Acompanhe o desempenho de todos os módulos em tempo real
+ );
+}
diff --git a/front-end-agency/app/(agency)/page.tsx b/front-end-agency/app/(agency)/page.tsx
new file mode 100644
index 0000000..e837c6b
--- /dev/null
+++ b/front-end-agency/app/(agency)/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from 'next/navigation';
+
+export default function AgencyRootPage() {
+ redirect('/dashboard');
+}
diff --git a/front-end-agency/app/(agency)/projetos/page.tsx b/front-end-agency/app/(agency)/projetos/page.tsx
new file mode 100644
index 0000000..c0bebb6
--- /dev/null
+++ b/front-end-agency/app/(agency)/projetos/page.tsx
@@ -0,0 +1,10 @@
+export default function ProjetosPage() {
+ return (
+
+
Projetos
+
+
Gestão de Projetos em breve
+
+
+ );
+}
diff --git a/front-end-agency/app/(agency)/social/page.tsx b/front-end-agency/app/(agency)/social/page.tsx
new file mode 100644
index 0000000..a21a9bb
--- /dev/null
+++ b/front-end-agency/app/(agency)/social/page.tsx
@@ -0,0 +1,10 @@
+export default function SocialPage() {
+ return (
+
+
Gestão de Redes Sociais
+
+
Planejamento e Publicação de Posts em breve
+
+
+ );
+}
diff --git a/front-end-agency/app/login/page.tsx b/front-end-agency/app/login/page.tsx
index 37157e2..60052b4 100644
--- a/front-end-agency/app/login/page.tsx
+++ b/front-end-agency/app/login/page.tsx
@@ -4,7 +4,8 @@ import { useState, useEffect } from "react";
import Link from "next/link";
import { Button, Input, Checkbox } from "@/components/ui";
import toast, { Toaster } from 'react-hot-toast';
-import { saveAuth, isAuthenticated } from '@/lib/auth';
+import { saveAuth, isAuthenticated, getToken, clearAuth } from '@/lib/auth';
+import { API_ENDPOINTS } from '@/lib/api';
import dynamic from 'next/dynamic';
const ThemeToggle = dynamic(() => import('@/components/ThemeToggle'), { ssr: false });
@@ -53,8 +54,26 @@ export default function LoginPage() {
}
if (isAuthenticated()) {
- const target = superAdmin ? '/superadmin' : '/dashboard';
- window.location.href = target;
+ // Validar token antes de redirecionar para evitar loops
+ const token = getToken();
+ fetch(API_ENDPOINTS.me, {
+ headers: {
+ 'Authorization': `Bearer ${token}`
+ }
+ })
+ .then(res => {
+ if (res.ok) {
+ const target = superAdmin ? '/superadmin' : '/dashboard';
+ window.location.href = target;
+ } else {
+ // Token inválido ou expirado
+ clearAuth();
+ }
+ })
+ .catch((err) => {
+ console.error('Erro ao validar sessão:', err);
+ // Em caso de erro de rede, não redireciona nem limpa, deixa o usuário tentar logar
+ });
}
}
}, []);
diff --git a/front-end-agency/components/auth/AuthGuard.tsx b/front-end-agency/components/auth/AuthGuard.tsx
new file mode 100644
index 0000000..e4da0d1
--- /dev/null
+++ b/front-end-agency/components/auth/AuthGuard.tsx
@@ -0,0 +1,47 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+import { useRouter, usePathname } from 'next/navigation';
+import { isAuthenticated } from '@/lib/auth';
+
+export default function AuthGuard({ children }: { children: React.ReactNode }) {
+ const router = useRouter();
+ const pathname = usePathname();
+ const [authorized, setAuthorized] = useState(false);
+
+ useEffect(() => {
+ const checkAuth = () => {
+ const isAuth = isAuthenticated();
+
+ if (!isAuth) {
+ setAuthorized(false);
+ // Evitar redirect loop se já estiver no login (embora o AuthGuard deva ser usado apenas em rotas protegidas)
+ if (pathname !== '/login') {
+ router.push('/login');
+ }
+ } else {
+ setAuthorized(true);
+ }
+ };
+
+ checkAuth();
+
+ // Opcional: Adicionar listener para storage events para logout em outras abas
+ const handleStorageChange = (e: StorageEvent) => {
+ if (e.key === 'token' || e.key === 'user') {
+ checkAuth();
+ }
+ };
+
+ window.addEventListener('storage', handleStorageChange);
+ return () => window.removeEventListener('storage', handleStorageChange);
+ }, [router, pathname]);
+
+ // Enquanto verifica, não renderiza nada ou um loading
+ // Para evitar "flash" de conteúdo não autorizado
+ if (!authorized) {
+ return null;
+ }
+
+ return <>{children}>;
+}
diff --git a/front-end-agency/components/layout/DashboardLayout.tsx b/front-end-agency/components/layout/DashboardLayout.tsx
new file mode 100644
index 0000000..1d3d950
--- /dev/null
+++ b/front-end-agency/components/layout/DashboardLayout.tsx
@@ -0,0 +1,40 @@
+'use client';
+
+import React, { useState } from 'react';
+import { SidebarRail, MenuItem } from './SidebarRail';
+import { TopBar } from './TopBar';
+
+interface DashboardLayoutProps {
+ children: React.ReactNode;
+ menuItems: MenuItem[];
+}
+
+export const DashboardLayout: React.FC = ({ children, menuItems }) => {
+ // Estado centralizado do layout
+ const [isExpanded, setIsExpanded] = useState(true);
+ const [activeTab, setActiveTab] = useState('dashboard');
+
+ return (
+
+ {/* Sidebar controla seu próprio estado visual via props */}
+ setIsExpanded(!isExpanded)}
+ menuItems={menuItems}
+ />
+
+ {/* Área de Conteúdo (Children) */}
+
+ {/* TopBar com Breadcrumbs e Search */}
+
+
+ {/* Conteúdo das páginas */}
+