vision refactor

This commit is contained in:
alma 2026-01-15 19:21:19 +01:00
parent b5e05bdd81
commit 6eb510e7d7

View File

@ -12,6 +12,20 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Textarea } from "@/components/ui/textarea";
import { Checkbox } from "@/components/ui/checkbox";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import DatePicker, { registerLocale } from "react-datepicker";
import "react-datepicker/dist/react-datepicker.css";
import { fr } from "date-fns/locale";
registerLocale('fr', fr);
interface Group { interface Group {
id: string; id: string;
@ -56,16 +70,24 @@ export default function VisionPage() {
type: "group" | "mission" | ""; type: "group" | "mission" | "";
entityId: string; entityId: string;
entityName: string; entityName: string;
date: string;
time: string;
title: string; title: string;
start: string;
end: string;
allDay: boolean;
location: string;
description: string;
recurrence: "none" | "daily" | "weekly" | "monthly";
}>({ }>({
type: "", type: "",
entityId: "", entityId: "",
entityName: "", entityName: "",
date: "",
time: "",
title: "", title: "",
start: "",
end: "",
allDay: false,
location: "",
description: "",
recurrence: "none",
}); });
// Redirect if not authenticated // Redirect if not authenticated
@ -186,22 +208,42 @@ export default function VisionPage() {
return scheduledMeetings.filter(meeting => meeting.date === dateStr); return scheduledMeetings.filter(meeting => meeting.date === dateStr);
}; };
// Helper function to get date from string
const getDateFromString = (dateString: string): Date | null => {
if (!dateString) return null;
try {
return new Date(dateString);
} catch {
return null;
}
};
// Handle open meeting dialog // Handle open meeting dialog
const handleOpenMeetingDialog = (type: "group" | "mission", id: string, name: string) => { const handleOpenMeetingDialog = (type: "group" | "mission", id: string, name: string) => {
const defaultDate = selectedDate || new Date();
const startDate = new Date(defaultDate);
startDate.setHours(new Date().getHours(), 0, 0, 0);
const endDate = new Date(startDate);
endDate.setHours(startDate.getHours() + 1);
setMeetingForm({ setMeetingForm({
type, type,
entityId: id, entityId: id,
entityName: name, entityName: name,
date: selectedDate ? selectedDate.toISOString().split('T')[0] : new Date().toISOString().split('T')[0],
time: "",
title: "", title: "",
start: startDate.toISOString().slice(0, 16),
end: endDate.toISOString().slice(0, 16),
allDay: false,
location: "",
description: "",
recurrence: "none",
}); });
setShowMeetingDialog(true); setShowMeetingDialog(true);
}; };
// Handle save meeting // Handle save meeting
const handleSaveMeeting = () => { const handleSaveMeeting = () => {
if (!meetingForm.type || !meetingForm.entityId || !meetingForm.date || !meetingForm.time) { if (!meetingForm.type || !meetingForm.entityId || !meetingForm.start || !meetingForm.end) {
toast({ toast({
title: "Erreur", title: "Erreur",
description: "Veuillez remplir tous les champs requis", description: "Veuillez remplir tous les champs requis",
@ -210,29 +252,74 @@ export default function VisionPage() {
return; return;
} }
const newMeeting: ScheduledMeeting = { const startDate = new Date(meetingForm.start);
id: `${Date.now()}-${Math.random()}`, const endDate = new Date(meetingForm.end);
type: meetingForm.type,
entityId: meetingForm.entityId,
entityName: meetingForm.entityName,
date: meetingForm.date,
time: meetingForm.time,
title: meetingForm.title || `${meetingForm.type === "group" ? "Groupe" : "Mission"}: ${meetingForm.entityName}`,
};
setScheduledMeetings([...scheduledMeetings, newMeeting]); // Create meetings based on recurrence
const meetings: ScheduledMeeting[] = [];
if (meetingForm.recurrence === "none") {
// Single meeting
const dateStr = startDate.toISOString().split('T')[0];
const timeStr = meetingForm.allDay ? "" : startDate.toTimeString().slice(0, 5);
meetings.push({
id: `${Date.now()}-${Math.random()}`,
type: meetingForm.type,
entityId: meetingForm.entityId,
entityName: meetingForm.entityName,
date: dateStr,
time: timeStr,
title: meetingForm.title || `${meetingForm.type === "group" ? "Groupe" : "Mission"}: ${meetingForm.entityName}`,
});
} else {
// Recurring meetings - create for next 12 occurrences
const recurrenceCount = 12;
let currentDate = new Date(startDate);
const timeDiff = endDate.getTime() - startDate.getTime();
for (let i = 0; i < recurrenceCount; i++) {
const dateStr = currentDate.toISOString().split('T')[0];
const timeStr = meetingForm.allDay ? "" : currentDate.toTimeString().slice(0, 5);
meetings.push({
id: `${Date.now()}-${i}-${Math.random()}`,
type: meetingForm.type,
entityId: meetingForm.entityId,
entityName: meetingForm.entityName,
date: dateStr,
time: timeStr,
title: meetingForm.title || `${meetingForm.type === "group" ? "Groupe" : "Mission"}: ${meetingForm.entityName}`,
});
// Calculate next occurrence
if (meetingForm.recurrence === "daily") {
currentDate.setDate(currentDate.getDate() + 1);
} else if (meetingForm.recurrence === "weekly") {
currentDate.setDate(currentDate.getDate() + 7);
} else if (meetingForm.recurrence === "monthly") {
currentDate.setMonth(currentDate.getMonth() + 1);
}
}
}
setScheduledMeetings([...scheduledMeetings, ...meetings]);
setShowMeetingDialog(false); setShowMeetingDialog(false);
setMeetingForm({ setMeetingForm({
type: "", type: "",
entityId: "", entityId: "",
entityName: "", entityName: "",
date: "",
time: "",
title: "", title: "",
start: "",
end: "",
allDay: false,
location: "",
description: "",
recurrence: "none",
}); });
toast({ toast({
title: "Succès", title: "Succès",
description: "Réunion planifiée avec succès", description: `${meetings.length} réunion${meetings.length > 1 ? 's' : ''} planifiée${meetings.length > 1 ? 's' : ''} avec succès`,
}); });
}; };
@ -706,43 +793,177 @@ export default function VisionPage() {
{/* Meeting Dialog */} {/* Meeting Dialog */}
<Dialog open={showMeetingDialog} onOpenChange={setShowMeetingDialog}> <Dialog open={showMeetingDialog} onOpenChange={setShowMeetingDialog}>
<DialogContent className="sm:max-w-[500px]"> <DialogContent className="sm:max-w-lg">
<DialogHeader> <DialogHeader>
<DialogTitle>Planifier une réunion</DialogTitle> <DialogTitle className="text-gray-800">Planifier une réunion</DialogTitle>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-4"> <div className="space-y-4 py-4">
<div> <div className="grid gap-2">
<Label htmlFor="meeting-title">Titre (optionnel)</Label> <Label htmlFor="meeting-title" className="text-base font-semibold text-gray-800">Titre</Label>
<Input <Input
id="meeting-title" id="meeting-title"
value={meetingForm.title} value={meetingForm.title}
onChange={(e) => setMeetingForm({ ...meetingForm, title: e.target.value })} onChange={(e) => setMeetingForm({ ...meetingForm, title: e.target.value })}
placeholder={`Réunion ${meetingForm.type === "group" ? "du groupe" : "de la mission"}`} placeholder={`Réunion ${meetingForm.type === "group" ? "du groupe" : "de la mission"}`}
className="mt-1 bg-black text-white border-gray-700 placeholder-gray-400" className="bg-white text-gray-900"
/> />
</div> </div>
<div>
<Label htmlFor="meeting-date">Date *</Label> <div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label className="text-gray-800">Début *</Label>
<div className="flex gap-2">
<div className="flex-1">
<DatePicker
selected={getDateFromString(meetingForm.start)}
onChange={(date: Date | null) => {
if (date) {
const newStart = new Date(date);
if (!meetingForm.allDay) {
const currentStart = getDateFromString(meetingForm.start);
if (currentStart) {
newStart.setHours(currentStart.getHours(), currentStart.getMinutes());
}
}
setMeetingForm({ ...meetingForm, start: newStart.toISOString().slice(0, 16) });
}
}}
dateFormat="dd/MM/yyyy"
locale="fr"
className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary bg-white text-gray-900"
placeholderText="Date"
customInput={<Input className="bg-white text-gray-900" />}
/>
</div>
{!meetingForm.allDay && (
<DatePicker
selected={getDateFromString(meetingForm.start)}
onChange={(date: Date | null) => {
if (date) {
const currentStart = getDateFromString(meetingForm.start) || new Date();
const newStart = new Date(currentStart);
newStart.setHours(date.getHours(), date.getMinutes());
setMeetingForm({ ...meetingForm, start: newStart.toISOString().slice(0, 16) });
}
}}
showTimeSelect
showTimeSelectOnly
timeIntervals={15}
timeCaption="Heure"
dateFormat="HH:mm"
className="w-32 bg-white text-gray-900"
customInput={<Input className="bg-white text-gray-900" />}
/>
)}
</div>
</div>
<div className="space-y-2">
<Label className="text-gray-800">Fin *</Label>
<div className="flex gap-2">
<div className="flex-1">
<DatePicker
selected={getDateFromString(meetingForm.end)}
onChange={(date: Date | null) => {
if (date) {
const newEnd = new Date(date);
if (!meetingForm.allDay) {
const currentEnd = getDateFromString(meetingForm.end);
if (currentEnd) {
newEnd.setHours(currentEnd.getHours(), currentEnd.getMinutes());
}
}
setMeetingForm({ ...meetingForm, end: newEnd.toISOString().slice(0, 16) });
}
}}
dateFormat="dd/MM/yyyy"
locale="fr"
className="w-full rounded-md border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary bg-white text-gray-900"
placeholderText="Date"
customInput={<Input className="bg-white text-gray-900" />}
minDate={getDateFromString(meetingForm.start)}
/>
</div>
{!meetingForm.allDay && (
<DatePicker
selected={getDateFromString(meetingForm.end)}
onChange={(date: Date | null) => {
if (date) {
const currentEnd = getDateFromString(meetingForm.end) || new Date();
const newEnd = new Date(currentEnd);
newEnd.setHours(date.getHours(), date.getMinutes());
setMeetingForm({ ...meetingForm, end: newEnd.toISOString().slice(0, 16) });
}
}}
showTimeSelect
showTimeSelectOnly
timeIntervals={15}
timeCaption="Heure"
dateFormat="HH:mm"
className="w-32 bg-white text-gray-900"
customInput={<Input className="bg-white text-gray-900" />}
/>
)}
</div>
</div>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="allDay"
checked={meetingForm.allDay}
onCheckedChange={(checked) =>
setMeetingForm({ ...meetingForm, allDay: checked as boolean })
}
/>
<Label htmlFor="allDay" className="text-gray-800">Toute la journée</Label>
</div>
<div className="space-y-2">
<Label className="text-gray-800">Lieu</Label>
<Input <Input
id="meeting-date" value={meetingForm.location}
type="date" onChange={(e) =>
value={meetingForm.date} setMeetingForm({ ...meetingForm, location: e.target.value })
onChange={(e) => setMeetingForm({ ...meetingForm, date: e.target.value })} }
className="mt-1 bg-black text-white border-gray-700" placeholder="Ajouter un lieu"
required className="bg-white text-gray-900"
/> />
</div> </div>
<div>
<Label htmlFor="meeting-time">Heure *</Label> <div className="space-y-2">
<Input <Label className="text-gray-800">Description</Label>
id="meeting-time" <Textarea
type="time" value={meetingForm.description}
value={meetingForm.time} onChange={(e) =>
onChange={(e) => setMeetingForm({ ...meetingForm, time: e.target.value })} setMeetingForm({ ...meetingForm, description: e.target.value })
className="mt-1 bg-black text-white border-gray-700" }
required placeholder="Ajouter une description"
className="bg-white text-gray-900"
rows={3}
/> />
</div> </div>
<div className="space-y-2">
<Label className="text-gray-800">Récurrence</Label>
<Select
value={meetingForm.recurrence}
onValueChange={(value: "none" | "daily" | "weekly" | "monthly") =>
setMeetingForm({ ...meetingForm, recurrence: value })
}
>
<SelectTrigger className="w-full bg-white text-gray-900">
<SelectValue placeholder="Sélectionner une récurrence" />
</SelectTrigger>
<SelectContent>
<SelectItem value="none">Aucune récurrence</SelectItem>
<SelectItem value="daily">Tous les jours</SelectItem>
<SelectItem value="weekly">Toutes les semaines</SelectItem>
<SelectItem value="monthly">Tous les mois</SelectItem>
</SelectContent>
</Select>
</div>
<div className="bg-gray-50 p-3 rounded-lg"> <div className="bg-gray-50 p-3 rounded-lg">
<p className="text-sm text-gray-600"> <p className="text-sm text-gray-600">
<strong>{meetingForm.type === "group" ? "Groupe" : "Mission"}:</strong> {meetingForm.entityName} <strong>{meetingForm.type === "group" ? "Groupe" : "Mission"}:</strong> {meetingForm.entityName}
@ -751,18 +972,23 @@ export default function VisionPage() {
</div> </div>
<DialogFooter> <DialogFooter>
<Button <Button
variant="outline"
onClick={() => { onClick={() => {
setShowMeetingDialog(false); setShowMeetingDialog(false);
setMeetingForm({ setMeetingForm({
type: "", type: "",
entityId: "", entityId: "",
entityName: "", entityName: "",
date: "",
time: "",
title: "", title: "",
start: "",
end: "",
allDay: false,
location: "",
description: "",
recurrence: "none",
}); });
}} }}
className="bg-black text-white hover:bg-gray-900 border border-black" className="bg-white hover:bg-gray-50 text-gray-900 border-gray-200"
> >
Annuler Annuler
</Button> </Button>