diff --git a/README.md b/README.md index 645e828..174be0b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Audit360 Hotéis +# Audit360 Motéis -Aplicativo web em React + TypeScript para auditorias de hotéis. O projeto foi criado manualmente com Vite e utiliza um backend simulado via `localStorage`. +Aplicativo web em React + TypeScript para auditorias de motéis. O projeto foi criado manualmente com Vite e utiliza um backend simulado via `localStorage`. ## Scripts @@ -13,5 +13,7 @@ Aplicativo web em React + TypeScript para auditorias de hotéis. O projeto foi c - Dashboard com listagem de auditorias e botão para iniciar novas auditorias. - Formulários de checklist com opções "Conforme", "Não conforme leve", "Não conforme grave" e upload de fotos. - Relatórios filtrados por unidade, data e responsável. +- Agenda de reuniões com cadastro de encontros. +- Tela de performance das gestoras (blackbelts) com resumo de auditorias. As informações são salvas no `localStorage` do navegador e as imagens ficam codificadas em base64. diff --git a/index.html b/index.html index 2f9f924..65d025e 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - Audit360 Hotéis + Audit360 Motéis
diff --git a/src/App.tsx b/src/App.tsx index a0b49e5..cff409b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -3,6 +3,8 @@ import Login from './pages/Login'; import Dashboard from './pages/Dashboard'; import AuditForm from './pages/AuditForm'; import Reports from './pages/Reports'; +import Meetings from './pages/Meetings'; +import Performance from './pages/Performance'; import Header from './components/Header'; import { useAuth } from './auth'; @@ -30,6 +32,14 @@ export default function App() { path="/reports" element={} /> + } + /> + } + /> ); diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 18b8de5..c9c52a8 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -5,10 +5,12 @@ export default function Header() { const { user, logout } = useAuth(); return (
-

Audit360 Hotéis

+

Audit360 Motéis

{user && ( diff --git a/src/pages/Meetings.tsx b/src/pages/Meetings.tsx new file mode 100644 index 0000000..93684a2 --- /dev/null +++ b/src/pages/Meetings.tsx @@ -0,0 +1,55 @@ +import { useEffect, useState, FormEvent } from 'react'; +import { useAuth } from '../auth'; +import { Meeting } from '../types'; + +export default function Meetings() { + const { user } = useAuth(); + const [meetings, setMeetings] = useState([]); + const [date, setDate] = useState(''); + const [subject, setSubject] = useState(''); + const [participants, setParticipants] = useState(''); + + useEffect(() => { + const stored = localStorage.getItem('meetings'); + if (stored) setMeetings(JSON.parse(stored)); + }, []); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + const newMeeting: Meeting = { + id: Date.now(), + date, + subject, + participants, + }; + const updated = [...meetings, newMeeting]; + setMeetings(updated); + localStorage.setItem('meetings', JSON.stringify(updated)); + setDate(''); + setSubject(''); + setParticipants(''); + }; + + return ( +
+

Reuniões

+ {user?.role !== 'Supervisor' && ( +
+ + setDate(e.target.value)} required /> + + setSubject(e.target.value)} required /> + + setParticipants(e.target.value)} required /> + +
+ )} + {meetings.map((m) => ( +
+ {m.date} - {m.subject} - {m.participants} +
+ ))} + {meetings.length === 0 &&

Nenhuma reunião agendada.

} +
+ ); +} diff --git a/src/pages/Performance.tsx b/src/pages/Performance.tsx new file mode 100644 index 0000000..34b3717 --- /dev/null +++ b/src/pages/Performance.tsx @@ -0,0 +1,42 @@ +import { useEffect, useState } from 'react'; +import { Audit } from '../types'; + +interface Metric { + responsible: string; + total: number; + severe: number; +} + +export default function Performance() { + const [metrics, setMetrics] = useState([]); + + useEffect(() => { + const stored = localStorage.getItem('audits'); + if (stored) { + const audits: Audit[] = JSON.parse(stored); + const map: Record = {}; + audits.forEach((a) => { + if (!map[a.responsible]) { + map[a.responsible] = { responsible: a.responsible, total: 0, severe: 0 }; + } + map[a.responsible].total += 1; + if (a.answers.some((ans) => ans.status === 'Não conforme grave')) { + map[a.responsible].severe += 1; + } + }); + setMetrics(Object.values(map)); + } + }, []); + + return ( +
+

Performance das Blackbelts

+ {metrics.map((m) => ( +
+ {m.responsible} - Auditorias: {m.total} - Ocorrências graves: {m.severe} +
+ ))} + {metrics.length === 0 &&

Nenhuma auditoria registrada.

} +
+ ); +} diff --git a/src/types.ts b/src/types.ts index 89e84f8..a6cc3f9 100644 --- a/src/types.ts +++ b/src/types.ts @@ -10,3 +10,10 @@ export interface Audit { responsible: string; answers: AuditAnswer[]; } + +export interface Meeting { + id: number; + date: string; + subject: string; + participants: string; +}