Improve real-time space status handling and date-based resets
- Add logic to clear real-time updates when the selected date changes. - Enhance space status updates to consider reservation time ranges for relevance. - Update relevant Svelte components (`SpaceCard`, `FloorplanViewer`) to handle date-sensitive real-time updates. - Update API to include reservation details in `space_status_changed` events for improved update precision.main
parent
93bd2808ac
commit
fa93fc0ac6
|
|
@ -4,10 +4,8 @@
|
|||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="6c8b3c54-eabb-40e5-967f-c7b594c750bc" name="Changes" comment="Add i18n integration and translations for improved localization - Extend English (`en.json`) and Spanish (`es-ES.json`) locale files with additional keys for buildings, bookings, reports, and user management. - Update Svelte components and pages to use `svelte-i18n` for dynamic translations across the app. - Refactor labels, titles, and error messages for multilingual support. - Improve date formatting based on locale preferences.">
|
||||
<change beforePath="$PROJECT_DIR$/apps/api/prisma/seed.ts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/api/prisma/seed.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/api/src/prisma/prisma.module.ts" beforeDir="false" afterPath="$PROJECT_DIR$/apps/api/src/prisma/prisma.module.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/apps/web/src/routes/login/+page.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/routes/login/+page.svelte" afterDir="false" />
|
||||
<list default="true" id="6c8b3c54-eabb-40e5-967f-c7b594c750bc" name="Changes" comment="Add seed service to initialize default admin and organization - Implement `SeedService` to create a default admin account and organization on app startup. - Update `PrismaModule` to include `SeedService` as a provider. - Remove seeded credentials display from login page. - Refactor existing seed script for consistency with the new service.">
|
||||
<change beforePath="$PROJECT_DIR$/apps/web/src/lib/components/FloorplanViewer.svelte" beforeDir="false" afterPath="$PROJECT_DIR$/apps/web/src/lib/components/FloorplanViewer.svelte" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
|
|
@ -129,7 +127,7 @@
|
|||
<updated>1774643312599</updated>
|
||||
<workItem from="1774643313611" duration="74000" />
|
||||
<workItem from="1774643422240" duration="5278000" />
|
||||
<workItem from="1774706361502" duration="11023000" />
|
||||
<workItem from="1774706361502" duration="11346000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="Initialize Hot Desking app with web and API services - Set up SvelteKit for the web interface with Tailwind CSS. - Build the API using NestJS with Prisma ORM for database interaction. - Add environment variable management and docker-compose for PostgreSQL and PgAdmin instances. - Create shared data models for consistent typing across services. - Establish basic routing and layouts for web and API services.">
|
||||
<option name="closed" value="true" />
|
||||
|
|
@ -299,7 +297,15 @@
|
|||
<option name="project" value="LOCAL" />
|
||||
<updated>1774717104130</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="22" />
|
||||
<task id="LOCAL-00022" summary="Add seed service to initialize default admin and organization - Implement `SeedService` to create a default admin account and organization on app startup. - Update `PrismaModule` to include `SeedService` as a provider. - Remove seeded credentials display from login page. - Refactor existing seed script for consistency with the new service.">
|
||||
<option name="closed" value="true" />
|
||||
<created>1774717642991</created>
|
||||
<option name="number" value="00022" />
|
||||
<option name="presentableId" value="LOCAL-00022" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1774717642991</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="23" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
|
|
@ -329,6 +335,7 @@
|
|||
<MESSAGE value="Add Docker deployment support for API and Web services - Add Dockerfiles for API and Web apps with multi-stage builds. - Configure `docker-compose.yml` to manage services (Postgres, API, Web, pgAdmin). - Add `.dockerignore` for optimized builds. - Include `DOCKER_SETUP.md` for deployment steps and guidance." />
|
||||
<MESSAGE value="Add rotation support for spaces in floorplan editor - Extend `Space` model and Prisma schema to include `rotation` property. - Update API services and DTOs to handle rotation data. - Add UI controls in `FloorplanEditor.svelte` for rotating spaces. - Refactor space rendering logic in both editor and viewer to support rotation." />
|
||||
<MESSAGE value="Add i18n integration and translations for improved localization - Extend English (`en.json`) and Spanish (`es-ES.json`) locale files with additional keys for buildings, bookings, reports, and user management. - Update Svelte components and pages to use `svelte-i18n` for dynamic translations across the app. - Refactor labels, titles, and error messages for multilingual support. - Improve date formatting based on locale preferences." />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Add i18n integration and translations for improved localization - Extend English (`en.json`) and Spanish (`es-ES.json`) locale files with additional keys for buildings, bookings, reports, and user management. - Update Svelte components and pages to use `svelte-i18n` for dynamic translations across the app. - Refactor labels, titles, and error messages for multilingual support. - Improve date formatting based on locale preferences." />
|
||||
<MESSAGE value="Add seed service to initialize default admin and organization - Implement `SeedService` to create a default admin account and organization on app startup. - Update `PrismaModule` to include `SeedService` as a provider. - Remove seeded credentials display from login page. - Refactor existing seed script for consistency with the new service." />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Add seed service to initialize default admin and organization - Implement `SeedService` to create a default admin account and organization on app startup. - Update `PrismaModule` to include `SeedService` as a provider. - Remove seeded credentials display from login page. - Refactor existing seed script for consistency with the new service." />
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -137,6 +137,13 @@ export class ReservationsService {
|
|||
reservation.space.floorId,
|
||||
reservation.spaceId,
|
||||
false,
|
||||
{
|
||||
reservation: {
|
||||
id: reservation.id,
|
||||
startTime: reservation.startTime,
|
||||
endTime: reservation.endTime,
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
console.log('Emitted space_status_changed for cancelled reservation', reservation.space.floorId, reservation.spaceId);
|
||||
|
|
|
|||
|
|
@ -21,23 +21,35 @@
|
|||
|
||||
// Track if we have received any real-time updates to avoid overwriting them with stale initial status
|
||||
let realtimeUpdates: Record<string, any> = {};
|
||||
let lastSelectedDateString = selectedDate.toDateString();
|
||||
|
||||
$: isToday = new Date(selectedDate.getTime()).setHours(0,0,0,0) === new Date().setHours(0,0,0,0);
|
||||
|
||||
$: {
|
||||
// Initialize space status from the current state (e.g. from API)
|
||||
// Detect when the actual day changes to clear real-time updates
|
||||
const currentDateString = selectedDate.toDateString();
|
||||
if (currentDateString !== lastSelectedDateString) {
|
||||
realtimeUpdates = {};
|
||||
lastSelectedDateString = currentDateString;
|
||||
}
|
||||
|
||||
// Reset spaceStatus and ensure we re-calculate from new spaces prop
|
||||
let newStatus: Record<string, any> = {};
|
||||
|
||||
spaces.forEach(s => {
|
||||
// Don't overwrite if we already have a real-time update for today
|
||||
if (isToday && realtimeUpdates[s.id]) {
|
||||
spaceStatus[s.id] = realtimeUpdates[s.id];
|
||||
// Don't overwrite if we already have a real-time update for this view
|
||||
if (realtimeUpdates[s.id]) {
|
||||
newStatus[s.id] = realtimeUpdates[s.id];
|
||||
return;
|
||||
}
|
||||
|
||||
const activeReservation = s.reservations && s.reservations.length > 0 ? s.reservations[0] : null;
|
||||
spaceStatus[s.id] = {
|
||||
newStatus[s.id] = {
|
||||
isOccupied: !!activeReservation,
|
||||
reservation: activeReservation
|
||||
};
|
||||
});
|
||||
spaceStatus = { ...spaceStatus };
|
||||
spaceStatus = newStatus;
|
||||
updateContainerSizeToFitAll();
|
||||
}
|
||||
|
||||
|
|
@ -58,15 +70,6 @@
|
|||
if (b > maxB) maxB = b;
|
||||
});
|
||||
|
||||
// To be consistent with editor, we should probably use the same base.
|
||||
// However, in the viewer, we just want to show everything.
|
||||
// If spaces were saved with percentages relative to a larger container,
|
||||
// we need to know that container's aspect ratio.
|
||||
|
||||
// Actually, the percentages ARE the aspect ratio.
|
||||
// If a space is at x=110%, it means it's outside the "original" 800px.
|
||||
// So we should just find the max percentage and scale the container.
|
||||
|
||||
let maxXP = 100;
|
||||
let maxYP = 100;
|
||||
spaces.forEach(s => {
|
||||
|
|
@ -84,8 +87,6 @@
|
|||
containerHeight += 40;
|
||||
}
|
||||
|
||||
$: 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, {
|
||||
|
|
@ -99,10 +100,30 @@
|
|||
|
||||
socket.on('space_status_changed', (data: { spaceId: string, isOccupied: boolean, reservation?: any }) => {
|
||||
console.log('Space status changed received:', data);
|
||||
if (!isToday) {
|
||||
console.log('Ignoring real-time update: not today', {
|
||||
|
||||
let isRelevant = false;
|
||||
if (data.reservation) {
|
||||
const start = new Date(data.reservation.startTime);
|
||||
const end = new Date(data.reservation.endTime);
|
||||
const viewStart = new Date(selectedDate.getTime());
|
||||
viewStart.setHours(0, 0, 0, 0);
|
||||
const viewEnd = new Date(selectedDate.getTime());
|
||||
viewEnd.setHours(23, 59, 59, 999);
|
||||
|
||||
isRelevant = (start <= viewEnd && end >= viewStart);
|
||||
} else {
|
||||
// If we don't have reservation data (should not happen with updated API),
|
||||
// fallback to isToday check.
|
||||
isRelevant = isToday;
|
||||
}
|
||||
|
||||
if (!isRelevant) {
|
||||
console.log('Ignoring real-time update: not relevant to current view', {
|
||||
selectedDate: selectedDate.toDateString(),
|
||||
now: new Date().toDateString()
|
||||
reservation: data.reservation ? {
|
||||
start: new Date(data.reservation.startTime).toDateString(),
|
||||
end: new Date(data.reservation.endTime).toDateString()
|
||||
} : 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,11 +14,19 @@
|
|||
|
||||
// Track if we have received any real-time updates for this space
|
||||
let realtimeOccupied: boolean | null = null;
|
||||
let lastSelectedDateString = selectedDate.toDateString();
|
||||
|
||||
$: isToday = new Date(selectedDate.getTime()).setHours(0,0,0,0) === new Date().setHours(0,0,0,0);
|
||||
|
||||
$: if (initialStatus !== undefined) {
|
||||
if (isToday && realtimeOccupied !== null) {
|
||||
// Detect when the actual day changes to clear real-time updates
|
||||
const currentDateString = selectedDate.toDateString();
|
||||
if (currentDateString !== lastSelectedDateString) {
|
||||
realtimeOccupied = null;
|
||||
lastSelectedDateString = currentDateString;
|
||||
}
|
||||
|
||||
if (realtimeOccupied !== null) {
|
||||
isOccupied = realtimeOccupied;
|
||||
} else {
|
||||
isOccupied = initialStatus;
|
||||
|
|
@ -37,8 +45,24 @@
|
|||
socket.emit('join_floor', floorId);
|
||||
});
|
||||
|
||||
socket.on('space_status_changed', (data: { spaceId: string, isOccupied: boolean }) => {
|
||||
if (data.spaceId === spaceId && isToday) {
|
||||
socket.on('space_status_changed', (data: { spaceId: string, isOccupied: boolean, reservation?: any }) => {
|
||||
if (data.spaceId !== spaceId) return;
|
||||
|
||||
let isRelevant = false;
|
||||
if (data.reservation) {
|
||||
const start = new Date(data.reservation.startTime);
|
||||
const end = new Date(data.reservation.endTime);
|
||||
const viewStart = new Date(selectedDate.getTime());
|
||||
viewStart.setHours(0, 0, 0, 0);
|
||||
const viewEnd = new Date(selectedDate.getTime());
|
||||
viewEnd.setHours(23, 59, 59, 999);
|
||||
|
||||
isRelevant = (start <= viewEnd && end >= viewStart);
|
||||
} else {
|
||||
isRelevant = isToday;
|
||||
}
|
||||
|
||||
if (isRelevant) {
|
||||
console.log('Space status updated for:', spaceId, data.isOccupied);
|
||||
realtimeOccupied = data.isOccupied;
|
||||
isOccupied = data.isOccupied;
|
||||
|
|
|
|||
Loading…
Reference in New Issue