ajustes finais no calendario
This commit is contained in:
parent
1aed4c6164
commit
214da568c3
@ -77,17 +77,22 @@ export default function AgendamentoPage() {
|
|||||||
const patient = (patientsById[String(obj.patient_id)]?.full_name) || obj.patient_name || obj.patient_full_name || obj.patient || 'Paciente';
|
const patient = (patientsById[String(obj.patient_id)]?.full_name) || obj.patient_name || obj.patient_full_name || obj.patient || 'Paciente';
|
||||||
const title = `${patient}: ${obj.appointment_type ?? obj.type ?? ''}`.trim();
|
const title = `${patient}: ${obj.appointment_type ?? obj.type ?? ''}`.trim();
|
||||||
|
|
||||||
let color = "gray"; // Cor padrão
|
// Mapeamento de cores padronizado:
|
||||||
if (obj.status === 'confirmed') color = 'green';
|
// azul = solicitado; verde = confirmado; laranja = pendente; vermelho = cancelado; azul como fallback
|
||||||
if (obj.status === 'pending') color = 'orange';
|
const status = String(obj.status || "").toLowerCase();
|
||||||
|
let color: Event["color"] = "blue";
|
||||||
|
if (status === "confirmed" || status === "confirmado") color = "green";
|
||||||
|
else if (status === "pending" || status === "pendente") color = "orange";
|
||||||
|
else if (status === "canceled" || status === "cancelado" || status === "cancelled") color = "red";
|
||||||
|
else if (status === "requested" || status === "solicitado") color = "blue";
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: obj.id || uuidv4(), // Usa ID da API ou gera um
|
id: obj.id || uuidv4(),
|
||||||
title: title,
|
title,
|
||||||
description: `Agendamento para ${patient}. Status: ${obj.status || 'N/A'}.`,
|
description: `Agendamento para ${patient}. Status: ${obj.status || 'N/A'}.`,
|
||||||
startTime: start,
|
startTime: start,
|
||||||
endTime: end,
|
endTime: end,
|
||||||
color: color,
|
color,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
setManagerEvents(newManagerEvents);
|
setManagerEvents(newManagerEvents);
|
||||||
@ -144,17 +149,6 @@ export default function AgendamentoPage() {
|
|||||||
setThreeDEvents((prev) => prev.filter((e) => e.id !== id));
|
setThreeDEvents((prev) => prev.filter((e) => e.id !== id));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Tenta clicar no botão de filtro correspondente (procura por texto do botão)
|
|
||||||
const clickFilter = (label: string) => {
|
|
||||||
try {
|
|
||||||
const buttons = Array.from(document.querySelectorAll<HTMLButtonElement>("button"));
|
|
||||||
const match = buttons.find((b) => b.textContent?.trim().toLowerCase().includes(label.toLowerCase()));
|
|
||||||
if (match) match.click();
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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">
|
||||||
@ -170,13 +164,6 @@ export default function AgendamentoPage() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex space-x-2 items-center">
|
<div className="flex space-x-2 items-center">
|
||||||
{/* Botões rápidos de filtros (acionam os triggers se existirem no DOM) */}
|
|
||||||
<div className="hidden sm:flex items-center gap-2">
|
|
||||||
<Button type="button" variant="outline" size="sm" onClick={() => clickFilter("Cores")}>Cores</Button>
|
|
||||||
<Button type="button" variant="outline" size="sm" onClick={() => clickFilter("Tags")}>Tags</Button>
|
|
||||||
<Button type="button" variant="outline" size="sm" onClick={() => clickFilter("Categorias")}>Categorias</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-row">
|
<div className="flex flex-row">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
@ -197,7 +184,21 @@ export default function AgendamentoPage() {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Legenda de status (estilo Google Calendar) */}
|
||||||
|
<div className="rounded-md border bg-card/60 p-2 sm:p-3 -mt-4">
|
||||||
|
<div className="flex flex-wrap items-center gap-6 text-sm">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span aria-hidden className="h-3 w-3 rounded-full bg-blue-500 ring-2 ring-blue-500/30" />
|
||||||
|
<span className="text-foreground">Solicitado</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span aria-hidden className="h-3 w-3 rounded-full bg-green-500 ring-2 ring-green-500/30" />
|
||||||
|
<span className="text-foreground">Confirmado</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* --- AQUI ESTÁ A SUBSTITUIÇÃO --- */}
|
{/* --- AQUI ESTÁ A SUBSTITUIÇÃO --- */}
|
||||||
{activeTab === "calendar" ? (
|
{activeTab === "calendar" ? (
|
||||||
@ -226,8 +227,8 @@ export default function AgendamentoPage() {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import React, { useState, useCallback, useMemo } from "react"
|
import React, { useState, useCallback, useMemo, useEffect } from "react"
|
||||||
import { Button } from "@/components/ui/button"
|
import { Button } from "@/components/ui/button"
|
||||||
import { Card } from "@/components/ui/card"
|
import { Card } from "@/components/ui/card"
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from "@/components/ui/input"
|
||||||
@ -52,6 +52,10 @@ const defaultColors = [
|
|||||||
{ name: "Red", value: "red", bg: "bg-red-500", text: "text-red-700" },
|
{ name: "Red", value: "red", bg: "bg-red-500", text: "text-red-700" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// Locale/timezone padrão BR
|
||||||
|
const LOCALE = "pt-BR"
|
||||||
|
const TIMEZONE = "America/Sao_Paulo"
|
||||||
|
|
||||||
export function EventManager({
|
export function EventManager({
|
||||||
events: initialEvents = [],
|
events: initialEvents = [],
|
||||||
onEventCreate,
|
onEventCreate,
|
||||||
@ -215,6 +219,17 @@ export function EventManager({
|
|||||||
[colors],
|
[colors],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Força lang/cookie pt-BR no documento (reforço local)
|
||||||
|
useEffect(() => {
|
||||||
|
try {
|
||||||
|
document.documentElement.lang = "pt-BR"
|
||||||
|
document.documentElement.setAttribute("xml:lang", "pt-BR")
|
||||||
|
document.documentElement.setAttribute("data-lang", "pt-BR")
|
||||||
|
const oneYear = 60 * 60 * 24 * 365
|
||||||
|
document.cookie = `NEXT_LOCALE=pt-BR; Path=/; Max-Age=${oneYear}; SameSite=Lax`
|
||||||
|
} catch {}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("flex flex-col gap-4", className)}>
|
<div className={cn("flex flex-col gap-4", className)}>
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
@ -222,21 +237,24 @@ export function EventManager({
|
|||||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-4">
|
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-4">
|
||||||
<h2 className="text-xl font-semibold sm:text-2xl">
|
<h2 className="text-xl font-semibold sm:text-2xl">
|
||||||
{view === "month" &&
|
{view === "month" &&
|
||||||
currentDate.toLocaleDateString("pt-BR", {
|
currentDate.toLocaleDateString(LOCALE, {
|
||||||
month: "long",
|
month: "long",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
|
timeZone: TIMEZONE,
|
||||||
})}
|
})}
|
||||||
{view === "week" &&
|
{view === "week" &&
|
||||||
`Semana de ${currentDate.toLocaleDateString("pt-BR", {
|
`Semana de ${currentDate.toLocaleDateString(LOCALE, {
|
||||||
month: "short",
|
month: "short",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
|
timeZone: TIMEZONE,
|
||||||
})}`}
|
})}`}
|
||||||
{view === "day" &&
|
{view === "day" &&
|
||||||
currentDate.toLocaleDateString("pt-BR", {
|
currentDate.toLocaleDateString(LOCALE, {
|
||||||
weekday: "long",
|
weekday: "long",
|
||||||
month: "long",
|
month: "long",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
|
timeZone: TIMEZONE,
|
||||||
})}
|
})}
|
||||||
{view === "list" && "Todos os eventos"}
|
{view === "list" && "Todos os eventos"}
|
||||||
</h2>
|
</h2>
|
||||||
@ -430,9 +448,9 @@ export function EventManager({
|
|||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-2">
|
||||||
<div className="font-semibold truncate">{ev.title}</div>
|
<div className="font-semibold truncate">{ev.title}</div>
|
||||||
<div className="text-xs text-muted-foreground">
|
<div className="text-xs text-muted-foreground">
|
||||||
{ev.startTime.toLocaleTimeString("pt-BR",{hour:"2-digit",minute:"2-digit"})}
|
{ev.startTime.toLocaleTimeString(LOCALE,{hour:"2-digit",minute:"2-digit",hour12:false,timeZone:TIMEZONE})}
|
||||||
{" - "}
|
{" - "}
|
||||||
{ev.endTime.toLocaleTimeString("pt-BR",{hour:"2-digit",minute:"2-digit"})}
|
{ev.endTime.toLocaleTimeString(LOCALE,{hour:"2-digit",minute:"2-digit",hour12:false,timeZone:TIMEZONE})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{ev.description && (
|
{ev.description && (
|
||||||
@ -647,9 +665,11 @@ function EventCard({
|
|||||||
const colorClasses = getColorClasses(event.color)
|
const colorClasses = getColorClasses(event.color)
|
||||||
|
|
||||||
const formatTime = (date: Date) => {
|
const formatTime = (date: Date) => {
|
||||||
return date.toLocaleTimeString("en-US", {
|
return date.toLocaleTimeString(LOCALE, {
|
||||||
hour: "2-digit",
|
hour: "2-digit",
|
||||||
minute: "2-digit",
|
minute: "2-digit",
|
||||||
|
hour12: false,
|
||||||
|
timeZone: TIMEZONE,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -984,10 +1004,10 @@ function WeekView({
|
|||||||
key={day.toISOString()}
|
key={day.toISOString()}
|
||||||
className="border-r p-2 text-center text-xs font-medium last:border-r-0 sm:text-sm"
|
className="border-r p-2 text-center text-xs font-medium last:border-r-0 sm:text-sm"
|
||||||
>
|
>
|
||||||
<div className="hidden sm:block">{day.toLocaleDateString("pt-BR", { weekday: "short" })}</div>
|
<div className="hidden sm:block">{day.toLocaleDateString(LOCALE, { weekday: "short", timeZone: TIMEZONE })}</div>
|
||||||
<div className="sm:hidden">{day.toLocaleDateString("pt-BR", { weekday: "narrow" })}</div>
|
<div className="sm:hidden">{day.toLocaleDateString(LOCALE, { weekday: "narrow", timeZone: TIMEZONE })}</div>
|
||||||
<div className="text-[10px] text-muted-foreground sm:text-xs">
|
<div className="text-[10px] text-muted-foreground sm:text-xs">
|
||||||
{day.toLocaleDateString("pt-BR", { month: "short", day: "numeric" })}
|
{day.toLocaleDateString(LOCALE, { month: "short", day: "numeric", timeZone: TIMEZONE })}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@ -1118,15 +1138,14 @@ function ListView({
|
|||||||
|
|
||||||
const groupedEvents = sortedEvents.reduce(
|
const groupedEvents = sortedEvents.reduce(
|
||||||
(acc, event) => {
|
(acc, event) => {
|
||||||
const dateKey = event.startTime.toLocaleDateString("pt-BR", {
|
const dateKey = event.startTime.toLocaleDateString(LOCALE, {
|
||||||
weekday: "long",
|
weekday: "long",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
month: "long",
|
month: "long",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
|
timeZone: TIMEZONE,
|
||||||
})
|
})
|
||||||
if (!acc[dateKey]) {
|
if (!acc[dateKey]) acc[dateKey] = []
|
||||||
acc[dateKey] = []
|
|
||||||
}
|
|
||||||
acc[dateKey].push(event)
|
acc[dateKey].push(event)
|
||||||
return acc
|
return acc
|
||||||
},
|
},
|
||||||
@ -1143,11 +1162,7 @@ function ListView({
|
|||||||
{dateEvents.map((event) => {
|
{dateEvents.map((event) => {
|
||||||
const colorClasses = getColorClasses(event.color)
|
const colorClasses = getColorClasses(event.color)
|
||||||
return (
|
return (
|
||||||
<div
|
<div key={event.id} onClick={() => onEventClick(event)} className="group cursor-pointer rounded-lg border bg-card p-3 transition-all hover:shadow-md hover:scale-[1.01] animate-in fade-in slide-in-from-bottom-2 duration-300 sm:p-4">
|
||||||
key={event.id}
|
|
||||||
onClick={() => onEventClick(event)}
|
|
||||||
className="group cursor-pointer rounded-lg border bg-card p-3 transition-all hover:shadow-md hover:scale-[1.01] animate-in fade-in slide-in-from-bottom-2 duration-300 sm:p-4"
|
|
||||||
>
|
|
||||||
<div className="flex items-start gap-2 sm:gap-3">
|
<div className="flex items-start gap-2 sm:gap-3">
|
||||||
<div className={cn("mt-1 h-2.5 w-2.5 rounded-full sm:h-3 sm:w-3", colorClasses.bg)} />
|
<div className={cn("mt-1 h-2.5 w-2.5 rounded-full sm:h-3 sm:w-3", colorClasses.bg)} />
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
@ -1173,7 +1188,9 @@ function ListView({
|
|||||||
<div className="mt-2 flex flex-wrap items-center gap-2 text-[10px] text-muted-foreground sm:gap-4 sm:text-xs">
|
<div className="mt-2 flex flex-wrap items-center gap-2 text-[10px] text-muted-foreground sm:gap-4 sm:text-xs">
|
||||||
<div className="flex items-center gap-1">
|
<div className="flex items-center gap-1">
|
||||||
<Clock className="h-3 w-3" />
|
<Clock className="h-3 w-3" />
|
||||||
{event.startTime.toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" })} - {event.endTime.toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" })}
|
{event.startTime.toLocaleTimeString(LOCALE, { hour: "2-digit", minute: "2-digit", hour12: false, timeZone: TIMEZONE })}
|
||||||
|
{" - "}
|
||||||
|
{event.endTime.toLocaleTimeString(LOCALE, { hour: "2-digit", minute: "2-digit", hour12: false, timeZone: TIMEZONE })}
|
||||||
</div>
|
</div>
|
||||||
{event.tags && event.tags.length > 0 && (
|
{event.tags && event.tags.length > 0 && (
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user