fix(agenda): fix $.map crash and getPacientes type mismatch; restore preloader
AgendaView:
- Fix agendamentos?.filter(...).map(...) crash: optional chain didn't guard .map when agendamentos is null on initial render
- Fix getPacientes fetcher to extract .data array — API now returns {data, total} not Paciente[], causing searchResults.map crash
LoginView:
- Restore 3s preloader splash screen with {APP_VERSION} instead of hardcoded version string
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -44,7 +44,7 @@ const AppointmentModal: React.FC<{
|
|||||||
|
|
||||||
const debouncedSearchTerm = useDebounce(searchTerm, 400);
|
const debouncedSearchTerm = useDebounce(searchTerm, 400);
|
||||||
|
|
||||||
const fetcher = useCallback(() => HybridBackend.getPacientes(debouncedSearchTerm), [debouncedSearchTerm]);
|
const fetcher = useCallback(() => HybridBackend.getPacientes(debouncedSearchTerm).then(r => Array.isArray(r.data) ? r.data : []), [debouncedSearchTerm]);
|
||||||
const { data: searchResults, isLoading: isSearching } = useHybridBackend<Paciente[]>(fetcher, [debouncedSearchTerm]);
|
const { data: searchResults, isLoading: isSearching } = useHybridBackend<Paciente[]>(fetcher, [debouncedSearchTerm]);
|
||||||
|
|
||||||
const { data: agendamentos, refresh: refreshAgendamentos } = useHybridBackend<Agendamento[]>(HybridBackend.getAgendamentos);
|
const { data: agendamentos, refresh: refreshAgendamentos } = useHybridBackend<Agendamento[]>(HybridBackend.getAgendamentos);
|
||||||
@@ -93,14 +93,14 @@ const AppointmentModal: React.FC<{
|
|||||||
const slots: string[] = [];
|
const slots: string[] = [];
|
||||||
if (!selectedDentistId || !selectedDate) return slots;
|
if (!selectedDentistId || !selectedDate) return slots;
|
||||||
|
|
||||||
const existingAppointments = agendamentos?.filter(ag =>
|
const existingAppointments = (agendamentos ?? []).filter(ag =>
|
||||||
ag.dentistaId === selectedDentistId &&
|
ag.dentistaId === selectedDentistId &&
|
||||||
new Date(ag.start).toISOString().split('T')[0] === selectedDate &&
|
new Date(ag.start).toISOString().split('T')[0] === selectedDate &&
|
||||||
ag.id !== initialAppointment?.id
|
ag.id !== initialAppointment?.id
|
||||||
).map(ag => ({
|
).map(ag => ({
|
||||||
start: new Date(ag.start).getHours() * 60 + new Date(ag.start).getMinutes(),
|
start: new Date(ag.start).getHours() * 60 + new Date(ag.start).getMinutes(),
|
||||||
end: new Date(ag.end).getHours() * 60 + new Date(ag.end).getMinutes(),
|
end: new Date(ag.end).getHours() * 60 + new Date(ag.end).getMinutes(),
|
||||||
})) || [];
|
}));
|
||||||
|
|
||||||
const dayOfWeek = new Date(selectedDate).getUTCDay();
|
const dayOfWeek = new Date(selectedDate).getUTCDay();
|
||||||
const dayHorarios = allHorarios?.filter(h => h.dia_semana === dayOfWeek && h.ativo) || [];
|
const dayHorarios = allHorarios?.filter(h => h.dia_semana === dayOfWeek && h.ativo) || [];
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Database, Lock, User, ArrowRight, ShieldCheck } from 'lucide-react';
|
import { Database, Lock, User, ArrowRight, ShieldCheck, Loader2 } from 'lucide-react';
|
||||||
import { HybridBackend } from '../services/backend.ts';
|
import { HybridBackend } from '../services/backend.ts';
|
||||||
import { APP_VERSION } from '../constants.ts';
|
import { APP_VERSION } from '../constants.ts';
|
||||||
|
|
||||||
@@ -12,6 +12,22 @@ export const LoginView: React.FC<LoginViewProps> = ({ onLoginSuccess }) => {
|
|||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
|
const [isInitializing, setIsInitializing] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => setIsInitializing(false), 3000);
|
||||||
|
return () => clearTimeout(timer);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (isInitializing) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center p-4">
|
||||||
|
<Loader2 className="animate-spin text-blue-600 mb-4" size={48} />
|
||||||
|
<h2 className="text-gray-500 font-bold uppercase tracking-widest text-sm">Inicializando Sistema...</h2>
|
||||||
|
<span className="text-gray-400 text-xs mt-2 font-mono font-bold bg-gray-200 px-2 py-1 rounded">{APP_VERSION}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
Reference in New Issue
Block a user