From 6ec2453081691ae1149f37c57449cace6028205e Mon Sep 17 00:00:00 2001 From: Gabriel Lira Figueira Date: Thu, 23 Oct 2025 01:39:29 -0300 Subject: [PATCH] =?UTF-8?q?feat:=20refatora=20UI=20e=20corrige=20bugs=20na?= =?UTF-8?q?s=20p=C3=A1ginas=20de=20agendamento?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/context/AppointmentsContext.tsx | 42 +-- app/dev/api-check/page.tsx | 206 +++++++++++ app/page.tsx | 4 +- app/patient/appointments/page.tsx | 468 ++++++++++-------------- app/patient/dashboard/page.tsx | 349 ++++++++++++------ app/patient/layout.tsx | 66 ++-- app/patient/schedule/page.tsx | 540 ++++++++++++++-------------- components/ui/button.tsx | 42 +-- services/agendamentosApi.ts | 87 +++-- services/api/apiService.ts | 221 ++++++++++++ services/api/apiTestData.ts | 61 ++++ services/api/types.ts | 194 ++++++++++ services/disponibilidadeApi.ts | 110 +++++- services/medicosApi.ts | 82 ++++- services/pacientesApi.ts | 81 ++++- 15 files changed, 1758 insertions(+), 795 deletions(-) create mode 100644 app/dev/api-check/page.tsx create mode 100644 services/api/apiService.ts create mode 100644 services/api/apiTestData.ts create mode 100644 services/api/types.ts diff --git a/app/context/AppointmentsContext.tsx b/app/context/AppointmentsContext.tsx index b64d349..aae6eb8 100644 --- a/app/context/AppointmentsContext.tsx +++ b/app/context/AppointmentsContext.tsx @@ -1,16 +1,21 @@ +// Caminho: app/context/AppointmentsContext.tsx (Completo e Corrigido) "use client"; import React, { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react'; -import { agendamentosApi, Appointment } from '@/services/agendamentosApi'; +import { agendamentosApi, Appointment, CreateAppointmentData } from '@/services/agendamentosApi'; +import { usuariosApi, User } from '@/services/usuariosApi'; +import { toast } from "sonner"; + +// As definições de componentes de UI foram REMOVIDAS deste arquivo. +// Elas pertencem aos arquivos que as utilizam, como `dashboard/page.tsx`. export interface AppointmentsContextType { appointments: Appointment[]; isLoading: boolean; error: string | null; fetchAppointments: () => Promise; - addAppointment: (appointmentData: Omit) => Promise; + addAppointment: (appointmentData: CreateAppointmentData) => Promise; updateAppointment: (appointmentId: string, updatedData: Partial>) => Promise; - deleteAppointment: (appointmentId: string) => Promise; } const AppointmentsContext = createContext(undefined); @@ -24,8 +29,13 @@ export function AppointmentsProvider({ children }: { children: ReactNode }) { setIsLoading(true); setError(null); try { - const data = await agendamentosApi.list(); - setAppointments(data || []); + const user = await usuariosApi.getCurrentUser(); + if (user?.id) { + const data = await agendamentosApi.listByPatient(user.id); + setAppointments(data || []); + } else { + setAppointments([]); + } } catch (err) { console.error("Erro ao buscar agendamentos:", err); setError("Não foi possível carregar os agendamentos."); @@ -39,33 +49,24 @@ export function AppointmentsProvider({ children }: { children: ReactNode }) { fetchAppointments(); }, [fetchAppointments]); - const addAppointment = async (appointmentData: Omit) => { + const addAppointment = async (appointmentData: CreateAppointmentData) => { try { await agendamentosApi.create(appointmentData); - await fetchAppointments(); // Recarrega a lista para incluir o novo agendamento + await fetchAppointments(); } catch (err) { console.error("Erro ao adicionar agendamento:", err); setError("Falha ao criar o novo agendamento. Tente novamente."); + throw err; } }; const updateAppointment = async (appointmentId: string, updatedData: Partial>) => { try { - await agendamentosApi.update(appointmentId, updatedData); - await fetchAppointments(); // Recarrega a lista para refletir as alterações + toast.warning("Funcionalidade indisponível.", { description: "A API não suporta a atualização de agendamentos." }); } catch (err) { - console.error("Erro ao atualizar agendamento:", err); + console.error("Erro ao tentar atualizar agendamento:", err); setError("Falha ao atualizar o agendamento. Tente novamente."); - } - }; - - const deleteAppointment = async (appointmentId: string) => { - try { - await agendamentosApi.delete(appointmentId); - await fetchAppointments(); // Recarrega a lista para remover o item excluído - } catch (err) { - console.error("Erro ao excluir agendamento:", err); - setError("Falha ao excluir o agendamento. Tente novamente."); + throw err; } }; @@ -76,7 +77,6 @@ export function AppointmentsProvider({ children }: { children: ReactNode }) { fetchAppointments, addAppointment, updateAppointment, - deleteAppointment, }; return ( diff --git a/app/dev/api-check/page.tsx b/app/dev/api-check/page.tsx new file mode 100644 index 0000000..506d508 --- /dev/null +++ b/app/dev/api-check/page.tsx @@ -0,0 +1,206 @@ +// app/dev/api-check/page.tsx (V2 - Com Mocks e Seções Colapsáveis) + +"use client"; + +import React, { useState } from 'react'; +import { + authService, + userService, + patientService, + doctorService, + scheduleService, + reportService, +} from '@/services/api/apiService'; +import * as TestData from '@/services/api/apiTestData'; +import { + LoginResponse, SendMagicLinkResponse, LogoutResponse, GetCurrentUserResponse, + RequestPasswordResetResponse, HardDeleteUserResponse, RegisterPatientResponse, + ListResponse, Patient, GetAvailableSlotsResponse +} from '@/services/api/types'; + +type ApiResponse = { status: number | 'network_error'; data?: any; error?: Error }; + +const getStyleForResponse = (response: ApiResponse | null): React.CSSProperties => { + if (!response) return {}; + const baseStyle: React.CSSProperties = { padding: '10px', border: '1px solid', borderRadius: '4px', whiteSpace: 'pre-wrap', wordBreak: 'break-all', marginTop: '10px' }; + if (response.status === 'network_error') return { ...baseStyle, backgroundColor: 'lightgoldenrodyellow', borderColor: 'goldenrod' }; + if (response.status >= 200 && response.status < 300) return { ...baseStyle, backgroundColor: 'lightgreen', borderColor: 'green' }; + if (response.status >= 400) return { ...baseStyle, backgroundColor: 'lightcoral', borderColor: 'darkred' }; + return {}; +}; + +const ApiVerificationPage: React.FC = () => { + // --- ESTADOS --- + const [loginData, setLoginData] = useState(TestData.loginTestData.success); + const [loginResponse, setLoginResponse] = useState(null); + const [magicLinkEmail, setMagicLinkEmail] = useState(TestData.magicLinkTestData.success); + const [magicLinkResponse, setMagicLinkResponse] = useState(null); + const [logoutResponse, setLogoutResponse] = useState(null); + const [currentUserResponse, setCurrentUserResponse] = useState(null); + const [resetPassData, setResetPassData] = useState(TestData.resetPassTestData.success); + const [resetPassResponse, setResetPassResponse] = useState(null); + const [deleteUserData, setDeleteUserData] = useState(TestData.deleteUserTestData.success); + const [deleteUserResponse, setDeleteUserResponse] = useState(null); + const [registerPatientData, setRegisterPatientData] = useState(TestData.registerPatientTestData.success); + const [registerPatientResponse, setRegisterPatientResponse] = useState(null); + const [listPatientsFilter, setListPatientsFilter] = useState(TestData.listPatientsTestData.success); + const [listPatientsResponse, setListPatientsResponse] = useState | null>(null); + const [slotsData, setSlotsData] = useState(TestData.slotsTestData.success); + const [slotsResponse, setSlotsResponse] = useState(null); + + // --- HANDLERS --- + const handleApiCall = async (apiFunction: (...args: any[]) => Promise, payload: any, setResponse: React.Dispatch>) => { + setResponse(null); + const response = await apiFunction(payload); + setResponse(response); + }; + const handleApiCallNoPayload = async (apiFunction: () => Promise, setResponse: React.Dispatch>) => { + setResponse(null); + const response = await apiFunction(); + setResponse(response); + }; + const handleRequestPasswordReset = async () => { + setResetPassResponse(null); + const response = await userService.requestPasswordReset(resetPassData.email, resetPassData.redirectUrl || undefined); + setResetPassResponse(response); + }; + const handleGetAvailableSlots = async () => { + setSlotsResponse(null); + const response = await scheduleService.getAvailableSlots(slotsData.doctorId, slotsData.date); + setSlotsResponse(response); + }; + + return ( +
+

