fix: doctor-availabity

This commit is contained in:
João Gustavo 2025-12-03 18:18:00 -03:00
parent 31b4472aee
commit 78e37220b6
3 changed files with 87 additions and 17 deletions

View File

@ -111,8 +111,11 @@ export default function ConsultasPage() {
const baseDate = scheduledBase ? new Date(scheduledBase) : new Date();
const duration = appointment.duration_minutes ?? appointment.duration ?? 30;
// compute start and end times (HH:MM)
const appointmentDateStr = baseDate.toISOString().split("T")[0];
// compute start and end times (HH:MM) and date using local time to avoid timezone issues
const year = baseDate.getFullYear();
const month = String(baseDate.getMonth() + 1).padStart(2, '0');
const day = String(baseDate.getDate()).padStart(2, '0');
const appointmentDateStr = `${year}-${month}-${day}`;
const startTime = `${String(baseDate.getHours()).padStart(2, '0')}:${String(baseDate.getMinutes()).padStart(2, '0')}`;
const endDate = new Date(baseDate.getTime() + duration * 60000);
const endTime = `${String(endDate.getHours()).padStart(2, '0')}:${String(endDate.getMinutes()).padStart(2, '0')}`;
@ -811,7 +814,7 @@ export default function ConsultasPage() {
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label className="text-right">Tipo</Label>
<span className="col-span-3">{capitalize(viewingAppointment?.type || "")}</span>
<span className="col-span-3">{capitalize(viewingAppointment?.appointment_type || viewingAppointment?.type || "")}</span>
</div>
<div className="grid grid-cols-4 items-center gap-4">
<Label className="text-right">Observações</Label>

View File

@ -131,6 +131,7 @@ export default function DoutoresPage() {
const [availabilityOpenFor, setAvailabilityOpenFor] = useState<Medico | null>(null);
const [availabilityViewingFor, setAvailabilityViewingFor] = useState<Medico | null>(null);
const [availabilities, setAvailabilities] = useState<DoctorAvailability[]>([]);
const [availabilitiesForCreate, setAvailabilitiesForCreate] = useState<DoctorAvailability[]>([]);
const [availLoading, setAvailLoading] = useState(false);
const [editingAvailability, setEditingAvailability] = useState<DoctorAvailability | null>(null);
const [exceptions, setExceptions] = useState<DoctorException[]>([]);
@ -633,7 +634,17 @@ export default function DoutoresPage() {
Ver pacientes atribuídos
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setAvailabilityOpenFor(doctor)}>
<DropdownMenuItem onClick={async () => {
try {
const list = await listarDisponibilidades({ doctorId: doctor.id, active: true });
setAvailabilitiesForCreate(list || []);
setAvailabilityOpenFor(doctor);
} catch (e) {
console.warn('Erro ao carregar disponibilidades:', e);
setAvailabilitiesForCreate([]);
setAvailabilityOpenFor(doctor);
}
}}>
<Plus className="mr-2 h-4 w-4" />
Criar disponibilidade
</DropdownMenuItem>
@ -869,6 +880,7 @@ export default function DoutoresPage() {
open={!!availabilityOpenFor}
onOpenChange={(open) => { if (!open) setAvailabilityOpenFor(null); }}
doctorId={availabilityOpenFor?.id}
existingAvailabilities={availabilitiesForCreate}
onSaved={(saved) => { console.log('Disponibilidade salva', saved); setAvailabilityOpenFor(null); /* optionally reload list */ reloadAvailabilities(availabilityOpenFor?.id); }}
/>
)}
@ -890,6 +902,7 @@ export default function DoutoresPage() {
doctorId={editingAvailability?.doctor_id ?? availabilityViewingFor?.id}
availability={editingAvailability}
mode="edit"
existingAvailabilities={availabilities}
onSaved={(saved) => { console.log('Disponibilidade atualizada', saved); setEditingAvailability(null); reloadAvailabilities(editingAvailability?.doctor_id ?? availabilityViewingFor?.id); }}
/>
)}
@ -910,14 +923,35 @@ export default function DoutoresPage() {
<div>Carregando disponibilidades</div>
) : availabilities && availabilities.length ? (
<div className="space-y-2">
{availabilities.map((a) => (
{availabilities
.sort((a, b) => {
// Define a ordem dos dias da semana (Segunda a Domingo)
const weekdayOrder: Record<string, number> = {
'segunda': 1, 'segunda-feira': 1, 'mon': 1, 'monday': 1, '1': 1,
'terca': 2, 'terça': 2, 'terça-feira': 2, 'tue': 2, 'tuesday': 2, '2': 2,
'quarta': 3, 'quarta-feira': 3, 'wed': 3, 'wednesday': 3, '3': 3,
'quinta': 4, 'quinta-feira': 4, 'thu': 4, 'thursday': 4, '4': 4,
'sexta': 5, 'sexta-feira': 5, 'fri': 5, 'friday': 5, '5': 5,
'sabado': 6, 'sábado': 6, 'sat': 6, 'saturday': 6, '6': 6,
'domingo': 7, 'dom': 7, 'sun': 7, 'sunday': 7, '0': 7, '7': 7
};
const getWeekdayOrder = (weekday: any) => {
if (typeof weekday === 'number') return weekday === 0 ? 7 : weekday;
const normalized = String(weekday).toLowerCase().normalize('NFD').replace(/\p{Diacritic}/gu, '');
return weekdayOrder[normalized] || 999;
};
return getWeekdayOrder(a.weekday) - getWeekdayOrder(b.weekday);
})
.map((a) => (
<div key={String(a.id)} className="p-2 border rounded flex justify-between items-start">
<div>
<div className="font-medium">{translateWeekday(a.weekday)} {a.start_time} {a.end_time}</div>
<div className="text-xs text-muted-foreground">Duração: {a.slot_minutes} min Tipo: {a.appointment_type || '—'} {a.active ? 'Ativa' : 'Inativa'}</div>
</div>
<div className="flex gap-2">
<Button size="sm" variant="outline" onClick={() => setEditingAvailability(a)}>Editar</Button>
<Button size="sm" variant="outline" onClick={() => setEditingAvailability(a)} className="hover:bg-muted hover:text-foreground">Editar</Button>
<Button size="sm" variant="destructive" onClick={async () => {
if (!confirm('Excluir esta disponibilidade?')) return;
try {

View File

@ -18,9 +18,11 @@ export interface AvailabilityFormProps {
// when editing, pass the existing availability and set mode to 'edit'
availability?: DoctorAvailability | null
mode?: 'create' | 'edit'
// existing availabilities to prevent duplicate weekday selection
existingAvailabilities?: DoctorAvailability[]
}
export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved, availability = null, mode = 'create' }: AvailabilityFormProps) {
export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved, availability = null, mode = 'create', existingAvailabilities = [] }: AvailabilityFormProps) {
const [weekday, setWeekday] = useState<string>('segunda')
const [startTime, setStartTime] = useState<string>('09:00')
const [endTime, setEndTime] = useState<string>('17:00')
@ -31,6 +33,26 @@ export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved,
const { toast } = useToast()
const [blockedException, setBlockedException] = useState<null | { date: string; reason?: string; times?: string }>(null)
// Normalize weekday to standard format for comparison
const normalizeWeekdayForComparison = (w?: string) => {
if (!w) return w;
const k = String(w).toLowerCase().normalize('NFD').replace(/\p{Diacritic}/gu, '').replace(/[^a-z0-9]/g, '');
const map: Record<string,string> = {
'segunda':'segunda','terca':'terca','quarta':'quarta','quinta':'quinta','sexta':'sexta','sabado':'sabado','domingo':'domingo',
'monday':'segunda','tuesday':'terca','wednesday':'quarta','thursday':'quinta','friday':'sexta','saturday':'sabado','sunday':'domingo',
'1':'segunda','2':'terca','3':'quarta','4':'quinta','5':'sexta','6':'sabado','0':'domingo','7':'domingo'
};
return map[k] ?? k;
};
// Get list of already used weekdays (excluding current one in edit mode)
const usedWeekdays = new Set(
(existingAvailabilities || [])
.filter(a => mode === 'edit' ? a.id !== availability?.id : true)
.map(a => normalizeWeekdayForComparison(a.weekday))
.filter(Boolean)
);
// When editing, populate state from availability prop
useEffect(() => {
if (mode === 'edit' && availability) {
@ -47,6 +69,17 @@ export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved,
}
}, [mode, availability])
// When creating and modal opens, set the first available weekday
useEffect(() => {
if (mode === 'create' && open) {
const allWeekdays = ['segunda', 'terca', 'quarta', 'quinta', 'sexta', 'sabado', 'domingo'];
const firstAvailable = allWeekdays.find(day => !usedWeekdays.has(day));
if (firstAvailable) {
setWeekday(firstAvailable);
}
}
}, [mode, open, usedWeekdays])
async function handleSubmit(e?: React.FormEvent) {
e?.preventDefault()
if (!doctorId) {
@ -181,25 +214,25 @@ export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved,
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent>
<DialogHeader>
<DialogTitle>Criar disponibilidade</DialogTitle>
<DialogTitle>{mode === 'edit' ? 'Editar disponibilidade' : 'Criar disponibilidade'}</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4 py-4">
<div className="grid grid-cols-2 gap-4">
<div>
<Label>Dia da semana</Label>
<Select value={weekday} onValueChange={(v) => setWeekday(v)}>
<Select value={weekday} onValueChange={(v) => setWeekday(v)} disabled={mode === 'edit'}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="segunda">Segunda</SelectItem>
<SelectItem value="terca">Terça</SelectItem>
<SelectItem value="quarta">Quarta</SelectItem>
<SelectItem value="quinta">Quinta</SelectItem>
<SelectItem value="sexta">Sexta</SelectItem>
<SelectItem value="sabado">Sábado</SelectItem>
<SelectItem value="domingo">Domingo</SelectItem>
<SelectItem value="segunda" disabled={usedWeekdays.has('segunda')}>Segunda</SelectItem>
<SelectItem value="terca" disabled={usedWeekdays.has('terca')}>Terça</SelectItem>
<SelectItem value="quarta" disabled={usedWeekdays.has('quarta')}>Quarta</SelectItem>
<SelectItem value="quinta" disabled={usedWeekdays.has('quinta')}>Quinta</SelectItem>
<SelectItem value="sexta" disabled={usedWeekdays.has('sexta')}>Sexta</SelectItem>
<SelectItem value="sabado" disabled={usedWeekdays.has('sabado')}>Sábado</SelectItem>
<SelectItem value="domingo" disabled={usedWeekdays.has('domingo')}>Domingo</SelectItem>
</SelectContent>
</Select>
</div>
@ -242,7 +275,7 @@ export function AvailabilityForm({ open, onOpenChange, doctorId = null, onSaved,
<DialogFooter>
<Button variant="ghost" onClick={() => onOpenChange(false)} disabled={submitting}>Cancelar</Button>
<Button type="submit" disabled={submitting}>{submitting ? 'Salvando...' : 'Criar disponibilidade'}</Button>
<Button type="submit" disabled={submitting}>{submitting ? 'Salvando...' : (mode === 'edit' ? 'Salvar alterações' : 'Criar disponibilidade')}</Button>
</DialogFooter>
</form>
</DialogContent>