WeekView: limita renderização às horas do menor/maior horário da semana visível
This commit is contained in:
parent
b302bf1c66
commit
280d314b5d
@ -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>
|
||||||
|
|||||||
@ -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) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user