WeekView: limita renderização às horas do menor/maior horário da semana visível

This commit is contained in:
Jonas Francisco 2025-11-06 17:30:53 -03:00
parent b302bf1c66
commit 280d314b5d
2 changed files with 87 additions and 7 deletions

View File

@ -121,6 +121,40 @@ export default function AgendamentoPage() {
} }
}; };
// Mapeia cor do calendário -> status da API
const statusFromColor = (color?: string) => {
switch ((color || "").toLowerCase()) {
case "green": return "confirmed";
case "orange": return "pending";
case "red": return "canceled";
default: return "requested";
}
};
// Envia atualização para a API e atualiza UI
const handleEventUpdate = async (id: string, partial: Partial<Event>) => {
try {
const payload: any = {};
if (partial.startTime) payload.scheduled_at = partial.startTime.toISOString();
if (partial.startTime && partial.endTime) {
const minutes = Math.max(1, Math.round((partial.endTime.getTime() - partial.startTime.getTime()) / 60000));
payload.duration_minutes = minutes;
}
if (partial.color) payload.status = statusFromColor(partial.color);
if (typeof partial.description === "string") payload.notes = partial.description;
if (Object.keys(payload).length) {
const api = await import('@/lib/api');
await api.atualizarAgendamento(id, payload);
}
// Otimista: reflete mudanças locais
setManagerEvents((prev) => prev.map((e) => (e.id === id ? { ...e, ...partial } : e)));
} catch (e) {
console.warn("[Calendário] Falha ao atualizar agendamento na API:", e);
}
};
return ( return (
<div className="flex flex-row bg-background"> <div className="flex flex-row bg-background">
<div className="flex w-full flex-col"> <div className="flex w-full flex-col">
@ -164,7 +198,11 @@ export default function AgendamentoPage() {
</div> </div>
) : ( ) : (
<div className="w-full min-h-[70vh]"> <div className="w-full min-h-[70vh]">
<EventManager events={managerEvents} className="compact-event-manager" /> <EventManager
events={managerEvents}
className="compact-event-manager"
onEventUpdate={handleEventUpdate}
/>
</div> </div>
)} )}
</div> </div>

View File

@ -515,7 +515,7 @@ export function EventManager({
{/* Event Dialog */} {/* Event Dialog */}
<Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}> <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<DialogContent className="max-w-md max-h[90vh] overflow-y-auto"> <DialogContent className="max-w-md max-h-[90vh] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle>{isCreating ? "Criar Evento" : "Detalhes do Evento"}</DialogTitle> <DialogTitle>{isCreating ? "Criar Evento" : "Detalhes do Evento"}</DialogTitle>
<DialogDescription> <DialogDescription>
@ -528,7 +528,7 @@ export function EventManager({
<Label htmlFor="title">Título</Label> <Label htmlFor="title">Título</Label>
<Input <Input
id="title" id="title"
value={isCreating ? newEvent.title : selectedEvent?.title} value={isCreating ? (newEvent.title ?? "") : (selectedEvent?.title ?? "")}
onChange={(e) => onChange={(e) =>
isCreating isCreating
? setNewEvent((prev) => ({ ...prev, title: e.target.value })) ? setNewEvent((prev) => ({ ...prev, title: e.target.value }))
@ -542,7 +542,7 @@ export function EventManager({
<Label htmlFor="description">Descrição</Label> <Label htmlFor="description">Descrição</Label>
<Textarea <Textarea
id="description" id="description"
value={isCreating ? newEvent.description : selectedEvent?.description} value={isCreating ? (newEvent.description ?? "") : (selectedEvent?.description ?? "")}
onChange={(e) => onChange={(e) =>
isCreating isCreating
? setNewEvent((prev) => ({ ? setNewEvent((prev) => ({
@ -972,7 +972,7 @@ function WeekView({
getColorClasses: (color: string) => { bg: string; text: string } getColorClasses: (color: string) => { bg: string; text: string }
}) { }) {
const startOfWeek = new Date(currentDate) const startOfWeek = new Date(currentDate)
startOfWeek.setDate(currentDate.getDay()) startOfWeek.setDate(currentDate.getDate() - currentDate.getDay())
const weekDays = Array.from({ length: 7 }, (_, i) => { const weekDays = Array.from({ length: 7 }, (_, i) => {
const day = new Date(startOfWeek) const day = new Date(startOfWeek)
@ -980,7 +980,30 @@ function WeekView({
return day return day
}) })
const hours = Array.from({ length: 24 }, (_, i) => i) // NOVO: limita intervalo de horas ao 1º e último evento da semana
const [startHour, endHour] = React.useMemo(() => {
let minH = Infinity
let maxH = -Infinity
for (const ev of events) {
const d = ev.startTime
const sameWeekDay = weekDays.some(wd =>
d.getFullYear() === wd.getFullYear() &&
d.getMonth() === wd.getMonth() &&
d.getDate() === wd.getDate()
)
if (!sameWeekDay) continue
minH = Math.min(minH, d.getHours())
maxH = Math.max(maxH, ev.endTime.getHours())
}
if (!isFinite(minH) || !isFinite(maxH)) return [0, 23] as const
if (maxH < minH) maxH = minH
return [minH, maxH] as const
}, [events, weekDays])
const hours = React.useMemo(
() => Array.from({ length: (endHour - startHour + 1) }, (_, i) => startHour + i),
[startHour, endHour]
)
const getEventsForDayAndHour = (date: Date, hour: number) => { const getEventsForDayAndHour = (date: Date, hour: number) => {
return events.filter((event) => { return events.filter((event) => {
@ -1071,7 +1094,26 @@ function DayView({
onDrop: (date: Date, hour: number) => void onDrop: (date: Date, hour: number) => void
getColorClasses: (color: string) => { bg: string; text: string } getColorClasses: (color: string) => { bg: string; text: string }
}) { }) {
const hours = Array.from({ length: 24 }, (_, i) => i) // NOVO: calcula intervalo de horas do 1º ao último evento do dia
const [startHour, endHour] = React.useMemo(() => {
const sameDayEvents = events.filter((ev) => {
const d = ev.startTime
return (
d.getDate() === currentDate.getDate() &&
d.getMonth() === currentDate.getMonth() &&
d.getFullYear() === currentDate.getFullYear()
)
})
if (!sameDayEvents.length) return [0, 23] as const
const minH = Math.min(...sameDayEvents.map((e) => e.startTime.getHours()))
const maxH = Math.max(...sameDayEvents.map((e) => e.endTime.getHours()))
return [minH, Math.max(maxH, minH)] as const
}, [events, currentDate])
const hours = React.useMemo(
() => Array.from({ length: (endHour - startHour + 1) }, (_, i) => startHour + i),
[startHour, endHour]
)
const getEventsForHour = (hour: number) => { const getEventsForHour = (hour: number) => {
return events.filter((event) => { return events.filter((event) => {