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 { Label } from "@/components/ui/label";
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 {
id: string;
@ -56,16 +70,24 @@ export default function VisionPage() {
type: "group" | "mission" | "";
entityId: string;
entityName: string;
date: string;
time: string;
title: string;
start: string;
end: string;
allDay: boolean;
location: string;
description: string;
recurrence: "none" | "daily" | "weekly" | "monthly";
}>({
type: "",
entityId: "",
entityName: "",
date: "",
time: "",
title: "",
start: "",
end: "",
allDay: false,
location: "",
description: "",
recurrence: "none",
});
// Redirect if not authenticated
@ -186,22 +208,42 @@ export default function VisionPage() {
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
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({
type,
entityId: id,
entityName: name,
date: selectedDate ? selectedDate.toISOString().split('T')[0] : new Date().toISOString().split('T')[0],
time: "",
title: "",
start: startDate.toISOString().slice(0, 16),
end: endDate.toISOString().slice(0, 16),
allDay: false,
location: "",
description: "",
recurrence: "none",
});
setShowMeetingDialog(true);
};
// Handle save meeting
const handleSaveMeeting = () => {
if (!meetingForm.type || !meetingForm.entityId || !meetingForm.date || !meetingForm.time) {
if (!meetingForm.type || !meetingForm.entityId || !meetingForm.start || !meetingForm.end) {
toast({
title: "Erreur",
description: "Veuillez remplir tous les champs requis",
@ -210,29 +252,74 @@ export default function VisionPage() {
return;
}
const newMeeting: ScheduledMeeting = {
id: `${Date.now()}-${Math.random()}`,
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}`,
};
const startDate = new Date(meetingForm.start);
const endDate = new Date(meetingForm.end);
// 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, newMeeting]);
setScheduledMeetings([...scheduledMeetings, ...meetings]);
setShowMeetingDialog(false);
setMeetingForm({
type: "",
entityId: "",
entityName: "",
date: "",
time: "",
title: "",
start: "",
end: "",
allDay: false,
location: "",
description: "",
recurrence: "none",
});
toast({
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 */}
<Dialog open={showMeetingDialog} onOpenChange={setShowMeetingDialog}>
<DialogContent className="sm:max-w-[500px]">
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle>Planifier une réunion</DialogTitle>
<DialogTitle className="text-gray-800">Planifier une réunion</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<div>
<Label htmlFor="meeting-title">Titre (optionnel)</Label>
<div className="grid gap-2">
<Label htmlFor="meeting-title" className="text-base font-semibold text-gray-800">Titre</Label>
<Input
id="meeting-title"
value={meetingForm.title}
onChange={(e) => setMeetingForm({ ...meetingForm, title: e.target.value })}
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>
<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
id="meeting-date"
type="date"
value={meetingForm.date}
onChange={(e) => setMeetingForm({ ...meetingForm, date: e.target.value })}
className="mt-1 bg-black text-white border-gray-700"
required
value={meetingForm.location}
onChange={(e) =>
setMeetingForm({ ...meetingForm, location: e.target.value })
}
placeholder="Ajouter un lieu"
className="bg-white text-gray-900"
/>
</div>
<div>
<Label htmlFor="meeting-time">Heure *</Label>
<Input
id="meeting-time"
type="time"
value={meetingForm.time}
onChange={(e) => setMeetingForm({ ...meetingForm, time: e.target.value })}
className="mt-1 bg-black text-white border-gray-700"
required
<div className="space-y-2">
<Label className="text-gray-800">Description</Label>
<Textarea
value={meetingForm.description}
onChange={(e) =>
setMeetingForm({ ...meetingForm, description: e.target.value })
}
placeholder="Ajouter une description"
className="bg-white text-gray-900"
rows={3}
/>
</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">
<p className="text-sm text-gray-600">
<strong>{meetingForm.type === "group" ? "Groupe" : "Mission"}:</strong> {meetingForm.entityName}
@ -751,18 +972,23 @@ export default function VisionPage() {
</div>
<DialogFooter>
<Button
variant="outline"
onClick={() => {
setShowMeetingDialog(false);
setMeetingForm({
type: "",
entityId: "",
entityName: "",
date: "",
time: "",
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
</Button>