develop #83

Merged
M-Gabrielly merged 426 commits from develop into main 2025-12-04 04:13:15 +00:00
4 changed files with 299 additions and 71 deletions
Showing only changes of commit ac66a68c04 - Show all commits

View File

@ -38,6 +38,7 @@ export default function AgendamentoPage() {
// --- NOVO ESTADO --- // --- NOVO ESTADO ---
// Estado para alimentar o NOVO EventManager com dados da API // Estado para alimentar o NOVO EventManager com dados da API
const [managerEvents, setManagerEvents] = useState<Event[]>([]); const [managerEvents, setManagerEvents] = useState<Event[]>([]);
const [managerLoading, setManagerLoading] = useState<boolean>(true);
useEffect(() => { useEffect(() => {
document.addEventListener("keydown", (event) => { document.addEventListener("keydown", (event) => {
@ -57,6 +58,7 @@ export default function AgendamentoPage() {
let mounted = true; let mounted = true;
(async () => { (async () => {
try { try {
setManagerLoading(true);
const api = await import('@/lib/api'); const api = await import('@/lib/api');
const arr = await api.listarAgendamentos('select=*&order=scheduled_at.desc&limit=500').catch(() => []); const arr = await api.listarAgendamentos('select=*&order=scheduled_at.desc&limit=500').catch(() => []);
if (!mounted) return; if (!mounted) return;
@ -64,6 +66,7 @@ export default function AgendamentoPage() {
setAppointments([]); setAppointments([]);
setThreeDEvents([]); setThreeDEvents([]);
setManagerEvents([]); // Limpa o novo calendário setManagerEvents([]); // Limpa o novo calendário
setManagerLoading(false);
return; return;
} }
@ -98,6 +101,7 @@ export default function AgendamentoPage() {
}; };
}); });
setManagerEvents(newManagerEvents); setManagerEvents(newManagerEvents);
setManagerLoading(false);
// --- FIM DA LÓGICA --- // --- FIM DA LÓGICA ---
// Convert to 3D calendar events (MANTIDO 100%) // Convert to 3D calendar events (MANTIDO 100%)
@ -121,6 +125,7 @@ export default function AgendamentoPage() {
setAppointments([]); setAppointments([]);
setThreeDEvents([]); setThreeDEvents([]);
setManagerEvents([]); // Limpa o novo calendário setManagerEvents([]); // Limpa o novo calendário
setManagerLoading(false);
} }
})(); })();
return () => { mounted = false; }; return () => { mounted = false; };
@ -216,9 +221,19 @@ export default function AgendamentoPage() {
{/* --- AQUI ESTÁ A SUBSTITUIÇÃO --- */} {/* --- AQUI ESTÁ A SUBSTITUIÇÃO --- */}
{activeTab === "calendar" ? ( {activeTab === "calendar" ? (
<div className="flex w-full"> <div className="flex w-full">
{/* O FullCalendar foi substituído pelo EventManager, {/* mostra loading até managerEvents ser preenchido (API integrada desde a entrada) */}
agora alimentado pelo estado dinâmico 'managerEvents' */} <div className="w-full">
<EventManager events={managerEvents} /> {managerLoading ? (
<div className="flex items-center justify-center w-full min-h-[70vh]">
<div className="text-sm text-muted-foreground">Conectando ao calendário carregando agendamentos...</div>
</div>
) : (
// EventManager ocupa a área principal e já recebe events da API
<div className="w-full min-h-[70vh]">
<EventManager events={managerEvents} className="compact-event-manager" />
</div>
)}
</div>
</div> </div>
) : activeTab === "3d" ? ( ) : activeTab === "3d" ? (
// O calendário 3D (ThreeDWallCalendar) foi MANTIDO 100% // O calendário 3D (ThreeDWallCalendar) foi MANTIDO 100%

View File

@ -0,0 +1,118 @@
import React, { useState, useCallback, useMemo } from "react";
import { EventCard } from "./EventCard";
import { Card } from "@/components/ui/card";
// Types
import { Event } from "@/components/event-manager";
// Week View Component
export function WeekView({
currentDate,
events,
onEventClick,
onDragStart,
onDragEnd,
onDrop,
getColorClasses,
}: {
currentDate: Date;
events: Event[];
onEventClick: (event: Event) => void;
onDragStart: (event: Event) => void;
onDragEnd: () => void;
onDrop: (date: Date, hour: number) => void;
getColorClasses: (color: string) => { bg: string; text: string };
}) {
const startOfWeek = new Date(currentDate);
startOfWeek.setDate(currentDate.getDay());
const weekDays = Array.from({ length: 7 }, (_, i) => {
const day = new Date(startOfWeek);
day.setDate(startOfWeek.getDate() + i);
return day;
});
const hours = Array.from({ length: 24 }, (_, i) => i);
const getEventsForDayAndHour = (date: Date, hour: number) => {
return events.filter((event) => {
const eventDate = new Date(event.startTime);
const eventHour = eventDate.getHours();
return (
eventDate.getDate() === date.getDate() &&
eventDate.getMonth() === date.getMonth() &&
eventDate.getFullYear() === date.getFullYear() &&
eventHour === hour
);
});
};
// dias da semana em pt-BR (abreviações)
const weekDayNames = ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"];
return (
<Card className="overflow-auto">
<div className="grid grid-cols-8 border-b">
<div className="border-r p-2 text-center text-xs font-medium sm:text-sm">
Hora
</div>
{weekDays.map((day) => (
<div
key={day.toISOString()}
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="sm:hidden">
{day.toLocaleDateString("pt-BR", { weekday: "narrow" })}
</div>
<div className="text-[10px] text-muted-foreground sm:text-xs">
{day.toLocaleDateString("pt-BR", {
month: "short",
day: "numeric",
})}
</div>
</div>
))}
</div>
<div className="grid grid-cols-8">
{hours.map((hour) => (
<React.Fragment key={`hour-${hour}`}>
<div
key={`time-${hour}`}
className="border-b border-r p-1 text-[10px] text-muted-foreground sm:p-2 sm:text-xs"
>
{hour.toString().padStart(2, "0")}:00
</div>
{weekDays.map((day) => {
const dayEvents = getEventsForDayAndHour(day, hour);
return (
<div
key={`${day.toISOString()}-${hour}`}
className="min-h-12 border-b border-r p-0.5 transition-colors hover:bg-accent/50 last:border-r-0 sm:min-h-16 sm:p-1"
onDragOver={(e) => e.preventDefault()}
onDrop={() => onDrop(day, hour)}
>
<div className="space-y-1">
{dayEvents.map((event) => (
<EventCard
key={event.id}
event={event}
onEventClick={onEventClick}
onDragStart={onDragStart}
onDragEnd={onDragEnd}
getColorClasses={getColorClasses}
variant="default"
/>
))}
</div>
</div>
);
})}
</React.Fragment>
))}
</div>
</Card>
);
}

View File

@ -0,0 +1,103 @@
import React, { useState } from "react";
import { Event } from "@/components/event-manager";
import { cn } from "@/lib/utils";
/*
Componente leve para representar um evento no calendário.
Compatível com o uso em Calendar.tsx (WeekView / DayView).
*/
export function EventCard({
event,
onEventClick,
onDragStart,
onDragEnd,
getColorClasses,
variant = "default",
}: {
event: Event;
onEventClick: (e: Event) => void;
onDragStart: (e: Event) => void;
onDragEnd: () => void;
getColorClasses: (color: string) => { bg: string; text: string };
variant?: "default" | "compact" | "detailed";
}) {
const [hover, setHover] = useState(false);
const color = getColorClasses?.(event.color) ?? { bg: "bg-slate-400", text: "text-white" };
const handleDragStart = (e: React.DragEvent) => {
e.dataTransfer.setData("text/plain", event.id);
onDragStart && onDragStart(event);
};
const handleClick = () => {
onEventClick && onEventClick(event);
};
if (variant === "compact") {
return (
<div
draggable
onDragStart={handleDragStart}
onDragEnd={() => onDragEnd && onDragEnd()}
onClick={handleClick}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
className={cn(
"rounded px-2 py-0.5 text-xs font-medium truncate",
color.bg,
color.text,
"cursor-pointer transition-all",
hover && "shadow-md scale-105"
)}
>
{event.title}
</div>
);
}
if (variant === "detailed") {
return (
<div
draggable
onDragStart={handleDragStart}
onDragEnd={() => onDragEnd && onDragEnd()}
onClick={handleClick}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
className={cn(
"rounded-lg p-2 text-sm cursor-pointer transition-all",
color.bg,
color.text,
hover && "shadow-lg scale-[1.02]"
)}
>
<div className="font-semibold">{event.title}</div>
{event.description && <div className="text-xs opacity-90 mt-1 line-clamp-2">{event.description}</div>}
<div className="mt-1 text-[11px] opacity-80">
{event.startTime?.toLocaleTimeString?.("pt-BR", { hour: "2-digit", minute: "2-digit" }) ?? ""} - {event.endTime?.toLocaleTimeString?.("pt-BR", { hour: "2-digit", minute: "2-digit" }) ?? ""}
</div>
</div>
);
}
// default
return (
<div
draggable
onDragStart={handleDragStart}
onDragEnd={() => onDragEnd && onDragEnd()}
onClick={handleClick}
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
className={cn(
"relative rounded px-2 py-1 text-xs font-medium cursor-pointer transition-all",
color.bg,
color.text,
hover && "shadow-md scale-105"
)}
>
<div className="truncate">{event.title}</div>
</div>
);
}

View File

@ -1,6 +1,6 @@
"use client" "use client"
import { useState, useCallback, useMemo } from "react" import React, { useState, useCallback, useMemo } 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"
@ -263,30 +263,30 @@ 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("en-US", { currentDate.toLocaleDateString("pt-BR", {
month: "long", month: "long",
year: "numeric", year: "numeric",
})} })}
{view === "week" && {view === "week" &&
`Week of ${currentDate.toLocaleDateString("en-US", { `Semana de ${currentDate.toLocaleDateString("pt-BR", {
month: "short", month: "short",
day: "numeric", day: "numeric",
})}`} })}`}
{view === "day" && {view === "day" &&
currentDate.toLocaleDateString("en-US", { currentDate.toLocaleDateString("pt-BR", {
weekday: "long", weekday: "long",
month: "long", month: "long",
day: "numeric", day: "numeric",
year: "numeric", year: "numeric",
})} })}
{view === "list" && "All Events"} {view === "list" && "Todos os eventos"}
</h2> </h2>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button variant="outline" size="icon" onClick={() => navigateDate("prev")} className="h-8 w-8"> <Button variant="outline" size="icon" onClick={() => navigateDate("prev")} className="h-8 w-8">
<ChevronLeft className="h-4 w-4" /> <ChevronLeft className="h-4 w-4" />
</Button> </Button>
<Button variant="outline" size="sm" onClick={() => setCurrentDate(new Date())}> <Button variant="outline" size="sm" onClick={() => setCurrentDate(new Date())}>
Today Hoje
</Button> </Button>
<Button variant="outline" size="icon" onClick={() => navigateDate("next")} className="h-8 w-8"> <Button variant="outline" size="icon" onClick={() => navigateDate("next")} className="h-8 w-8">
<ChevronRight className="h-4 w-4" /> <ChevronRight className="h-4 w-4" />
@ -305,25 +305,25 @@ export function EventManager({
<SelectItem value="month"> <SelectItem value="month">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Calendar className="h-4 w-4" /> <Calendar className="h-4 w-4" />
Month View Mês
</div> </div>
</SelectItem> </SelectItem>
<SelectItem value="week"> <SelectItem value="week">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Grid3x3 className="h-4 w-4" /> <Grid3x3 className="h-4 w-4" />
Week View Semana
</div> </div>
</SelectItem> </SelectItem>
<SelectItem value="day"> <SelectItem value="day">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Clock className="h-4 w-4" /> <Clock className="h-4 w-4" />
Day View Dia
</div> </div>
</SelectItem> </SelectItem>
<SelectItem value="list"> <SelectItem value="list">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<List className="h-4 w-4" /> <List className="h-4 w-4" />
List View Lista
</div> </div>
</SelectItem> </SelectItem>
</SelectContent> </SelectContent>
@ -339,7 +339,7 @@ export function EventManager({
className="h-8" className="h-8"
> >
<Calendar className="h-4 w-4" /> <Calendar className="h-4 w-4" />
<span className="ml-1">Month</span> <span className="ml-1">Mês</span>
</Button> </Button>
<Button <Button
variant={view === "week" ? "secondary" : "ghost"} variant={view === "week" ? "secondary" : "ghost"}
@ -348,7 +348,7 @@ export function EventManager({
className="h-8" className="h-8"
> >
<Grid3x3 className="h-4 w-4" /> <Grid3x3 className="h-4 w-4" />
<span className="ml-1">Week</span> <span className="ml-1">Semana</span>
</Button> </Button>
<Button <Button
variant={view === "day" ? "secondary" : "ghost"} variant={view === "day" ? "secondary" : "ghost"}
@ -357,7 +357,7 @@ export function EventManager({
className="h-8" className="h-8"
> >
<Clock className="h-4 w-4" /> <Clock className="h-4 w-4" />
<span className="ml-1">Day</span> <span className="ml-1">Dia</span>
</Button> </Button>
<Button <Button
variant={view === "list" ? "secondary" : "ghost"} variant={view === "list" ? "secondary" : "ghost"}
@ -366,7 +366,7 @@ export function EventManager({
className="h-8" className="h-8"
> >
<List className="h-4 w-4" /> <List className="h-4 w-4" />
<span className="ml-1">List</span> <span className="ml-1">Lista</span>
</Button> </Button>
</div> </div>
@ -378,7 +378,7 @@ export function EventManager({
className="w-full sm:w-auto" className="w-full sm:w-auto"
> >
<Plus className="mr-2 h-4 w-4" /> <Plus className="mr-2 h-4 w-4" />
New Event Novo Evento
</Button> </Button>
</div> </div>
</div> </div>
@ -387,7 +387,7 @@ export function EventManager({
<div className="relative flex-1"> <div className="relative flex-1">
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" /> <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<Input <Input
placeholder="Search events..." placeholder="Buscar eventos..."
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
className="pl-9" className="pl-9"
@ -412,7 +412,7 @@ export function EventManager({
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="gap-2 whitespace-nowrap flex-shrink-0 bg-transparent"> <Button variant="outline" size="sm" className="gap-2 whitespace-nowrap flex-shrink-0 bg-transparent">
<Filter className="h-4 w-4" /> <Filter className="h-4 w-4" />
Colors Cores
{selectedColors.length > 0 && ( {selectedColors.length > 0 && (
<Badge variant="secondary" className="ml-1 h-5 px-1.5"> <Badge variant="secondary" className="ml-1 h-5 px-1.5">
{selectedColors.length} {selectedColors.length}
@ -421,7 +421,7 @@ export function EventManager({
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-48"> <DropdownMenuContent align="start" className="w-48">
<DropdownMenuLabel>Filter by Color</DropdownMenuLabel> <DropdownMenuLabel>Filtrar por Cor</DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
{colors.map((color) => ( {colors.map((color) => (
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
@ -456,7 +456,7 @@ export function EventManager({
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-48"> <DropdownMenuContent align="start" className="w-48">
<DropdownMenuLabel>Filter by Tag</DropdownMenuLabel> <DropdownMenuLabel>Filtrar por Tag</DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
{availableTags.map((tag) => ( {availableTags.map((tag) => (
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
@ -477,7 +477,7 @@ export function EventManager({
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="gap-2 whitespace-nowrap flex-shrink-0 bg-transparent"> <Button variant="outline" size="sm" className="gap-2 whitespace-nowrap flex-shrink-0 bg-transparent">
<Filter className="h-4 w-4" /> <Filter className="h-4 w-4" />
Categories Categorias
{selectedCategories.length > 0 && ( {selectedCategories.length > 0 && (
<Badge variant="secondary" className="ml-1 h-5 px-1.5"> <Badge variant="secondary" className="ml-1 h-5 px-1.5">
{selectedCategories.length} {selectedCategories.length}
@ -486,7 +486,7 @@ export function EventManager({
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start" className="w-48"> <DropdownMenuContent align="start" className="w-48">
<DropdownMenuLabel>Filter by Category</DropdownMenuLabel> <DropdownMenuLabel>Filtrar por Categoria</DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
{categories.map((category) => ( {categories.map((category) => (
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
@ -512,7 +512,7 @@ export function EventManager({
className="gap-2 whitespace-nowrap flex-shrink-0" className="gap-2 whitespace-nowrap flex-shrink-0"
> >
<X className="h-4 w-4" /> <X className="h-4 w-4" />
Clear Filters Limpar Filtros
</Button> </Button>
)} )}
</div> </div>
@ -525,7 +525,7 @@ export function EventManager({
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="gap-2 bg-transparent"> <Button variant="outline" size="sm" className="gap-2 bg-transparent">
<Filter className="h-4 w-4" /> <Filter className="h-4 w-4" />
Colors Cores
{selectedColors.length > 0 && ( {selectedColors.length > 0 && (
<Badge variant="secondary" className="ml-1 h-5 px-1"> <Badge variant="secondary" className="ml-1 h-5 px-1">
{selectedColors.length} {selectedColors.length}
@ -534,7 +534,7 @@ export function EventManager({
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48"> <DropdownMenuContent align="end" className="w-48">
<DropdownMenuLabel>Filter by Color</DropdownMenuLabel> <DropdownMenuLabel>Filtrar por Cor</DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
{colors.map((color) => ( {colors.map((color) => (
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
@ -569,7 +569,7 @@ export function EventManager({
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48"> <DropdownMenuContent align="end" className="w-48">
<DropdownMenuLabel>Filter by Tag</DropdownMenuLabel> <DropdownMenuLabel>Filtrar por Tag</DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
{availableTags.map((tag) => ( {availableTags.map((tag) => (
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
@ -590,7 +590,7 @@ export function EventManager({
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className="gap-2 bg-transparent"> <Button variant="outline" size="sm" className="gap-2 bg-transparent">
<Filter className="h-4 w-4" /> <Filter className="h-4 w-4" />
Categories Categorias
{selectedCategories.length > 0 && ( {selectedCategories.length > 0 && (
<Badge variant="secondary" className="ml-1 h-5 px-1"> <Badge variant="secondary" className="ml-1 h-5 px-1">
{selectedCategories.length} {selectedCategories.length}
@ -599,7 +599,7 @@ export function EventManager({
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48"> <DropdownMenuContent align="end" className="w-48">
<DropdownMenuLabel>Filter by Category</DropdownMenuLabel> <DropdownMenuLabel>Filtrar por Categoria</DropdownMenuLabel>
<DropdownMenuSeparator /> <DropdownMenuSeparator />
{categories.map((category) => ( {categories.map((category) => (
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
@ -620,7 +620,7 @@ export function EventManager({
{hasActiveFilters && ( {hasActiveFilters && (
<Button variant="ghost" size="sm" onClick={clearFilters} className="gap-2"> <Button variant="ghost" size="sm" onClick={clearFilters} className="gap-2">
<X className="h-4 w-4" /> <X className="h-4 w-4" />
Clear Limpar
</Button> </Button>
)} )}
</div> </div>
@ -628,7 +628,7 @@ export function EventManager({
{hasActiveFilters && ( {hasActiveFilters && (
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
<span className="text-sm text-muted-foreground">Active filters:</span> <span className="text-sm text-muted-foreground">Filtros ativos:</span>
{selectedColors.map((colorValue) => { {selectedColors.map((colorValue) => {
const color = getColorClasses(colorValue) const color = getColorClasses(colorValue)
return ( return (
@ -678,8 +678,8 @@ export function EventManager({
setSelectedEvent(event) setSelectedEvent(event)
setIsDialogOpen(true) setIsDialogOpen(true)
}} }}
onDragStart={handleDragStart} onDragStart={(event) => handleDragStart(event)}
onDragEnd={handleDragEnd} onDragEnd={() => handleDragEnd()}
onDrop={handleDrop} onDrop={handleDrop}
getColorClasses={getColorClasses} getColorClasses={getColorClasses}
/> />
@ -693,8 +693,8 @@ export function EventManager({
setSelectedEvent(event) setSelectedEvent(event)
setIsDialogOpen(true) setIsDialogOpen(true)
}} }}
onDragStart={handleDragStart} onDragStart={(event) => handleDragStart(event)}
onDragEnd={handleDragEnd} onDragEnd={() => handleDragEnd()}
onDrop={handleDrop} onDrop={handleDrop}
getColorClasses={getColorClasses} getColorClasses={getColorClasses}
/> />
@ -708,8 +708,8 @@ export function EventManager({
setSelectedEvent(event) setSelectedEvent(event)
setIsDialogOpen(true) setIsDialogOpen(true)
}} }}
onDragStart={handleDragStart} onDragStart={(event) => handleDragStart(event)}
onDragEnd={handleDragEnd} onDragEnd={() => handleDragEnd()}
onDrop={handleDrop} onDrop={handleDrop}
getColorClasses={getColorClasses} getColorClasses={getColorClasses}
/> />
@ -730,15 +730,15 @@ export function EventManager({
<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 ? "Create Event" : "Event Details"}</DialogTitle> <DialogTitle>{isCreating ? "Criar Evento" : "Detalhes do Evento"}</DialogTitle>
<DialogDescription> <DialogDescription>
{isCreating ? "Add a new event to your calendar" : "View and edit event details"} {isCreating ? "Adicione um novo evento ao seu calendário" : "Visualizar e editar detalhes do evento"}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4"> <div className="space-y-4">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="title">Title</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}
@ -747,12 +747,12 @@ export function EventManager({
? setNewEvent((prev) => ({ ...prev, title: e.target.value })) ? setNewEvent((prev) => ({ ...prev, title: e.target.value }))
: setSelectedEvent((prev) => (prev ? { ...prev, title: e.target.value } : null)) : setSelectedEvent((prev) => (prev ? { ...prev, title: e.target.value } : null))
} }
placeholder="Event title" placeholder="Título do evento"
/> />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="description">Description</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}
@ -764,14 +764,14 @@ export function EventManager({
})) }))
: setSelectedEvent((prev) => (prev ? { ...prev, description: e.target.value } : null)) : setSelectedEvent((prev) => (prev ? { ...prev, description: e.target.value } : null))
} }
placeholder="Event description" placeholder="Descrição do evento"
rows={3} rows={3}
/> />
</div> </div>
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="startTime">Start Time</Label> <Label htmlFor="startTime">Início</Label>
<Input <Input
id="startTime" id="startTime"
type="datetime-local" type="datetime-local"
@ -800,7 +800,7 @@ export function EventManager({
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="endTime">End Time</Label> <Label htmlFor="endTime">Fim</Label>
<Input <Input
id="endTime" id="endTime"
type="datetime-local" type="datetime-local"
@ -829,7 +829,7 @@ export function EventManager({
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="category">Category</Label> <Label htmlFor="category">Categoria</Label>
<Select <Select
value={isCreating ? newEvent.category : selectedEvent?.category} value={isCreating ? newEvent.category : selectedEvent?.category}
onValueChange={(value) => onValueChange={(value) =>
@ -839,7 +839,7 @@ export function EventManager({
} }
> >
<SelectTrigger id="category"> <SelectTrigger id="category">
<SelectValue placeholder="Select category" /> <SelectValue placeholder="Selecione a categoria" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{categories.map((cat) => ( {categories.map((cat) => (
@ -852,7 +852,7 @@ export function EventManager({
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="color">Color</Label> <Label htmlFor="color">Cor</Label>
<Select <Select
value={isCreating ? newEvent.color : selectedEvent?.color} value={isCreating ? newEvent.color : selectedEvent?.color}
onValueChange={(value) => onValueChange={(value) =>
@ -862,7 +862,7 @@ export function EventManager({
} }
> >
<SelectTrigger id="color"> <SelectTrigger id="color">
<SelectValue placeholder="Select color" /> <SelectValue placeholder="Selecione a cor" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
{colors.map((color) => ( {colors.map((color) => (
@ -901,7 +901,7 @@ export function EventManager({
<DialogFooter> <DialogFooter>
{!isCreating && ( {!isCreating && (
<Button variant="destructive" onClick={() => selectedEvent && handleDeleteEvent(selectedEvent.id)}> <Button variant="destructive" onClick={() => selectedEvent && handleDeleteEvent(selectedEvent.id)}>
Delete Deletar
</Button> </Button>
)} )}
<Button <Button
@ -912,10 +912,10 @@ export function EventManager({
setSelectedEvent(null) setSelectedEvent(null)
}} }}
> >
Cancel Cancelar
</Button> </Button>
<Button onClick={isCreating ? handleCreateEvent : handleUpdateEvent}> <Button onClick={isCreating ? handleCreateEvent : handleUpdateEvent}>
{isCreating ? "Create" : "Save"} {isCreating ? "Criar" : "Salvar"}
</Button> </Button>
</DialogFooter> </DialogFooter>
</DialogContent> </DialogContent>
@ -1160,7 +1160,7 @@ function MonthView({
return ( return (
<Card className="overflow-hidden"> <Card className="overflow-hidden">
<div className="grid grid-cols-7 border-b"> <div className="grid grid-cols-7 border-b">
{["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((day) => ( {["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"].map((day) => (
<div key={day} className="border-r p-2 text-center text-xs font-medium last:border-r-0 sm:text-sm"> <div key={day} className="border-r p-2 text-center text-xs font-medium last:border-r-0 sm:text-sm">
<span className="hidden sm:inline">{day}</span> <span className="hidden sm:inline">{day}</span>
<span className="sm:hidden">{day.charAt(0)}</span> <span className="sm:hidden">{day.charAt(0)}</span>
@ -1204,8 +1204,8 @@ function MonthView({
variant="compact" variant="compact"
/> />
))} ))}
{dayEvents.length > 3 && ( {dayEvents.length > 3 && (
<div className="text-[10px] text-muted-foreground sm:text-xs">+{dayEvents.length - 3} more</div> <div className="text-[10px] text-muted-foreground sm:text-xs">+{dayEvents.length - 3} mais</div>
)} )}
</div> </div>
</div> </div>
@ -1261,16 +1261,16 @@ function WeekView({
return ( return (
<Card className="overflow-auto"> <Card className="overflow-auto">
<div className="grid grid-cols-8 border-b"> <div className="grid grid-cols-8 border-b">
<div className="border-r p-2 text-center text-xs font-medium sm:text-sm">Time</div> <div className="border-r p-2 text-center text-xs font-medium sm:text-sm">Hora</div>
{weekDays.map((day) => ( {weekDays.map((day) => (
<div <div
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("en-US", { weekday: "short" })}</div> <div className="hidden sm:block">{day.toLocaleDateString("pt-BR", { weekday: "short" })}</div>
<div className="sm:hidden">{day.toLocaleDateString("en-US", { weekday: "narrow" })}</div> <div className="sm:hidden">{day.toLocaleDateString("pt-BR", { weekday: "narrow" })}</div>
<div className="text-[10px] text-muted-foreground sm:text-xs"> <div className="text-[10px] text-muted-foreground sm:text-xs">
{day.toLocaleDateString("en-US", { month: "short", day: "numeric" })} {day.toLocaleDateString("pt-BR", { month: "short", day: "numeric" })}
</div> </div>
</div> </div>
))} ))}
@ -1401,7 +1401,7 @@ function ListView({
const groupedEvents = sortedEvents.reduce( const groupedEvents = sortedEvents.reduce(
(acc, event) => { (acc, event) => {
const dateKey = event.startTime.toLocaleDateString("en-US", { const dateKey = event.startTime.toLocaleDateString("pt-BR", {
weekday: "long", weekday: "long",
year: "numeric", year: "numeric",
month: "long", month: "long",
@ -1456,15 +1456,7 @@ 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("en-US", { {event.startTime.toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" })} - {event.endTime.toLocaleTimeString("pt-BR", { hour: "2-digit", minute: "2-digit" })}
hour: "2-digit",
minute: "2-digit",
})}{" "}
-{" "}
{event.endTime.toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
})}
</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">
@ -1485,7 +1477,7 @@ function ListView({
</div> </div>
))} ))}
{sortedEvents.length === 0 && ( {sortedEvents.length === 0 && (
<div className="py-12 text-center text-sm text-muted-foreground sm:text-base">No events found</div> <div className="py-12 text-center text-sm text-muted-foreground sm:text-base">Nenhum evento encontrado</div>
)} )}
</div> </div>
</Card> </Card>