feat: add i18n for common terms and implement mobile calendar page with event management

This commit is contained in:
Michael committed 2025-12-10 11:46:19 +01:00
1 parent 193fc79a6e
commit 970a9d6de4
3 files changed
+214 -10

No files matched your search

+12
View File
@@ -0,0 +1,12 @@
{
"app": {
"title": "NoteFlow"
},
"common": {
"loading": "Loading...",
"error": "An error occurred",
"save": "Save",
"cancel": "Cancel",
"delete": "Delete"
}
}
+12
View File
@@ -0,0 +1,12 @@
{
"app": {
"title": "NoteFlow"
},
"common": {
"loading": "Chargement...",
"error": "Une erreur est survenue",
"save": "Enregistrer",
"cancel": "Annuler",
"delete": "Supprimer"
}
}
+190 -10
View File
@@ -19,6 +19,13 @@ export default function CalendarPage() {
const [editEventModal, setEditEventModal] = useState<{ open: boolean; event?: CalendarEvent }>({ open: false });
const [loading, setLoading] = useState(false);
// Form states
const [newEventTitle, setNewEventTitle] = useState("");
const [newEventDescription, setNewEventDescription] = useState("");
const [newEventLocation, setNewEventLocation] = useState("");
const [newEventStart, setNewEventStart] = useState("");
const [newEventEnd, setNewEventEnd] = useState("");
const loadEvents = async () => {
try {
const data = await CalendarService.getEvents(100);
@@ -46,6 +53,92 @@ export default function CalendarPage() {
}
};
const resetForm = () => {
setNewEventTitle("");
setNewEventDescription("");
setNewEventLocation("");
// Default start time: next hour
const now = new Date();
now.setMinutes(0, 0, 0);
now.setHours(now.getHours() + 1);
const end = new Date(now);
end.setHours(end.getHours() + 1);
// Format for datetime-local input (YYYY-MM-DDTHH:mm)
const formatDateTime = (date: Date) => {
const pad = (n: number) => n.toString().padStart(2, '0');
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
};
setNewEventStart(formatDateTime(now));
setNewEventEnd(formatDateTime(end));
};
useEffect(() => {
if (addEventModal) {
resetForm();
}
}, [addEventModal]);
useEffect(() => {
if (editEventModal.open && editEventModal.event) {
const event = editEventModal.event;
setNewEventTitle(event.title);
setNewEventDescription(event.description || "");
setNewEventLocation(event.location || "");
const formatDateTime = (dateStr: string) => {
const date = new Date(dateStr);
const pad = (n: number) => n.toString().padStart(2, '0');
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
};
setNewEventStart(formatDateTime(event.start_time));
setNewEventEnd(formatDateTime(event.end_time));
}
}, [editEventModal.open]);
const handleCreateEvent = async () => {
if (!newEventTitle || !newEventStart || !newEventEnd) {
showError("Veuillez remplir les champs obligatoires");
return;
}
try {
await CalendarService.createEvent({
title: newEventTitle,
description: newEventDescription,
location: newEventLocation,
startDateTime: new Date(newEventStart).toISOString(),
endDateTime: new Date(newEventEnd).toISOString(),
});
showSuccess("Événement créé avec succès");
setAddEventModal(false);
loadEvents();
} catch (error) {
showError("Erreur lors de la création de l'événement");
}
};
const handleUpdateEvent = async () => {
if (!editEventModal.event || !newEventTitle || !newEventStart || !newEventEnd) return;
try {
await CalendarService.updateEvent(editEventModal.event.id, {
title: newEventTitle,
description: newEventDescription,
location: newEventLocation,
startDateTime: new Date(newEventStart).toISOString(),
endDateTime: new Date(newEventEnd).toISOString(),
});
showSuccess("Événement modifié avec succès");
setEditEventModal({ open: false });
loadEvents();
} catch (error) {
showError("Erreur lors de la modification de l'événement");
}
};
const totalPages = Math.ceil(events.length / EVENTS_PER_PAGE);
const paginatedEvents = events.slice(page * EVENTS_PER_PAGE, (page + 1) * EVENTS_PER_PAGE);
@@ -79,8 +172,7 @@ export default function CalendarPage() {
return (
<MobileCard
key={event.id}
className={`${
isSoon ? 'border-red-500 bg-red-50 dark:bg-red-900/20' :
className={`${isSoon ? 'border-red-500 bg-red-50 dark:bg-red-900/20' :
isToday ? 'bg-red-50/50 dark:bg-red-900/10' : ''
}`}
>
@@ -158,27 +250,115 @@ export default function CalendarPage() {
{/* FAB */}
<MobileFAB icon={Plus} onClick={() => setAddEventModal(true)} label="Ajouter un événement" />
{/* Add Event Modal - Placeholder for now */}
{/* Add Event Modal */}
<Dialog open={addEventModal} onOpenChange={setAddEventModal}>
<DialogContent>
<DialogHeader>
<DialogTitle>Nouvel événement</DialogTitle>
<DialogDescription>
Cette fonctionnalité sera disponible prochainement
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label>Titre</Label>
<Input
placeholder="Titre de l'événement"
value={newEventTitle}
onChange={(e) => setNewEventTitle(e.target.value)}
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label>Début</Label>
<Input
type="datetime-local"
value={newEventStart}
onChange={(e) => setNewEventStart(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label>Fin</Label>
<Input
type="datetime-local"
value={newEventEnd}
onChange={(e) => setNewEventEnd(e.target.value)}
/>
</div>
</div>
<div className="space-y-2">
<Label>Lieu</Label>
<Input
placeholder="Lieu (optionnel)"
value={newEventLocation}
onChange={(e) => setNewEventLocation(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label>Description</Label>
<Input
placeholder="Description (optionnel)"
value={newEventDescription}
onChange={(e) => setNewEventDescription(e.target.value)}
/>
</div>
<Button className="w-full" onClick={handleCreateEvent}>
Créer l'événement
</Button>
</div>
</DialogContent>
</Dialog>
{/* Edit Event Modal - Placeholder for now */}
{/* Edit Event Modal */}
<Dialog open={editEventModal.open} onOpenChange={(open) => setEditEventModal({ open })}>
<DialogContent>
<DialogHeader>
<DialogTitle>Modifier l'événement</DialogTitle>
<DialogDescription>
Cette fonctionnalité sera disponible prochainement
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label>Titre</Label>
<Input
placeholder="Titre de l'événement"
value={newEventTitle}
onChange={(e) => setNewEventTitle(e.target.value)}
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label>Début</Label>
<Input
type="datetime-local"
value={newEventStart}
onChange={(e) => setNewEventStart(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label>Fin</Label>
<Input
type="datetime-local"
value={newEventEnd}
onChange={(e) => setNewEventEnd(e.target.value)}
/>
</div>
</div>
<div className="space-y-2">
<Label>Lieu</Label>
<Input
placeholder="Lieu (optionnel)"
value={newEventLocation}
onChange={(e) => setNewEventLocation(e.target.value)}
/>
</div>
<div className="space-y-2">
<Label>Description</Label>
<Input
placeholder="Description (optionnel)"
value={newEventDescription}
onChange={(e) => setNewEventDescription(e.target.value)}
/>
</div>
<Button className="w-full" onClick={handleUpdateEvent}>
Enregistrer les modifications
</Button>
</div>
</DialogContent>
</Dialog>
</div>