vision refactor

This commit is contained in:
alma 2026-01-15 21:00:00 +01:00
parent 607f762a40
commit b185c130c9

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect } from "react"; import { useState, useEffect, useCallback } from "react";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { ResponsiveIframe } from "@/app/components/responsive-iframe"; import { ResponsiveIframe } from "@/app/components/responsive-iframe";
@ -197,39 +197,70 @@ export default function VisionPage() {
return meetings; return meetings;
}; };
// Load meetings from database (events from group and mission calendars) // Function to fetch and refresh meetings from database
useEffect(() => { const fetchMeetings = useCallback(async (showLoading = true) => {
const fetchMeetings = async () => { if (status !== "authenticated" || !session?.user?.id) {
if (status !== "authenticated" || !session?.user?.id) { return;
return; }
}
try { try {
if (showLoading) {
setMeetingsLoading(true); setMeetingsLoading(true);
// Fetch calendars with events }
const calendarsResponse = await fetch("/api/calendars"); // Fetch calendars with events, use refresh=true to bypass cache
if (!calendarsResponse.ok) { const calendarsResponse = await fetch("/api/calendars?refresh=true");
throw new Error("Impossible de charger les calendriers"); if (!calendarsResponse.ok) {
} throw new Error("Impossible de charger les calendriers");
const calendarsData = await calendarsResponse.json(); }
const calendarsData = await calendarsResponse.json();
const meetings = convertCalendarsToMeetings(calendarsData); const meetings = convertCalendarsToMeetings(calendarsData);
setScheduledMeetings(meetings);
} catch (error) { // Debug log
console.error("Error loading meetings:", error); console.log('[Vision] Loaded meetings:', {
toast({ totalMeetings: meetings.length,
title: "Erreur", todayMeetings: meetings.filter(m => m.date === formatDateLocal(new Date())).length,
description: "Impossible de charger les réunions", meetings: meetings.map(m => ({
variant: "destructive", id: m.id,
}); title: m.title,
} finally { date: m.date,
time: m.time,
type: m.type,
entityId: m.entityId,
entityName: m.entityName,
start: m.start,
end: m.end,
location: m.location
}))
});
setScheduledMeetings(meetings);
} catch (error) {
console.error("Error loading meetings:", error);
toast({
title: "Erreur",
description: "Impossible de charger les réunions",
variant: "destructive",
});
} finally {
if (showLoading) {
setMeetingsLoading(false); setMeetingsLoading(false);
} }
}; }
fetchMeetings();
}, [session, status, groups, missions, toast]); }, [session, status, groups, missions, toast]);
// Load meetings from database (events from group and mission calendars)
useEffect(() => {
fetchMeetings();
// Refresh meetings every minute to update "Rejoindre" button status
const interval = setInterval(() => {
fetchMeetings(false); // Don't show loading spinner on auto-refresh
}, 60000); // Every minute
return () => clearInterval(interval);
}, [fetchMeetings]);
// Fetch user groups and missions // Fetch user groups and missions
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
@ -321,10 +352,25 @@ export default function VisionPage() {
// Check if meeting can be joined (5 minutes before start until end) // Check if meeting can be joined (5 minutes before start until end)
const canJoinMeeting = (meeting: ScheduledMeeting): boolean => { const canJoinMeeting = (meeting: ScheduledMeeting): boolean => {
if (!meeting.start || !meeting.end) {
console.warn('[Vision] canJoinMeeting: missing start or end', meeting);
return false;
}
const now = new Date(); const now = new Date();
const start = new Date(meeting.start); const start = new Date(meeting.start);
const end = new Date(meeting.end); const end = new Date(meeting.end);
// Validate dates
if (isNaN(start.getTime()) || isNaN(end.getTime())) {
console.warn('[Vision] canJoinMeeting: invalid dates', {
meetingId: meeting.id,
start: meeting.start,
end: meeting.end
});
return false;
}
// Calculate 5 minutes before start // Calculate 5 minutes before start
const fiveMinutesBeforeStart = new Date(start); const fiveMinutesBeforeStart = new Date(start);
fiveMinutesBeforeStart.setMinutes(fiveMinutesBeforeStart.getMinutes() - 5); fiveMinutesBeforeStart.setMinutes(fiveMinutesBeforeStart.getMinutes() - 5);
@ -332,18 +378,27 @@ export default function VisionPage() {
// Can join if now is between 5 minutes before start and end // Can join if now is between 5 minutes before start and end
const canJoin = now >= fiveMinutesBeforeStart && now <= end; const canJoin = now >= fiveMinutesBeforeStart && now <= end;
// Debug log // Debug log for today's meetings
if (meeting.date === formatDateLocal(now)) { const today = formatDateLocal(now);
if (meeting.date === today) {
console.log('[Vision] canJoinMeeting check:', { console.log('[Vision] canJoinMeeting check:', {
meetingId: meeting.id, meetingId: meeting.id,
title: meeting.title, title: meeting.title,
date: meeting.date,
time: meeting.time,
now: now.toISOString(), now: now.toISOString(),
nowLocal: now.toString(),
start: start.toISOString(), start: start.toISOString(),
startLocal: start.toString(),
end: end.toISOString(), end: end.toISOString(),
endLocal: end.toString(),
fiveMinutesBeforeStart: fiveMinutesBeforeStart.toISOString(), fiveMinutesBeforeStart: fiveMinutesBeforeStart.toISOString(),
fiveMinutesBeforeStartLocal: fiveMinutesBeforeStart.toString(),
canJoin, canJoin,
nowVsFiveMinBefore: now >= fiveMinutesBeforeStart, nowVsFiveMinBefore: now >= fiveMinutesBeforeStart,
nowVsEnd: now <= end nowVsEnd: now <= end,
timeDiff: (now.getTime() - fiveMinutesBeforeStart.getTime()) / 1000 / 60, // minutes
location: meeting.location
}); });
} }
@ -589,30 +644,7 @@ export default function VisionPage() {
await Promise.all(eventPromises); await Promise.all(eventPromises);
// Refresh meetings from database after creation // Refresh meetings from database after creation
// Use refresh=true to bypass cache and ensure immediate update await fetchMeetings(false); // Don't show loading spinner
const refreshCalendarsResponse = await fetch("/api/calendars?refresh=true");
if (refreshCalendarsResponse.ok) {
const calendarsData = await refreshCalendarsResponse.json();
const meetings = convertCalendarsToMeetings(calendarsData);
// Debug log
console.log('[Vision] Refreshed meetings after creation:', {
totalMeetings: meetings.length,
meetings: meetings.map(m => ({
id: m.id,
title: m.title,
date: m.date,
time: m.time,
type: m.type,
entityId: m.entityId,
entityName: m.entityName,
start: m.start,
end: m.end
}))
});
setScheduledMeetings(meetings);
}
setShowMeetingDialog(false); setShowMeetingDialog(false);
setMeetingForm({ setMeetingForm({
type: "", type: "",
@ -652,13 +684,7 @@ export default function VisionPage() {
} }
// Refresh meetings from database after deletion // Refresh meetings from database after deletion
// Use refresh=true to bypass cache and ensure immediate update await fetchMeetings(false); // Don't show loading spinner
const refreshCalendarsResponse = await fetch("/api/calendars?refresh=true");
if (refreshCalendarsResponse.ok) {
const calendarsData = await refreshCalendarsResponse.json();
const meetings = convertCalendarsToMeetings(calendarsData);
setScheduledMeetings(meetings);
}
toast({ toast({
title: "Succès", title: "Succès",
@ -773,7 +799,7 @@ export default function VisionPage() {
</div> </div>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{canJoinMeeting(meeting) && ( {canJoinMeeting(meeting) && meeting.location ? (
<Button <Button
size="sm" size="sm"
className="bg-blue-600 hover:bg-blue-700 text-white" className="bg-blue-600 hover:bg-blue-700 text-white"
@ -782,6 +808,10 @@ export default function VisionPage() {
<Video className="h-4 w-4 mr-1" /> <Video className="h-4 w-4 mr-1" />
Rejoindre Rejoindre
</Button> </Button>
) : (
<span className="text-xs text-gray-400">
{!meeting.location ? 'Pas de lien' : 'Bientôt disponible'}
</span>
)} )}
</div> </div>
</div> </div>
@ -988,8 +1018,7 @@ export default function VisionPage() {
{groups.map((group) => ( {groups.map((group) => (
<div <div
key={group.id} key={group.id}
className="bg-white rounded-lg border border-gray-200 p-4 hover:shadow-md transition-shadow cursor-pointer" className="bg-white rounded-lg border border-gray-200 p-4 hover:shadow-md transition-shadow"
onClick={() => handleConferenceClick("group", group.id, group.name)}
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-3 flex-1"> <div className="flex items-center gap-3 flex-1">
@ -1058,8 +1087,7 @@ export default function VisionPage() {
{missions.map((mission) => ( {missions.map((mission) => (
<div <div
key={mission.id} key={mission.id}
className="bg-white rounded-lg border border-gray-200 p-4 hover:shadow-md transition-shadow cursor-pointer" className="bg-white rounded-lg border border-gray-200 p-4 hover:shadow-md transition-shadow"
onClick={() => handleConferenceClick("mission", mission.id, mission.name)}
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-3 flex-1"> <div className="flex items-center gap-3 flex-1">