Compare commits

..

1 Commits

Author SHA1 Message Date
Rodribm10
3d87744dd2 Add meetings and performance pages 2025-07-15 08:59:52 -03:00
7 changed files with 122 additions and 4 deletions

View File

@ -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.

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Audit360 Hotéis</title>
<title>Audit360 Motéis</title>
</head>
<body>
<div id="root"></div>

View File

@ -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={<PrivateRoute><Reports /></PrivateRoute>}
/>
<Route
path="/meetings"
element={<PrivateRoute><Meetings /></PrivateRoute>}
/>
<Route
path="/performance"
element={<PrivateRoute><Performance /></PrivateRoute>}
/>
</Routes>
</>
);

View File

@ -5,10 +5,12 @@ export default function Header() {
const { user, logout } = useAuth();
return (
<header>
<h1>Audit360 Hotéis</h1>
<h1>Audit360 Motéis</h1>
<nav>
<Link to="/">Auditorias</Link>
<Link to="/reports">Relatórios</Link>
<Link to="/meetings">Reuniões</Link>
<Link to="/performance">Performance</Link>
</nav>
<div>
{user && (

55
src/pages/Meetings.tsx Normal file
View File

@ -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<Meeting[]>([]);
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 (
<div className="container">
<h2>Reuniões</h2>
{user?.role !== 'Supervisor' && (
<form onSubmit={handleSubmit} style={{ marginBottom: '1rem' }}>
<label>Data</label>
<input type="date" value={date} onChange={(e) => setDate(e.target.value)} required />
<label>Assunto</label>
<input value={subject} onChange={(e) => setSubject(e.target.value)} required />
<label>Participantes</label>
<input value={participants} onChange={(e) => setParticipants(e.target.value)} required />
<button type="submit">Agendar</button>
</form>
)}
{meetings.map((m) => (
<div key={m.id} className="audit-item">
<strong>{m.date}</strong> - {m.subject} - {m.participants}
</div>
))}
{meetings.length === 0 && <p>Nenhuma reunião agendada.</p>}
</div>
);
}

42
src/pages/Performance.tsx Normal file
View File

@ -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<Metric[]>([]);
useEffect(() => {
const stored = localStorage.getItem('audits');
if (stored) {
const audits: Audit[] = JSON.parse(stored);
const map: Record<string, Metric> = {};
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 (
<div className="container">
<h2>Performance das Blackbelts</h2>
{metrics.map((m) => (
<div key={m.responsible} className="audit-item">
<strong>{m.responsible}</strong> - Auditorias: {m.total} - Ocorrências graves: {m.severe}
</div>
))}
{metrics.length === 0 && <p>Nenhuma auditoria registrada.</p>}
</div>
);
}

View File

@ -10,3 +10,10 @@ export interface Audit {
responsible: string;
answers: AuditAnswer[];
}
export interface Meeting {
id: number;
date: string;
subject: string;
participants: string;
}