Painel de Verificação da API

+

Use este painel para executar cada função do `apiService` e verificar o objeto de resposta completo.

+ +
+

Autenticação

+
+
+

authService.login

+
+ + +
+
{JSON.stringify(loginData, null, 2)}
+ + {loginResponse &&
{JSON.stringify(loginResponse, null, 2)}
} +
+
+
+
+

authService.sendMagicLink

+
+ + +
+
{JSON.stringify(magicLinkEmail, null, 2)}
+ + {magicLinkResponse &&
{JSON.stringify(magicLinkResponse, null, 2)}
} +
+
+
+
+

authService.logout

+ + {logoutResponse &&
{JSON.stringify(logoutResponse, null, 2)}
} +
+
+
+
+

authService.getCurrentUser

+ + {currentUserResponse &&
{JSON.stringify(currentUserResponse, null, 2)}
} +
+
+
+ +
+

Usuários

+
+
+

userService.requestPasswordReset

+
+ + +
+
{JSON.stringify(resetPassData, null, 2)}
+ + {resetPassResponse &&
{JSON.stringify(resetPassResponse, null, 2)}
} +
+
+
+
+

userService.hardDeleteUser_DANGEROUS

+
+ + +
+
{JSON.stringify(deleteUserData, null, 2)}
+ + {deleteUserResponse &&
{JSON.stringify(deleteUserResponse, null, 2)}
} +
+
+
+ +
+

Pacientes

+
+
+

patientService.registerPatient

+
+ + + +
+
{JSON.stringify(registerPatientData, null, 2)}
+ + {registerPatientResponse &&
{JSON.stringify(registerPatientResponse, null, 2)}
} +
+
+
+
+

patientService.list

+
+ + +
+
{JSON.stringify(listPatientsFilter, null, 2)}
+ + {listPatientsResponse &&
{JSON.stringify(listPatientsResponse, null, 2)}
} +
+
+
+ +
+

Agendamentos

+
+
+

scheduleService.getAvailableSlots

+
+ + +
+
{JSON.stringify(slotsData, null, 2)}
+ + {slotsResponse &&
{JSON.stringify(slotsResponse, null, 2)}
} +
+
+
+ + +
+ ); +}; + +export default ApiVerificationPage; \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 85a6806..2b45291 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,3 +1,5 @@ +// app/page.tsx + "use client"; import Link from "next/link" @@ -18,7 +20,7 @@ export default function InicialPage() {

MediConnect