diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index dc72e1e..66c77d5 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,11 +4,13 @@
-
-
-
-
-
+
+
+
+
+
+
+
@@ -130,7 +132,7 @@
1774643312599
-
+
@@ -212,7 +214,15 @@
1774710987605
-
+
+
+ 1774711356754
+
+
+
+ 1774711356754
+
+
@@ -230,6 +240,7 @@
-
+
+
\ No newline at end of file
diff --git a/apps/api/src/events/events.gateway.ts b/apps/api/src/events/events.gateway.ts
index 832db1e..80b6a5c 100644
--- a/apps/api/src/events/events.gateway.ts
+++ b/apps/api/src/events/events.gateway.ts
@@ -27,7 +27,7 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
@SubscribeMessage('join_floor')
handleJoinFloor(client: Socket, floorId: string) {
client.join(floorId);
- console.log(`Client ${client.id} joined floor: ${floorId}`);
+ console.log(`Client ${client.id} joined floor: ${floorId}. Rooms:`, Array.from(client.rooms));
}
@SubscribeMessage('leave_floor')
@@ -37,6 +37,7 @@ export class EventsGateway implements OnGatewayConnection, OnGatewayDisconnect {
}
emitSpaceStatusChanged(floorId: string, spaceId: string, isOccupied: boolean, reservationData?: any) {
+ console.log(`Emitting space_status_changed to floor ${floorId}:`, { spaceId, isOccupied });
this.server.to(floorId).emit('space_status_changed', {
spaceId,
isOccupied,
diff --git a/apps/api/src/reservations/reservations.service.ts b/apps/api/src/reservations/reservations.service.ts
index 10aa69d..e8b8aa1 100644
--- a/apps/api/src/reservations/reservations.service.ts
+++ b/apps/api/src/reservations/reservations.service.ts
@@ -90,6 +90,8 @@ export class ReservationsService {
}
});
+ console.log('Emitted space_status_changed for new reservation', space.floorId, spaceId);
+
return reservation;
}
@@ -134,6 +136,8 @@ export class ReservationsService {
false,
);
+ console.log('Emitted space_status_changed for cancelled reservation', reservation.space.floorId, reservation.spaceId);
+
return { message: 'Reservation cancelled' };
}
diff --git a/apps/web/src/lib/components/BookingModal.svelte b/apps/web/src/lib/components/BookingModal.svelte
index bbc59f8..1c153a4 100644
--- a/apps/web/src/lib/components/BookingModal.svelte
+++ b/apps/web/src/lib/components/BookingModal.svelte
@@ -91,6 +91,7 @@
})
});
+ console.log('Booking created successfully');
dispatch('close');
} catch (e: any) {
error = e.message || "Failed to create booking";
diff --git a/apps/web/src/lib/components/FloorplanViewer.svelte b/apps/web/src/lib/components/FloorplanViewer.svelte
index d1fde61..1aff964 100644
--- a/apps/web/src/lib/components/FloorplanViewer.svelte
+++ b/apps/web/src/lib/components/FloorplanViewer.svelte
@@ -16,11 +16,18 @@
let containerWidth = 800;
let containerHeight = 600;
+ // Track if we have received any real-time updates to avoid overwriting them with stale initial status
+ let realtimeUpdates: Record = {};
+
$: {
- // Initialize space status from the current state
+ // Initialize space status from the current state (e.g. from API)
spaces.forEach(s => {
- // For desks, we check if there's any reservation for the selected date
- // For meeting rooms, we check if there's a reservation overlapping with the current/selected time
+ // Don't overwrite if we already have a real-time update for today
+ if (isToday && realtimeUpdates[s.id]) {
+ spaceStatus[s.id] = realtimeUpdates[s.id];
+ return;
+ }
+
const activeReservation = s.reservations && s.reservations.length > 0 ? s.reservations[0] : null;
spaceStatus[s.id] = {
isOccupied: !!activeReservation,
@@ -74,23 +81,37 @@
containerHeight += 40;
}
- // Disable real-time updates if viewing a different date than today
- $: isToday = new Date().toDateString() === selectedDate.toDateString();
+ $: isToday = new Date(selectedDate.getTime()).setHours(0,0,0,0) === new Date().setHours(0,0,0,0);
onMount(() => {
const socketUrl = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:3000';
- socket = io(socketUrl);
+ socket = io(socketUrl, {
+ transports: ['websocket'], // Prefer WebSockets
+ });
socket.on('connect', () => {
+ console.log('Connected to WebSocket for floor:', floorId);
socket.emit('join_floor', floorId);
});
socket.on('space_status_changed', (data: { spaceId: string, isOccupied: boolean, reservation?: any }) => {
- if (!isToday) return; // Ignore real-time updates for historical/future views
- spaceStatus[data.spaceId] = {
+ console.log('Space status changed received:', data);
+ if (!isToday) {
+ console.log('Ignoring real-time update: not today', {
+ selectedDate: selectedDate.toDateString(),
+ now: new Date().toDateString()
+ });
+ return;
+ }
+
+ const update = {
isOccupied: data.isOccupied,
reservation: data.reservation
};
+
+ // Save to realtimeUpdates to prevent overwriting by API re-fetches
+ realtimeUpdates[data.spaceId] = update;
+ spaceStatus[data.spaceId] = update;
spaceStatus = { ...spaceStatus }; // Trigger reactivity
});
});
diff --git a/apps/web/src/lib/components/SpaceCard.svelte b/apps/web/src/lib/components/SpaceCard.svelte
index 310aff4..a8d2044 100644
--- a/apps/web/src/lib/components/SpaceCard.svelte
+++ b/apps/web/src/lib/components/SpaceCard.svelte
@@ -12,24 +12,35 @@
let isOccupied = initialStatus;
let socket: Socket;
- $: isToday = new Date().toDateString() === selectedDate.toDateString();
+ // Track if we have received any real-time updates for this space
+ let realtimeOccupied: boolean | null = null;
+
+ $: isToday = new Date(selectedDate.getTime()).setHours(0,0,0,0) === new Date().setHours(0,0,0,0);
$: if (initialStatus !== undefined) {
- isOccupied = initialStatus;
+ if (isToday && realtimeOccupied !== null) {
+ isOccupied = realtimeOccupied;
+ } else {
+ isOccupied = initialStatus;
+ }
}
onMount(() => {
// Connect to WebSocket using the environment variable for the backend URL.
const socketUrl = import.meta.env.VITE_API_BASE_URL ?? 'http://localhost:3000';
- socket = io(socketUrl);
+ socket = io(socketUrl, {
+ transports: ['websocket'],
+ });
socket.on('connect', () => {
- console.log('Connected to WebSocket');
+ console.log('Connected to WebSocket (SpaceCard)');
socket.emit('join_floor', floorId);
});
socket.on('space_status_changed', (data: { spaceId: string, isOccupied: boolean }) => {
if (data.spaceId === spaceId && isToday) {
+ console.log('Space status updated for:', spaceId, data.isOccupied);
+ realtimeOccupied = data.isOccupied;
isOccupied = data.isOccupied;
}
});
diff --git a/apps/web/src/routes/dashboard/floor/[id]/+page.svelte b/apps/web/src/routes/dashboard/floor/[id]/+page.svelte
index 5c0f7ec..2deb354 100644
--- a/apps/web/src/routes/dashboard/floor/[id]/+page.svelte
+++ b/apps/web/src/routes/dashboard/floor/[id]/+page.svelte
@@ -10,31 +10,33 @@
let floor: any = null;
let loading = true;
- let selectedDateString = new Date().toISOString().split('T')[0];
+ let selectedDateString = new Date().toLocaleDateString('en-CA'); // YYYY-MM-DD in local time
let selectedDate = new Date();
$: {
- selectedDate = new Date(selectedDateString);
- if (selectedDateString === new Date().toISOString().split('T')[0]) {
+ selectedDate = new Date(selectedDateString + 'T00:00:00'); // Initialize to start of day in local time
+ if (selectedDateString === new Date().toLocaleDateString('en-CA')) {
selectedDate = new Date();
} else {
selectedDate.setHours(7, 0, 0, 0);
}
}
+ let floorIdFromUrl = $page.params.id;
+ $: floorIdFromUrl = $page.params.id;
+
onMount(async () => {
- const floorId = $page.params.id;
if (!$token) {
- goto(`/login?redirect=/dashboard/floor/${floorId}`);
+ goto(`/login?redirect=/dashboard/floor/${floorIdFromUrl}`);
return;
}
- await loadFloor(floorId);
+ await loadFloor(floorIdFromUrl, selectedDate);
loading = false;
});
- async function loadFloor(floorId: string) {
+ async function loadFloor(id: string, date: Date) {
try {
- floor = await apiFetch(`/spaces/floor/${floorId}?date=${selectedDate.toISOString()}`);
+ floor = await apiFetch(`/spaces/floor/${id}?date=${date.toISOString()}`);
} catch (e: any) {
console.error(e);
if (e.message?.includes('401')) {
@@ -43,8 +45,8 @@
}
}
- $: if (floor && floor.id && selectedDate) {
- loadFloor(floor.id);
+ $: if (floorIdFromUrl && selectedDate) {
+ loadFloor(floorIdFromUrl, selectedDate);
}