diff --git a/.idea/workspace.xml b/.idea/workspace.xml index b152a8f..9adc0b8 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,7 +4,14 @@ - + + + + + + + + @@ -125,7 +132,7 @@ 1774643312599 - + @@ -271,7 +278,15 @@ 1774715728737 - + + + 1774715983233 + + + + 1774715983233 + + @@ -298,6 +313,7 @@ - + + \ No newline at end of file diff --git a/apps/api/prisma/schema.prisma b/apps/api/prisma/schema.prisma index 2c6cad3..fe378ff 100644 --- a/apps/api/prisma/schema.prisma +++ b/apps/api/prisma/schema.prisma @@ -71,6 +71,7 @@ model Space { updatedAt DateTime @updatedAt height Float? width Float? + rotation Float? @default(0) reservations Reservation[] floor Floor @relation(fields: [floorId], references: [id]) } diff --git a/apps/api/src/spaces/dto/update-space.dto.ts b/apps/api/src/spaces/dto/update-space.dto.ts index f462aa3..c08f1a3 100644 --- a/apps/api/src/spaces/dto/update-space.dto.ts +++ b/apps/api/src/spaces/dto/update-space.dto.ts @@ -21,6 +21,10 @@ export class UpdateSpaceDto { @IsOptional() height?: number; + @IsNumber() + @IsOptional() + rotation?: number; + @IsString() @IsOptional() name?: string; diff --git a/apps/api/src/spaces/spaces.service.ts b/apps/api/src/spaces/spaces.service.ts index db3b6a7..f5f8e1a 100644 --- a/apps/api/src/spaces/spaces.service.ts +++ b/apps/api/src/spaces/spaces.service.ts @@ -167,6 +167,7 @@ export class SpacesService { y: data.y, width: data.width, height: data.height, + rotation: data.rotation, }, }); } @@ -177,6 +178,7 @@ export class SpacesService { y: data.y, width: data.width, height: data.height, + rotation: data.rotation, name: data.name, }, }); diff --git a/apps/web/src/lib/components/FloorplanViewer.svelte b/apps/web/src/lib/components/FloorplanViewer.svelte index 7b38c73..0099b5e 100644 --- a/apps/web/src/lib/components/FloorplanViewer.svelte +++ b/apps/web/src/lib/components/FloorplanViewer.svelte @@ -149,18 +149,25 @@ {@const status = spaceStatus[space.id] || { isOccupied: false }} handleSpaceClick(space)} - class="absolute p-2 border-2 rounded-lg shadow-md transition-all {space.type !== 'AMENITY' ? 'hover:scale-105 cursor-pointer' : 'cursor-default opacity-80'} {status.isOccupied ? 'bg-yellow-50 border-yellow-500' : 'bg-green-50 border-green-500'}" + class="absolute p-2 border-2 rounded-lg shadow-md transition-all overflow-hidden flex items-center justify-center {space.type !== 'AMENITY' ? 'hover:scale-105 cursor-pointer' : 'cursor-default opacity-80'} {status.isOccupied ? 'bg-yellow-50 border-yellow-500' : 'bg-green-50 border-green-500'}" style="left: 0; top: 0; width: {(space.width / 100) * containerWidth}px; height: {(space.height / 100) * containerHeight}px; transform: translate({(space.x / 100) * containerWidth}px, {(space.y / 100) * containerHeight}px)" title="{space.name} ({status.isOccupied ? 'Booked' : 'Available'})" > - + {#if status.isOccupied && space.type === 'DESK'} - + import { onMount } from 'svelte'; import interact from 'interactjs'; - import { Laptop, Bath, Coffee, Users, Save, Upload } from 'lucide-svelte'; + import { Laptop, Bath, Coffee, Users, Save, Upload, RotateCw } from 'lucide-svelte'; import { apiFetch } from '$lib/api/client'; import { resolveAssetUrl } from '$lib/utils/url'; @@ -219,6 +219,7 @@ y: 5, width: item.type === 'DESK' ? 12 : 20, // Default widths as percentage height: item.type === 'DESK' ? 8 : 15, // Default heights as percentage + rotation: 0, floorId }; spaces = [...spaces, newSpace]; @@ -287,6 +288,24 @@ editingId = null; } + function rotateSpace(id: string) { + const index = spaces.findIndex(s => s.id === id); + if (index !== -1) { + const space = spaces[index]; + const oldWidth = space.width; + const oldHeight = space.height; + + // Swap width and height + spaces[index].width = oldHeight * (containerHeight / containerWidth); + spaces[index].height = oldWidth * (containerWidth / containerHeight); + + // Update rotation + spaces[index].rotation = ((spaces[index].rotation || 0) + 90) % 360; + + spaces = [...spaces]; + } + } + function handleKeydown(event: KeyboardEvent, id: string) { if (event.key === 'Enter') { stopEditing(); @@ -368,13 +387,20 @@ {#each spaces as space (space.id)} - + {#if space.type === 'DESK'} @@ -408,14 +434,33 @@ > {space.name} - {/if} - removeSpace(space.id)} - class="hidden group-hover:flex items-center justify-center shrink-0 w-4 h-4 text-white bg-red-500 rounded-full hover:bg-red-600 ml-auto" - > - × - + {#if space.type === 'DESK'} + rotateSpace(space.id)} + class="hidden group-hover:flex items-center justify-center shrink-0 w-4 h-4 text-white bg-blue-500 rounded-full hover:bg-blue-600 ml-auto" + title="Rotate Desk" + style="transform: rotate(-{space.rotation || 0}deg);" + > + + + removeSpace(space.id)} + class="hidden group-hover:flex items-center justify-center shrink-0 w-4 h-4 text-white bg-red-500 rounded-full hover:bg-red-600 ml-1" + style="transform: rotate(-{space.rotation || 0}deg);" + > + × + + {:else} + removeSpace(space.id)} + class="hidden group-hover:flex items-center justify-center shrink-0 w-4 h-4 text-white bg-red-500 rounded-full hover:bg-red-600 ml-auto" + style="transform: rotate(-{space.rotation || 0}deg);" + > + × + + {/if} + {/if} {#if space.type !== 'DESK'}