From b02a7d176af116d25c1d38ea4ac0c406db8d502a Mon Sep 17 00:00:00 2001 From: Erik Date: Thu, 27 Nov 2025 19:12:26 -0300 Subject: [PATCH] feat: dashboard com dados reais de projetos, servicos e contatos --- frontend/package-lock.json | 15 ++ frontend/src/app/admin/page.tsx | 287 +++++++++++++++++++++++++++----- frontend/tsconfig.json | 16 +- 3 files changed, 268 insertions(+), 50 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index eff8fa0..675f993 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8297,6 +8297,21 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.0.tgz", + "integrity": "sha512-J1YdKuJv9xcixzXR24Dv+4SaDKc2jj31IVUEMdO5xJivMTXuE6MAdIi4qPjSymHuFG8O5wbfWKnhJUcHHpj5CA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } } } } diff --git a/frontend/src/app/admin/page.tsx b/frontend/src/app/admin/page.tsx index a5b1229..0e6fa87 100644 --- a/frontend/src/app/admin/page.tsx +++ b/frontend/src/app/admin/page.tsx @@ -1,4 +1,146 @@ +"use client"; + +import { useState, useEffect } from 'react'; +import Link from 'next/link'; + +interface Project { + id: string; + title: string; + category: string; + status: string; + coverImage: string | null; + createdAt: string; +} + +interface Service { + id: string; + title: string; + icon: string; + active: boolean; +} + +interface Contact { + id: string; + name: string; + email: string; + subject: string | null; + message: string; + createdAt: string; + read: boolean; +} + +interface Stats { + projects: number; + activeProjects: number; + services: number; + activeServices: number; + contacts: number; + unreadContacts: number; +} + export default function AdminDashboard() { + const [stats, setStats] = useState({ + projects: 0, + activeProjects: 0, + services: 0, + activeServices: 0, + contacts: 0, + unreadContacts: 0, + }); + const [recentProjects, setRecentProjects] = useState([]); + const [recentContacts, setRecentContacts] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + async function fetchData() { + try { + // Buscar projetos + const projectsRes = await fetch('/api/projects'); + const projects: Project[] = projectsRes.ok ? await projectsRes.json() : []; + + // Buscar serviços + const servicesRes = await fetch('/api/services'); + const services: Service[] = servicesRes.ok ? await servicesRes.json() : []; + + // Buscar contatos + const contactsRes = await fetch('/api/contacts'); + const contacts: Contact[] = contactsRes.ok ? await contactsRes.json() : []; + + // Calcular estatísticas + setStats({ + projects: projects.length, + activeProjects: projects.filter(p => p.status === 'Concluído' || p.status === 'Em andamento').length, + services: services.length, + activeServices: services.filter(s => s.active).length, + contacts: contacts.length, + unreadContacts: contacts.filter(c => !c.read).length, + }); + + // Projetos recentes (últimos 5) + setRecentProjects( + projects + .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) + .slice(0, 5) + ); + + // Contatos recentes (últimos 5) + setRecentContacts( + contacts + .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()) + .slice(0, 5) + ); + } catch (error) { + console.error('Erro ao carregar dados do dashboard:', error); + } finally { + setLoading(false); + } + } + + fetchData(); + }, []); + + const formatTimeAgo = (dateString: string) => { + const date = new Date(dateString); + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMins = Math.floor(diffMs / 60000); + const diffHours = Math.floor(diffMs / 3600000); + const diffDays = Math.floor(diffMs / 86400000); + + if (diffMins < 60) return `Há ${diffMins} min`; + if (diffHours < 24) return `Há ${diffHours}h`; + if (diffDays < 7) return `Há ${diffDays}d`; + return date.toLocaleDateString('pt-BR'); + }; + + const getInitials = (name: string) => { + return name + .split(' ') + .map(n => n[0]) + .slice(0, 2) + .join('') + .toUpperCase(); + }; + + const getStatusStyle = (status: string) => { + switch (status) { + case 'Concluído': + return 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'; + case 'Em andamento': + return 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400'; + default: + return 'bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400'; + } + }; + + if (loading) { + return ( +
+ +
+ ); + } + return (
@@ -7,71 +149,124 @@ export default function AdminDashboard() {
- {[ - { label: 'Projetos Ativos', value: '12', icon: 'ri-briefcase-line', color: 'text-blue-500', bg: 'bg-blue-50 dark:bg-blue-900/20' }, - { label: 'Mensagens Novas', value: '5', icon: 'ri-message-3-line', color: 'text-green-500', bg: 'bg-green-50 dark:bg-green-900/20' }, - { label: 'Serviços', value: '8', icon: 'ri-tools-line', color: 'text-orange-500', bg: 'bg-orange-50 dark:bg-orange-900/20' }, - { label: 'Visitas Hoje', value: '145', icon: 'ri-eye-line', color: 'text-purple-500', bg: 'bg-purple-50 dark:bg-purple-900/20' }, - ].map((stat, index) => ( -
-
-
- -
- {stat.value} + +
+
+
-

{stat.label}

+ {stats.projects}
- ))} +

Projetos

+

{stats.activeProjects} ativos

+ + + +
+
+ +
+ {stats.contacts} +
+

Mensagens

+

{stats.unreadContacts} não lidas

+ + + +
+
+ +
+ {stats.services} +
+

Serviços

+

{stats.activeServices} ativos

+ + +
+
+
+ +
+ +
+

Visitas

+

Em breve

+

Últimas Mensagens

- + Ver todas
- {[1, 2, 3].map((i) => ( -
-
- JD -
-
-
-

João da Silva

- Há 2 horas + {recentContacts.length === 0 ? ( +

Nenhuma mensagem recebida.

+ ) : ( + recentContacts.map((contact) => ( + +
+ {getInitials(contact.name)}
-

- Gostaria de solicitar um orçamento para adequação de frota conforme NR-12... -

-
-
- ))} +
+
+

+ {contact.name} + {!contact.read && ( + + )} +

+ {formatTimeAgo(contact.createdAt)} +
+

+ {contact.message} +

+
+ + )) + )}

Projetos Recentes

- + Ver todos
- {[1, 2, 3].map((i) => ( -
-
- {/* Placeholder image */} -
-
-
-

Adequação Coca-Cola

-

Engenharia Veicular

-
- - Concluído - -
- ))} + {recentProjects.length === 0 ? ( +

Nenhum projeto cadastrado.

+ ) : ( + recentProjects.map((project) => ( + +
+ {project.coverImage ? ( + {project.title} + ) : ( +
+ +
+ )} +
+
+

{project.title}

+

{project.category}

+
+ + {project.status} + + + )) + )}
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index cf9c65d..34939b2 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2017", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -11,7 +15,7 @@ "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "react-jsx", + "jsx": "preserve", "incremental": true, "plugins": [ { @@ -19,7 +23,9 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": [ + "./src/*" + ] } }, "include": [ @@ -30,5 +36,7 @@ ".next/dev/types/**/*.ts", "**/*.mts" ], - "exclude": ["node_modules"] + "exclude": [ + "node_modules" + ] }