diff --git a/src/App.tsx b/src/App.tsx
index 1e40497..03936be 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,307 +1,15 @@
-import logoAImg from '@/assets/images/a-agape.png';
-import logoImg from '@/assets/images/agape-logo.png';
-import { motion } from 'framer-motion';
-
-import ProfileCard from './views/components/ProfileCard';
-
-import {
- BuildingIcon,
- CalendarIcon,
- CertificateIcon,
- ClockIcon,
-} from '@phosphor-icons/react';
-import { useState } from 'react';
-import { summary } from './views/components/mock/summary';
-import { Navigation } from './views/components/navigation';
-import { StorySlide } from './views/components/story-slide';
-import { Button } from './views/components/ui/button';
-import { Card } from './views/components/ui/card';
-
-import { Badge } from './views/components/ui/badge';
+import { Navigate, Route, Routes } from 'react-router-dom';
+import { LoginSlide } from './views/components/login-slide';
+import { RetrospectiveSlides } from './views/components/retrospective-slides';
export function App() {
return (
-
-
-
+
+ } />
+ } />
+ } />
+
);
}
-export default function Retrospectiva() {
- const total = 6;
- const [current, setCurrent] = useState(0);
- return (
-
-
-
-
-
-
-
-
-
-
-
- Sua Retrospectiva
-
- Ágape {summary.year}
-
-
-
- Um ano de conquistas, eficiência e transparência na gestão pública
-
-
-
-
- Começar
-
-
-
-
- Transparência
- •
- Eficiência
- •
- Parceria
-
-
-
-
-
-
-
-
-
-
-
- Você utilizou
-
-
- {summary.numApplicationsUsed}
-
-
- aplicações Ágape neste ano
-
-
-
-
- {summary.topApplications.map((app, index) => (
-
-
- {app.name}
-
-
- {app.uses} usos
-
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
- Principais Ações
-
-
- As atividades que mais impactaram sua gestão
-
-
-
-
- {summary.topActions.map((action, index) => (
-
-
-
-
-
- {index + 1}
-
-
- {action.action}
-
-
-
- {action.count.toLocaleString('pt-BR')}
-
-
-
-
- ))}
-
-
-
-
-
-
-
-
-
-
-
-
-
- Seu horário preferido
-
-
-
- {summary.favoriteHourRange}
-
-
- É quando você mais acessa as plataformas Ágape
-
-
-
-
-
-
-
-
- {summary.badge.title}
-
-
-
- {summary.badge.subtitle}
-
-
-
- {summary.badge.description}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- A Ágape deseja a você um {summary.year + 1} repleto de sucesso!
-
-
-
- {summary.messageFinal}
-
-
-
-
- © {summary.year} {summary.orgName}
-
- O Futuro da Gestão Pública começa aqui!
-
-
-
-
-
-
console.log('Contact clicked')}
- />
-
-
-
-
-
-
- );
-}
+export default App;
diff --git a/src/app/hooks/useMetrics.ts b/src/app/hooks/useMetrics.ts
new file mode 100644
index 0000000..fa8c72a
--- /dev/null
+++ b/src/app/hooks/useMetrics.ts
@@ -0,0 +1,18 @@
+import { useQuery } from '@tanstack/react-query';
+import { metricsService } from '../services/metrics';
+
+export function useMetricsByPortal(cpf: string, ano: number) {
+ return useQuery({
+ queryKey: ['metricsByPortal', cpf, ano],
+ queryFn: () => metricsService.getMetricsByPortal({ cpf, ano }),
+ enabled: !!cpf && !!ano,
+ });
+}
+
+export function useMetricsBySystems(cpf: string, ano: number) {
+ return useQuery({
+ queryKey: ['metricsBySystems', cpf, ano],
+ queryFn: () => metricsService.getMetricsBySystems({ cpf, ano }),
+ enabled: !!cpf && !!ano,
+ });
+}
diff --git a/src/app/services/index.ts b/src/app/services/index.ts
index fce3918..806180a 100644
--- a/src/app/services/index.ts
+++ b/src/app/services/index.ts
@@ -6,6 +6,12 @@ const api = axios.create({
baseURL: import.meta.env.VITE_API_URL,
});
+// Adicionar token temporariamente aos headers
+const token = import.meta.env.VITE_API_TOKEN || '';
+if (token) {
+ api.defaults.headers.common['Authorization'] = `Bearer ${token}`;
+}
+
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
diff --git a/src/views/components/login-slide.tsx b/src/views/components/login-slide.tsx
new file mode 100644
index 0000000..f44f9d7
--- /dev/null
+++ b/src/views/components/login-slide.tsx
@@ -0,0 +1,148 @@
+import logoImg from '@/assets/images/agape-logo.png';
+import { motion } from 'framer-motion';
+import { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { Button } from './ui/button';
+import { Input } from './ui/input';
+
+export function LoginSlide() {
+ const [cpf, setCpf] = useState('');
+ const [ano, setAno] = useState(new Date().getFullYear().toString());
+ const [errors, setErrors] = useState<{ cpf?: string; ano?: string }>({});
+ const navigate = useNavigate();
+
+ const validateCPF = (cpfValue: string) => {
+ const cleanCPF = cpfValue.replace(/\D/g, '');
+ return cleanCPF.length === 11;
+ };
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ const newErrors: { cpf?: string; ano?: string } = {};
+
+ if (!cpf || !validateCPF(cpf)) {
+ newErrors.cpf = 'CPF inválido (deve ter 11 dígitos)';
+ }
+
+ if (
+ !ano ||
+ parseInt(ano) < 2020 ||
+ parseInt(ano) > new Date().getFullYear()
+ ) {
+ newErrors.ano = `Ano deve estar entre 2020 e ${new Date().getFullYear()}`;
+ }
+
+ if (Object.keys(newErrors).length > 0) {
+ setErrors(newErrors);
+ return;
+ }
+
+ // Navega para a retrospectiva com os parâmetros
+ navigate(`/retrospectiva?cpf=${cpf.replace(/\D/g, '')}&ano=${ano}`);
+ };
+
+ const formatCPF = (value: string) => {
+ const cleanValue = value.replace(/\D/g, '');
+ if (cleanValue.length <= 11) {
+ return cleanValue
+ .replace(/(\d{3})(\d)/, '$1.$2')
+ .replace(/(\d{3})(\d)/, '$1.$2')
+ .replace(/(\d{3})(\d{1,2})/, '$1-$2');
+ }
+ return value;
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ Sua Retrospectiva
+ Ágape {ano}
+
+
+
+
+
+
+ CPF
+
+
{
+ setCpf(formatCPF(e.target.value));
+ if (errors.cpf) {
+ setErrors({ ...errors, cpf: undefined });
+ }
+ }}
+ className="bg-white/20 border-white/30 text-white placeholder:text-white/50"
+ />
+ {errors.cpf &&
{errors.cpf}
}
+
+
+
+
+ Ano
+
+
{
+ setAno(e.target.value);
+ if (errors.ano) {
+ setErrors({ ...errors, ano: undefined });
+ }
+ }}
+ className="bg-white/20 border-white/30 text-white"
+ />
+ {errors.ano &&
{errors.ano}
}
+
+
+
+
+ Ver Minha Retrospectiva
+
+
+
+
+
+ Seus dados são seguros e processados com transparência
+
+
+
+ );
+}
diff --git a/src/views/components/retrospective-slides.tsx b/src/views/components/retrospective-slides.tsx
new file mode 100644
index 0000000..f63e780
--- /dev/null
+++ b/src/views/components/retrospective-slides.tsx
@@ -0,0 +1,402 @@
+import {
+ useMetricsByPortal,
+ useMetricsBySystems,
+} from '@/app/hooks/useMetrics';
+import logoAImg from '@/assets/images/a-agape.png';
+import logoImg from '@/assets/images/agape-logo.png';
+import {
+ BuildingIcon,
+ CalendarIcon,
+ ClockIcon,
+ SpinnerIcon,
+} from '@phosphor-icons/react';
+import { motion } from 'framer-motion';
+import * as React from 'react';
+import { useSearchParams } from 'react-router-dom';
+import { Navigation } from './navigation';
+import ProfileCard from './ProfileCard';
+import { StorySlide } from './story-slide';
+
+
+export function RetrospectiveSlides() {
+ const [searchParams] = useSearchParams();
+ const cpf = searchParams.get('cpf') || '';
+ const ano = searchParams.get('ano')
+ ? parseInt(searchParams.get('ano')!)
+ : new Date().getFullYear();
+
+ const {
+ data: metricsPortal,
+ isLoading: isLoadingPortal,
+ error: errorPortal,
+ } = useMetricsByPortal(cpf, ano);
+
+ const {
+ data: metricsSystems,
+ isLoading: isLoadingSystems,
+ error: errorSystems,
+ } = useMetricsBySystems(cpf, ano);
+
+ const [current, setCurrent] = React.useState(0);
+
+ // Calcula slides baseado nos dados disponíveis
+ const slides: number[] = [];
+ slides.push(0); // Slide inicial
+ if (metricsPortal && metricsPortal.length > 0) slides.push(1); // Slide portais
+ if (metricsSystems && metricsSystems.length > 0) slides.push(2); // Slide sistemas
+ if (
+ (metricsPortal && metricsPortal.length > 0) ||
+ (metricsSystems && metricsSystems.length > 0)
+ ) {
+ slides.push(3); // Slide com informações gerais
+ }
+ slides.push(4); // Slide final
+
+ const total = slides.length;
+
+ const isLoading = isLoadingPortal || isLoadingSystems;
+ const hasError = errorPortal || errorSystems;
+
+ // Extrai total de acessos
+ const totalAcessos = [
+ ...(metricsPortal || []),
+ ...(metricsSystems || []),
+ ].reduce((acc, item) => acc + item.totalAcessos, 0);
+
+
+ // Top sistemas/portais por acessos
+ const topItems = [...(metricsPortal || []), ...(metricsSystems || [])]
+ .sort((a, b) => b.totalAcessos - a.totalAcessos)
+ .slice(0, 5);
+
+ return (
+
+ {isLoading ? (
+
+
+
+
+ Carregando sua retrospectiva...
+
+
+
+ ) : hasError ? (
+
+
+
+ Erro ao carregar dados
+
+
+ Verifique seu CPF e ano, e tente novamente.
+
+
+
+ ) : (
+ <>
+
+
+
+
+
+
+
+
+
+
+ Sua Retrospectiva
+
+ Ágape {ano}
+
+
+
+ Um ano de conquistas, eficiência e transparência na gestão
+ pública
+
+
+
+ Seus dados analisados: CPF •••••••••••
+
+
+
+ Transparência
+ •
+ Eficiência
+ •
+ Parceria
+
+
+
+
+ {metricsPortal && metricsPortal.length > 0 && (
+
+
+
+
+
+
+
+ Seus Portais
+
+
+ {metricsPortal.length}
+
+
+ portais acessados neste ano
+
+
+
+
+ {metricsPortal.map((portal, index) => (
+
+
+ {portal.nomeSistema}
+
+
+ Acessos
+
+
+ {portal.totalAcessos.toLocaleString('pt-BR')}
+
+
+ ))}
+
+
+
+ )}
+
+ {metricsSystems && metricsSystems.length > 0 && (
+
+
+
+
+
+
+
+ Seus Sistemas
+
+
+ {metricsSystems.length}
+
+
+ sistemas utilizados neste ano
+
+
+
+
+ {metricsSystems.map((sistema, index) => (
+
+
+ {sistema.nomeSistema}
+
+
+ Acessos
+
+
+ {sistema.totalAcessos.toLocaleString('pt-BR')}
+
+
+ ))}
+
+
+
+ )}
+
+ {topItems.length > 0 && (
+
+
+
+
+
+
+
+
+
+ Seu Engajamento
+
+
+
+ {totalAcessos.toLocaleString('pt-BR')}
+
+
+ Total de acessos ao longo do ano
+
+
+
+
+
+
+ Principais Recursos
+
+ {topItems.slice(0, 3).map((item, index) => (
+
+
+
{item.nomeSistema}
+
+ {item.totalAcessos.toLocaleString('pt-BR')} acessos
+
+
+
+
+ #{index + 1}
+
+
+
+ ))}
+
+
+
+ )}
+
+
+
+
+
+
+
+
+ A Ágape deseja a você um {ano + 1} repleto de sucesso!
+
+
+
+ Obrigado por fazer parte desta jornada de transformação
+ digital na gestão pública. Sua dedicação é essencial para
+ construirmos um serviço público mais eficiente e
+ transparente.
+
+
+
+ © {ano} Ágape Sistemas e Tecnologia
+
+ O Futuro da Gestão Pública começa aqui!
+
+
+
+
+
+
+
console.log('Contact clicked')}
+ />
+
+
+
+
+
+ >
+ )}
+
+ );
+}
diff --git a/src/views/components/ui/input.tsx b/src/views/components/ui/input.tsx
new file mode 100644
index 0000000..1cf376f
--- /dev/null
+++ b/src/views/components/ui/input.tsx
@@ -0,0 +1,21 @@
+import * as React from 'react';
+
+import { cn } from '@/app/utils';
+
+const Input = React.forwardRef<
+ HTMLInputElement,
+ React.InputHTMLAttributes
+>(({ className, type, ...props }, ref) => (
+
+));
+Input.displayName = 'Input';
+
+export { Input };