diff --git a/src/App.tsx b/src/App.tsx index 7c985ad..b7000e1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -195,6 +195,13 @@ export default function App() { return () => evtSource.close(); }, [currentUser]); + // Keep the booking details modal in sync when SSE updates the bookings list. + useEffect(() => { + if (!selectedBookingForDetails) return; + const fresh = bookings.find(b => b.id === selectedBookingForDetails.id); + setSelectedBookingForDetails(fresh ?? null); + }, [bookings]); + // Upcoming-booking reminder - checks every 60s, fires once per booking useEffect(() => { if (!currentUser || bookings.length === 0) return; @@ -242,10 +249,7 @@ export default function App() { }); if (res.ok) { const data = await res.json(); - setBookings(prev => [data.booking, ...prev]); if (data.alertGenerated) setNotifications(prev => [data.alertGenerated, ...prev]); - const logsRes = await authFetch('/api/logs'); - if (logsRes.ok) setLogs(await logsRes.json()); } } catch (err) { console.error('[App] Error adding booking:', err); } }; @@ -258,111 +262,62 @@ export default function App() { body: JSON.stringify({ status: 'cancelled', operatorName: currentUser!.name }), }); if (res.ok) { - const updated = await res.json(); - setBookings(prev => prev.map(b => b.id === bookingId ? updated : b)); - if (selectedBookingForDetails?.id === bookingId) setSelectedBookingForDetails(updated); const labName = booking ? (labs.find(l => l.id === booking.labId)?.name ?? 'Lab') : 'Lab'; setNotifications(prev => [`Booking cancelled: "${labName}" - slot has been released.`, ...prev]); - const logsRes = await authFetch('/api/logs'); - if (logsRes.ok) setLogs(await logsRes.json()); } } catch (err) { console.error('[App] Error cancelling booking:', err); } }; const handleDeleteBooking = async (bookingId: string) => { try { - const res = await authFetch(`/api/bookings/${bookingId}`, { method: 'DELETE' }); - if (res.ok) { - setBookings(prev => prev.filter(b => b.id !== bookingId)); - if (selectedBookingForDetails?.id === bookingId) setSelectedBookingForDetails(null); - const logsRes = await authFetch('/api/logs'); - if (logsRes.ok) setLogs(await logsRes.json()); - } + await authFetch(`/api/bookings/${bookingId}`, { method: 'DELETE' }); } catch (err) { console.error('[App] Error deleting booking:', err); } }; // Device handlers const handleAddDevice = async (newDev: Omit) => { try { - const res = await authFetch('/api/devices', { method: 'POST', body: JSON.stringify(newDev) }); - if (res.ok) { - const created = await res.json(); - setDevices(prev => [...prev, created]); - const logsRes = await authFetch('/api/logs'); - if (logsRes.ok) setLogs(await logsRes.json()); - } + await authFetch('/api/devices', { method: 'POST', body: JSON.stringify(newDev) }); } catch (err) { console.error('[App] Error adding device:', err); } }; const handleUpdateDevice = async (updatedDev: Device) => { try { - const res = await authFetch(`/api/devices/${updatedDev.id}`, { + await authFetch(`/api/devices/${updatedDev.id}`, { method: 'PUT', body: JSON.stringify({ ...updatedDev, operatorName: currentUser!.name }), }); - if (res.ok) { - const updated = await res.json(); - setDevices(prev => prev.map(d => d.id === updatedDev.id ? updated : d)); - const logsRes = await authFetch('/api/logs'); - if (logsRes.ok) setLogs(await logsRes.json()); - } } catch (err) { console.error('[App] Error updating device:', err); } }; const handleDeleteDevice = async (id: string) => { try { - const res = await authFetch(`/api/devices/${id}`, { method: 'DELETE' }); - if (res.ok) { - setDevices(prev => prev.filter(d => d.id !== id)); - const labsRes = await authFetch('/api/labs'); - if (labsRes.ok) setLabs(await labsRes.json()); - const logsRes = await authFetch('/api/logs'); - if (logsRes.ok) setLogs(await logsRes.json()); - } + await authFetch(`/api/devices/${id}`, { method: 'DELETE' }); } catch (err) { console.error('[App] Error deleting device:', err); } }; // Lab handlers const handleAddLab = async (newLab: Omit) => { try { - const res = await authFetch('/api/labs', { method: 'POST', body: JSON.stringify(newLab) }); - if (res.ok) { - const created = await res.json(); - setLabs(prev => [...prev, created]); - const logsRes = await authFetch('/api/logs'); - if (logsRes.ok) setLogs(await logsRes.json()); - } + await authFetch('/api/labs', { method: 'POST', body: JSON.stringify(newLab) }); } catch (err) { console.error('[App] Error adding lab:', err); } }; const handleUpdateLab = async (updatedLab: LabTemplate) => { try { - const res = await authFetch(`/api/labs/${updatedLab.id}`, { method: 'PUT', body: JSON.stringify(updatedLab) }); - if (res.ok) { - const data = await res.json(); - setLabs(prev => prev.map(l => l.id === updatedLab.id ? data : l)); - const logsRes = await authFetch('/api/logs'); - if (logsRes.ok) setLogs(await logsRes.json()); - } + await authFetch(`/api/labs/${updatedLab.id}`, { method: 'PUT', body: JSON.stringify(updatedLab) }); } catch (err) { console.error('[App] Error updating lab:', err); } }; const handleDeleteLab = async (id: string) => { try { - const res = await authFetch(`/api/labs/${id}`, { method: 'DELETE' }); - if (res.ok) { - setLabs(prev => prev.filter(l => l.id !== id)); - setBookings(prev => prev.map(b => b.labId === id && b.status === 'upcoming' ? { ...b, status: 'cancelled' as const } : b)); - const logsRes = await authFetch('/api/logs'); - if (logsRes.ok) setLogs(await logsRes.json()); - } + await authFetch(`/api/labs/${id}`, { method: 'DELETE' }); } catch (err) { console.error('[App] Error deleting lab:', err); } }; const handleAddLogManually = async (newLogEntry: Omit) => { try { - const res = await authFetch('/api/logs', { method: 'POST', body: JSON.stringify(newLogEntry) }); - if (res.ok) { const log = await res.json(); setLogs(prev => [log, ...prev]); } + await authFetch('/api/logs', { method: 'POST', body: JSON.stringify(newLogEntry) }); } catch (err) { console.error('[App] Error adding log:', err); } }; @@ -370,18 +325,14 @@ export default function App() { const handleDeleteUser = async (id: string) => { try { const res = await authFetch(`/api/users/${id}`, { method: 'DELETE' }); - if (res.ok) setUsers(prev => prev.filter(u => u.id !== id)); - else { const d = await res.json(); throw new Error(d.error); } + if (!res.ok) { const d = await res.json(); throw new Error(d.error); } } catch (err: any) { throw err; } }; const handleUpdateUser = async (id: string, name: string, email: string) => { try { const res = await authFetch(`/api/users/${id}`, { method: 'PUT', body: JSON.stringify({ name, email }) }); - if (res.ok) { - const updated = await res.json(); - setUsers(prev => prev.map(u => u.id === id ? updated : u)); - } else { const d = await res.json(); throw new Error(d.error); } + if (!res.ok) { const d = await res.json(); throw new Error(d.error); } } catch (err: any) { throw err; } }; @@ -390,7 +341,8 @@ export default function App() { const res = await authFetch(`/api/users/${id}/role`, { method: 'PATCH', body: JSON.stringify({ role }) }); if (res.ok) { const updated: User = await res.json(); - setUsers(prev => prev.map(u => u.id === id ? updated : u)); + // Update currentUser immediately if the acting user changed their own role, + // since currentUser is not driven by the users SSE event. if (updated.id === currentUser?.id) setCurrentUser(updated); } else { const d = await res.json(); throw new Error(d.error); } } catch (err: any) { throw err; } @@ -399,28 +351,19 @@ export default function App() { // Quick-link handlers (shared link dashboard) const handleAddLink = async (newLink: Omit) => { try { - const res = await authFetch('/api/links', { method: 'POST', body: JSON.stringify(newLink) }); - if (res.ok) { - const created = await res.json(); - setLinks(prev => [...prev, created]); - } + await authFetch('/api/links', { method: 'POST', body: JSON.stringify(newLink) }); } catch (err) { console.error('[App] Error adding link:', err); } }; const handleUpdateLink = async (updated: QuickLink) => { try { - const res = await authFetch(`/api/links/${updated.id}`, { method: 'PUT', body: JSON.stringify(updated) }); - if (res.ok) { - const data = await res.json(); - setLinks(prev => prev.map(l => l.id === updated.id ? data : l)); - } + await authFetch(`/api/links/${updated.id}`, { method: 'PUT', body: JSON.stringify(updated) }); } catch (err) { console.error('[App] Error updating link:', err); } }; const handleDeleteLink = async (id: string) => { try { - const res = await authFetch(`/api/links/${id}`, { method: 'DELETE' }); - if (res.ok) setLinks(prev => prev.filter(l => l.id !== id)); + await authFetch(`/api/links/${id}`, { method: 'DELETE' }); } catch (err) { console.error('[App] Error deleting link:', err); } };