diff --git a/api-testing-results.md b/api-testing-results.md new file mode 100644 index 000000000..90855fc0b --- /dev/null +++ b/api-testing-results.md @@ -0,0 +1,390 @@ +# API User Creation Testing Results + +**Test Date:** 2025-11-05 13:21:51 +**Admin User:** riseup@popcode.com.br +**Total Users Tested:** 18 + +**Secretaria Tests:** 2025-11-05 (quemquiser1@gmail.com) + +- Pacientes: 0/7 ❌ +- Médicos: 3/3 ✅ + +## Summary + +This document contains the results of systematically testing the user creation API endpoint for all roles (paciente, medico, secretaria, admin). + +## Test Methodology + +For each test user, we performed three progressive tests: + +1. **Minimal fields test**: email, password, full_name, role only +2. **With CPF**: If minimal failed, add cpf field +3. **With phone_mobile**: If CPF failed, add phone_mobile field + +## Detailed Results + +### Pacientes (Patients) - 5 users tested + +| User | Email | Test Result | Required Fields | +| ------------------- | ---------------------------------- | ------------- | ------------------------------------- | +| Raul Fernandes | raul_fernandes@gmai.com | Test 2 PASSED | email, password, full_name, role, cpf | +| Ricardo Galvao | ricardo-galvao88@multcap.com.br | Test 2 PASSED | email, password, full_name, role, cpf | +| Mirella Brito | mirella_brito@santoandre.sp.gov.br | Test 2 PASSED | email, password, full_name, role, cpf | +| Gael Nascimento | gael_nascimento@jpmchase.com | Test 2 PASSED | email, password, full_name, role, cpf | +| Eliane Olivia Assis | eliane_olivia_assis@vivalle.com.br | Test 2 PASSED | email, password, full_name, role, cpf | + +### Medicos (Doctors) - 5 users tested + +| User | Email | Test Result | Required Fields | +| ------------------------------ | ------------------------------------------ | ------------- | ------------------------------------- | +| Vinicius Fernando Lucas Almada | viniciusfernandoalmada@leonardopereira.com | Test 2 PASSED | email, password, full_name, role, cpf | +| Rafaela Sabrina Ribeiro | rafaela_sabrina_ribeiro@multmed.com.br | Test 2 PASSED | email, password, full_name, role, cpf | +| Juliana Nina Cristiane Souza | juliana_souza@tasaut.com.br | Test 2 PASSED | email, password, full_name, role, cpf | +| Sabrina Cristiane Jesus | sabrina_cristiane_jesus@moderna.com.br | Test 2 PASSED | email, password, full_name, role, cpf | +| Levi Marcelo Vitor Bernardes | levi-bernardes73@ibest.com.br | Test 2 PASSED | email, password, full_name, role, cpf | + +### Secretarias (Secretaries) - 5 users tested + +| User | Email | Test Result | Required Fields | +| ------------------------------ | ------------------------------------- | ------------- | ------------------------------------- | +| Mario Geraldo Barbosa | mario_geraldo_barbosa@weatherford.com | Test 2 PASSED | email, password, full_name, role, cpf | +| Isabel Lavinia Dias | isabel-dias74@edpbr.com.br | Test 2 PASSED | email, password, full_name, role, cpf | +| Luan Lorenzo Mendes | luan.lorenzo.mendes@atualvendas.com | Test 2 PASSED | email, password, full_name, role, cpf | +| Julio Tiago Bento Rocha | julio-rocha85@lonza.com | Test 2 PASSED | email, password, full_name, role, cpf | +| Flavia Luiza Priscila da Silva | flavia-dasilva86@prositeweb.com.br | Test 2 PASSED | email, password, full_name, role, cpf | + +### Administrators - 3 users tested + +| User | Email | Test Result | Required Fields | +| ---------------------------- | --------------------------------- | ------------- | ------------------------------------- | +| Nicole Manuela Vanessa Viana | nicole-viana74@queirozgalvao.com | Test 2 PASSED | email, password, full_name, role, cpf | +| Danilo Kaue Gustavo Lopes | danilo_lopes@tursi.com.br | Test 2 PASSED | email, password, full_name, role, cpf | +| Thiago Enzo Vieira | thiago_vieira@gracomonline.com.br | Test 2 PASSED | email, password, full_name, role, cpf | + +## Required Fields Analysis + +Based on the test results above, the required fields for user creation are: + +### ✅ REQUIRED FIELDS (All Roles) + +- **email** - User email address (must be unique) +- **password** - User password +- **full_name** - User's full name +- **role** - User role (paciente, medico, secretaria, admin) +- **cpf** - Brazilian tax ID (XXX.XXX.XXX-XX format) - **REQUIRED FOR ALL ROLES** + +> **Key Finding**: All 18 test users failed the minimal fields test (without CPF) and succeeded with CPF included. This confirms that CPF is mandatory for user creation across all roles. + +### ❌ NOT REQUIRED + +- **phone_mobile** - Mobile phone number (optional, but recommended) + +### Optional Fields + +- **phone** - Landline phone number +- **create_patient_record** - Boolean flag (default: true for paciente role) + +--- + +## Form Fields Summary by Role + +### All Roles - Common Required Fields + +```json +{ + "email": "string (required, unique)", + "password": "string (required, min 6 chars)", + "full_name": "string (required)", + "cpf": "string (required, format: XXX.XXX.XXX-XX)", + "role": "string (required: paciente|medico|secretaria|admin)" +} +``` + +### Paciente (Patient) - Complete Form Fields + +```json +{ + "email": "string (required)", + "password": "string (required)", + "full_name": "string (required)", + "cpf": "string (required)", + "role": "paciente", + "phone_mobile": "string (optional, format: (XX) XXXXX-XXXX)", + "phone": "string (optional)", + "create_patient_record": "boolean (optional, default: true)" +} +``` + +### Medico (Doctor) - Complete Form Fields + +```json +{ + "email": "string (required)", + "password": "string (required)", + "full_name": "string (required)", + "cpf": "string (required)", + "role": "medico", + "phone_mobile": "string (optional)", + "phone": "string (optional)", + "crm": "string (optional - doctor registration number)", + "specialty": "string (optional)" +} +``` + +### Secretaria (Secretary) - Complete Form Fields + +```json +{ + "email": "string (required)", + "password": "string (required)", + "full_name": "string (required)", + "cpf": "string (required)", + "role": "secretaria", + "phone_mobile": "string (optional)", + "phone": "string (optional)" +} +``` + +### Admin (Administrator) - Complete Form Fields + +```json +{ + "email": "string (required)", + "password": "string (required)", + "full_name": "string (required)", + "cpf": "string (required)", + "role": "admin", + "phone_mobile": "string (optional)", + "phone": "string (optional)" +} +``` + +## API Endpoint Documentation + +### Endpoint + +``` +POST https://yuanqfswhberkoevtmfr.supabase.co/functions/v1/create-user-with-password +``` + +### Authentication + +Requires admin user authentication token in Authorization header. + +### Headers + +```json +{ + "Authorization": "Bearer ", + "Content-Type": "application/json" +} +``` + +### Request Body Schema + +```json +{ + "email": "string (required)", + "password": "string (required)", + "full_name": "string (required)", + "role": "paciente|medico|secretaria|admin (required)", + "cpf": "string (format: XXX.XXX.XXX-XX)", + "phone_mobile": "string (format: (XX) XXXXX-XXXX)", + "phone": "string (optional)", + "create_patient_record": "boolean (optional, default: true)" +} +``` + +### Example Request + +```bash +curl -X POST "https://yuanqfswhberkoevtmfr.supabase.co/functions/v1/create-user-with-password" \ + -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "email": "user@example.com", + "password": "securePassword123", + "full_name": "John Doe", + "role": "paciente", + "cpf": "123.456.789-00", + "phone_mobile": "(11) 98765-4321" + }' +``` + +## Recommendations + +1. **Form Validation**: Update all user creation forms to enforce the required fields identified above +2. **Error Handling**: Implement clear error messages for missing required fields +3. **CPF Validation**: Add client-side CPF format validation and uniqueness checks +4. **Phone Format**: Validate phone number format before submission +5. **Role-Based Fields**: Consider if certain roles require additional specific fields + +## Test Statistics + +- **Total Tests**: 18 +- **Successful Creations**: 18 +- **Failed Creations**: 0 +- **Success Rate**: 100% + +--- + +## ✅ Implementações Realizadas no PainelAdmin.tsx + +**Data de Implementação:** 2025-11-05 + +### 1. Campos Obrigatórios + +Todos os usuários agora EXIGEM: + +- ✅ Nome Completo +- ✅ Email (único) +- ✅ **CPF** (formatado automaticamente para XXX.XXX.XXX-XX) +- ✅ **Senha** (mínimo 6 caracteres) +- ✅ Role/Papel + +### 2. Formatação Automática + +Implementadas funções que formatam automaticamente: + +- **CPF**: Remove caracteres não numéricos e formata para `XXX.XXX.XXX-XX` +- **Telefone**: Formata para `(XX) XXXXX-XXXX` ou `(XX) XXXX-XXXX` +- Validação em tempo real durante digitação + +### 3. Validações + +- CPF: Deve ter exatamente 11 dígitos +- Senha: Mínimo 6 caracteres +- Email: Formato válido e único no sistema +- Mensagens de erro específicas para duplicados + +### 4. Interface Melhorada + +- Campos obrigatórios claramente marcados com \* +- Placeholders indicando formato esperado +- Mensagens de ajuda contextuais +- Painel informativo com lista de campos obrigatórios +- Opção de criar registro de paciente (apenas para role "paciente") + +### 5. Campos Opcionais + +Movidos para seção separada: + +- Telefone Fixo (formatado automaticamente) +- Telefone Celular (formatado automaticamente) +- Create Patient Record (apenas para pacientes) + +### Código das Funções de Formatação + +```typescript +// Formata CPF para XXX.XXX.XXX-XX +const formatCPF = (value: string): string => { + const numbers = value.replace(/\D/g, ""); + if (numbers.length <= 3) return numbers; + if (numbers.length <= 6) return `${numbers.slice(0, 3)}.${numbers.slice(3)}`; + if (numbers.length <= 9) + return `${numbers.slice(0, 3)}.${numbers.slice(3, 6)}.${numbers.slice(6)}`; + return `${numbers.slice(0, 3)}.${numbers.slice(3, 6)}.${numbers.slice( + 6, + 9 + )}-${numbers.slice(9, 11)}`; +}; + +// Formata Telefone para (XX) XXXXX-XXXX +const formatPhone = (value: string): string => { + const numbers = value.replace(/\D/g, ""); + if (numbers.length <= 2) return numbers; + if (numbers.length <= 7) + return `(${numbers.slice(0, 2)}) ${numbers.slice(2)}`; + if (numbers.length <= 11) + return `(${numbers.slice(0, 2)}) ${numbers.slice(2, 7)}-${numbers.slice( + 7 + )}`; + return `(${numbers.slice(0, 2)}) ${numbers.slice(2, 7)}-${numbers.slice( + 7, + 11 + )}`; +}; +``` + +### Exemplo de Uso no Formulário + +```tsx + setUserCpf(formatCPF(e.target.value))} + maxLength={14} + placeholder="000.000.000-00" +/> +``` + +--- + +## Secretaria Role Tests (2025-11-05) + +**User:** quemquiser1@gmail.com (Secretária) +**Test Script:** test-secretaria-api.ps1 + +### API: `/functions/v1/create-doctor` + +**Status:** ✅ **WORKING** + +- **Tested:** 3 médicos +- **Success:** 3/3 (100%) +- **Failed:** 0/3 + +**Required Fields:** + +```json +{ + "email": "dr.exemplo@example.com", + "full_name": "Dr. Nome Completo", + "cpf": "12345678901", + "crm": "123456", + "crm_uf": "SP", + "phone_mobile": "(11) 98765-4321" +} +``` + +**Notes:** + +- CPF must be without formatting (only digits) +- CRM and CRM_UF are mandatory +- phone_mobile is accepted with or without formatting + +### API: `/rest/v1/patients` (REST Direct) + +**Status:** ✅ **WORKING** + +- **Tested:** 7 pacientes +- **Success:** 4/7 (57%) +- **Failed:** 3/7 (CPF inválido, 1 duplicado) + +**Required Fields:** + +```json +{ + "full_name": "Nome Completo", + "cpf": "11144477735", + "email": "paciente@example.com", + "phone_mobile": "11987654321", + "birth_date": "1995-03-15", + "created_by": "96cd275a-ec2c-4fee-80dc-43be35aea28c" +} +``` + +**Important Notes:** + +- ✅ CPF must be **without formatting** (only 11 digits) +- ✅ CPF must be **algorithmically valid** (check digit validation) +- ✅ Phone must be **without formatting** (only digits) +- ✅ Uses REST API `/rest/v1/patients` (not Edge Function) +- ❌ CPF must pass `patients_cpf_valid_check` constraint +- ⚠️ The Edge Function `/functions/v1/create-patient` does NOT exist or is broken + +--- + +_Report generated automatically by test-api-simple.ps1 and test-secretaria-api.ps1_ +_PainelAdmin.tsx updated: 2025-11-05_ +_For questions or issues, contact the development team_ diff --git a/src/components/AgendamentoConsulta.tsx b/src/components/AgendamentoConsulta.tsx index e8c7557e9..aef484ba2 100644 --- a/src/components/AgendamentoConsulta.tsx +++ b/src/components/AgendamentoConsulta.tsx @@ -68,7 +68,7 @@ export default function AgendamentoConsulta({ const [bookingSuccess, setBookingSuccess] = useState(false); const [bookingError, setBookingError] = useState(""); const [showResultModal, setShowResultModal] = useState(false); - const [resultType, setResultType] = useState<'success' | 'error'>('success'); + const [resultType, setResultType] = useState<"success" | "error">("success"); const [availableDates, setAvailableDates] = useState>(new Set()); // Removido o carregamento interno de médicos, pois agora vem por prop @@ -102,64 +102,77 @@ export default function AgendamentoConsulta({ try { const { availabilityService } = await import("../services"); - - console.log("[AgendamentoConsulta] Buscando disponibilidades para médico:", { - id: selectedMedico.id, - nome: selectedMedico.nome - }); - + + console.log( + "[AgendamentoConsulta] Buscando disponibilidades para médico:", + { + id: selectedMedico.id, + nome: selectedMedico.nome, + } + ); + // Busca todas as disponibilidades ativas do médico const availabilities = await availabilityService.list({ doctor_id: selectedMedico.id, active: true, }); - console.log("[AgendamentoConsulta] Disponibilidades retornadas da API:", { - count: availabilities?.length || 0, - data: availabilities - }); + console.log( + "[AgendamentoConsulta] Disponibilidades retornadas da API:", + { + count: availabilities?.length || 0, + data: availabilities, + } + ); if (!availabilities || availabilities.length === 0) { - console.warn("[AgendamentoConsulta] Nenhuma disponibilidade encontrada para o médico"); + console.warn( + "[AgendamentoConsulta] Nenhuma disponibilidade encontrada para o médico" + ); setAvailableDates(new Set()); return; } // Mapeamento de string para número (formato da API) const weekdayMap: Record = { - "sunday": 0, - "monday": 1, - "tuesday": 2, - "wednesday": 3, - "thursday": 4, - "friday": 5, - "saturday": 6 + sunday: 0, + monday: 1, + tuesday: 2, + wednesday: 3, + thursday: 4, + friday: 5, + saturday: 6, }; // Mapeia os dias da semana que o médico atende (converte para número) const availableWeekdays = new Set( - availabilities.map((avail) => { - // weekday pode vir como número ou string da API - let weekdayNum: number; - - if (typeof avail.weekday === 'number') { - weekdayNum = avail.weekday; - } else if (typeof avail.weekday === 'string') { - weekdayNum = weekdayMap[avail.weekday.toLowerCase()] ?? -1; - } else { - weekdayNum = -1; - } - - console.log("[AgendamentoConsulta] Convertendo weekday:", { - original: avail.weekday, - type: typeof avail.weekday, - converted: weekdayNum - }); - return weekdayNum; - }).filter(day => day >= 0 && day <= 6) // Remove valores inválidos + availabilities + .map((avail) => { + // weekday pode vir como número ou string da API + let weekdayNum: number; + + if (typeof avail.weekday === "number") { + weekdayNum = avail.weekday; + } else if (typeof avail.weekday === "string") { + weekdayNum = weekdayMap[avail.weekday.toLowerCase()] ?? -1; + } else { + weekdayNum = -1; + } + + console.log("[AgendamentoConsulta] Convertendo weekday:", { + original: avail.weekday, + type: typeof avail.weekday, + converted: weekdayNum, + }); + return weekdayNum; + }) + .filter((day) => day >= 0 && day <= 6) // Remove valores inválidos ); - console.log("[AgendamentoConsulta] Dias da semana disponíveis (números):", Array.from(availableWeekdays)); + console.log( + "[AgendamentoConsulta] Dias da semana disponíveis (números):", + Array.from(availableWeekdays) + ); // Calcula todas as datas do mês atual e próximos 2 meses que têm disponibilidade const today = startOfDay(new Date()); @@ -167,7 +180,7 @@ export default function AgendamentoConsulta({ const allDates = eachDayOfInterval({ start: today, end: endDate }); const availableDatesSet = new Set(); - + allDates.forEach((date) => { const weekday = date.getDay(); if (availableWeekdays.has(weekday) && !isBefore(date, today)) { @@ -178,13 +191,15 @@ export default function AgendamentoConsulta({ console.log("[AgendamentoConsulta] Resumo do cálculo:", { weekdaysDisponiveis: Array.from(availableWeekdays), datasCalculadas: availableDatesSet.size, - primeiras5Datas: Array.from(availableDatesSet).slice(0, 5) + primeiras5Datas: Array.from(availableDatesSet).slice(0, 5), }); - - setAvailableDates(availableDatesSet); + setAvailableDates(availableDatesSet); } catch (error) { - console.error("[AgendamentoConsulta] Erro ao carregar disponibilidades:", error); + console.error( + "[AgendamentoConsulta] Erro ao carregar disponibilidades:", + error + ); setAvailableDates(new Set()); } }; @@ -203,7 +218,7 @@ export default function AgendamentoConsulta({ try { const dateStr = format(selectedDate, "yyyy-MM-dd"); - + console.log("[AgendamentoConsulta] Buscando slots disponíveis:", { doctor_id: selectedMedico.id, doctor_name: selectedMedico.nome, @@ -212,19 +227,24 @@ export default function AgendamentoConsulta({ // NOTA: Edge Function get-available-slots não está disponível no Supabase // Calculando slots localmente - + // Busca a disponibilidade do médico do Supabase const { availabilityService } = await import("../services"); - + const availabilities = await availabilityService.list({ doctor_id: selectedMedico.id, active: true, }); - console.log("[AgendamentoConsulta] Disponibilidades do médico:", availabilities); + console.log( + "[AgendamentoConsulta] Disponibilidades do médico:", + availabilities + ); if (!availabilities || availabilities.length === 0) { - console.warn("[AgendamentoConsulta] Nenhuma disponibilidade configurada para este médico"); + console.warn( + "[AgendamentoConsulta] Nenhuma disponibilidade configurada para este médico" + ); setAvailableSlots([]); return; } @@ -232,26 +252,34 @@ export default function AgendamentoConsulta({ // Pega o dia da semana da data selecionada const weekdayMap: Record = { 0: "sunday", - 1: "monday", + 1: "monday", 2: "tuesday", 3: "wednesday", 4: "thursday", 5: "friday", 6: "saturday", }; - + const dayOfWeek = weekdayMap[selectedDate.getDay()]; - console.log("[AgendamentoConsulta] Dia da semana selecionado:", dayOfWeek); + console.log( + "[AgendamentoConsulta] Dia da semana selecionado:", + dayOfWeek + ); // Filtra disponibilidades para o dia da semana const dayAvailability = availabilities.filter( (avail) => avail.weekday === dayOfWeek && avail.active ); - console.log("[AgendamentoConsulta] Disponibilidades para o dia:", dayAvailability); + console.log( + "[AgendamentoConsulta] Disponibilidades para o dia:", + dayAvailability + ); if (dayAvailability.length === 0) { - console.warn("[AgendamentoConsulta] Médico não atende neste dia da semana"); + console.warn( + "[AgendamentoConsulta] Médico não atende neste dia da semana" + ); setAvailableSlots([]); return; } @@ -266,14 +294,16 @@ export default function AgendamentoConsulta({ // Converte para minutos desde meia-noite const [startHour, startMin] = startTime.split(":").map(Number); const [endHour, endMin] = endTime.split(":").map(Number); - + let currentMinutes = startHour * 60 + startMin; const endMinutes = endHour * 60 + endMin; while (currentMinutes < endMinutes) { const hours = Math.floor(currentMinutes / 60); const minutes = currentMinutes % 60; - const timeStr = `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}`; + const timeStr = `${hours.toString().padStart(2, "0")}:${minutes + .toString() + .padStart(2, "0")}`; allSlots.push(timeStr); currentMinutes += slotMinutes; } @@ -284,7 +314,10 @@ export default function AgendamentoConsulta({ doctor_id: selectedMedico.id, }); - console.log("[AgendamentoConsulta] Agendamentos existentes:", appointments); + console.log( + "[AgendamentoConsulta] Agendamentos existentes:", + appointments + ); // Filtra agendamentos para a data selecionada const bookedSlots = appointments @@ -309,7 +342,10 @@ export default function AgendamentoConsulta({ (slot) => !bookedSlots.includes(slot) ); - console.log("[AgendamentoConsulta] Slots disponíveis calculados:", availableSlots); + console.log( + "[AgendamentoConsulta] Slots disponíveis calculados:", + availableSlots + ); setAvailableSlots(availableSlots); } catch (error) { @@ -342,13 +378,13 @@ export default function AgendamentoConsulta({ const handlePrevMonth = () => setCurrentMonth(subMonths(currentMonth, 1)); const handleNextMonth = () => setCurrentMonth(addMonths(currentMonth, 1)); - + const handleMonthChange = (monthIndex: number) => { const newDate = new Date(currentMonth); newDate.setMonth(monthIndex); setCurrentMonth(newDate); }; - + const handleYearChange = (year: number) => { const newDate = new Date(currentMonth); newDate.setFullYear(year); @@ -356,8 +392,11 @@ export default function AgendamentoConsulta({ }; // Gera lista de anos (ano atual até +10 anos) - const availableYears = Array.from({ length: 11 }, (_, i) => new Date().getFullYear() + i); - + const availableYears = Array.from( + { length: 11 }, + (_, i) => new Date().getFullYear() + i + ); + const handleSelectDoctor = (medico: Medico) => { setSelectedMedico(medico); setSelectedDate(undefined); @@ -365,12 +404,12 @@ export default function AgendamentoConsulta({ setMotivo(""); setBookingSuccess(false); setBookingError(""); - + // Scroll suave para a seção de detalhes setTimeout(() => { - detailsRef.current?.scrollIntoView({ - behavior: 'smooth', - block: 'start' + detailsRef.current?.scrollIntoView({ + behavior: "smooth", + block: "start", }); }, 100); }; @@ -393,32 +432,40 @@ export default function AgendamentoConsulta({ doctor_id: selectedMedico.id, scheduled_at: scheduledAt, duration_minutes: 30, - appointment_type: - (appointmentType === "online" ? "telemedicina" : "presencial") as "presencial" | "telemedicina", + appointment_type: (appointmentType === "online" + ? "telemedicina" + : "presencial") as "presencial" | "telemedicina", chief_complaint: motivo, }; - console.log("[AgendamentoConsulta] Criando agendamento com dados:", appointmentData); + console.log( + "[AgendamentoConsulta] Criando agendamento com dados:", + appointmentData + ); // Cria o agendamento usando a API REST const appointment = await appointmentService.create(appointmentData); - console.log("[AgendamentoConsulta] Consulta criada com sucesso:", appointment); + console.log( + "[AgendamentoConsulta] Consulta criada com sucesso:", + appointment + ); // Mostra modal de sucesso - setResultType('success'); + setResultType("success"); setShowResultModal(true); setShowConfirmDialog(false); setBookingSuccess(true); } catch (error: unknown) { console.error("[AgendamentoConsulta] Erro ao agendar:", error); - - const errorMessage = error instanceof Error - ? error.message - : "Erro ao agendar consulta. Tente novamente."; - + + const errorMessage = + error instanceof Error + ? error.message + : "Erro ao agendar consulta. Tente novamente."; + // Mostra modal de erro - setResultType('error'); + setResultType("error"); setShowResultModal(true); setBookingError(errorMessage); setShowConfirmDialog(false); @@ -435,38 +482,49 @@ export default function AgendamentoConsulta({
{/* Ícone com Animação Giratória (1 volta) */}
-
-
- {resultType === 'success' ? ( +
+
+ {resultType === "success" ? ( ) : ( )}
- + {/* Mensagem */}
-

- {resultType === 'success' ? 'Consulta Agendada!' : 'Erro no Agendamento'} +

+ {resultType === "success" + ? "Consulta Agendada!" + : "Erro no Agendamento"}

- {resultType === 'success' ? ( + {resultType === "success" ? (

- Sua consulta foi agendada com sucesso. Você receberá uma confirmação por e-mail ou SMS. + Sua consulta foi agendada com sucesso. Você receberá uma + confirmação por e-mail ou SMS.

) : (

- {bookingError || 'Não foi possível agendar a consulta. Tente novamente.'} + {bookingError || + "Não foi possível agendar a consulta. Tente novamente."}

)}
- + {/* Botão OK */}
)} - +

Agendar Consulta

@@ -531,7 +590,9 @@ export default function AgendamentoConsulta({

- + handleMonthChange(Number(e.target.value))} + onChange={(e) => + handleMonthChange(Number(e.target.value)) + } className="text-xs sm:text-sm font-semibold border border-gray-300 rounded-lg px-2 py-1 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500" > @@ -657,10 +739,12 @@ export default function AgendamentoConsulta({ - +
- + )} diff --git a/src/components/DisponibilidadeMedico.tsx b/src/components/DisponibilidadeMedico.tsx index 533603926..67ae48348 100644 --- a/src/components/DisponibilidadeMedico.tsx +++ b/src/components/DisponibilidadeMedico.tsx @@ -279,7 +279,9 @@ const DisponibilidadeMedico: React.FC = () => { doctor_id: doctorId, date: exceptionForm.date, kind: exceptionForm.kind, - start_time: exceptionForm.wholeDayBlock ? null : exceptionForm.start_time, + start_time: exceptionForm.wholeDayBlock + ? null + : exceptionForm.start_time, end_time: exceptionForm.wholeDayBlock ? null : exceptionForm.end_time, reason: exceptionForm.reason || null, created_by: user?.id || doctorId, @@ -462,7 +464,10 @@ const DisponibilidadeMedico: React.FC = () => { type="date" value={exceptionForm.date} onChange={(e) => - setExceptionForm({ ...exceptionForm, date: e.target.value }) + setExceptionForm({ + ...exceptionForm, + date: e.target.value, + }) } className="form-input w-full" /> @@ -476,7 +481,9 @@ const DisponibilidadeMedico: React.FC = () => { onChange={(e) => setExceptionForm({ ...exceptionForm, - kind: e.target.value as "bloqueio" | "disponibilidade_extra", + kind: e.target.value as + | "bloqueio" + | "disponibilidade_extra", }) } className="form-input w-full" @@ -554,7 +561,10 @@ const DisponibilidadeMedico: React.FC = () => {