## Changes
```diff
diff --git a/api/spec/unit/models/Room.spec.ts b/api/spec/unit/models/Room.spec.ts
index e6c7481..f34ea18 100644
--- a/api/spec/unit/models/Room.spec.ts
+++ b/api/spec/unit/models/Room.spec.ts
@@ -1,5 +1,8 @@
import createLocalizedText from '@spec/factories/LocalizedTextFactory.js'
+import createPlace from '@spec/factories/PlaceFactory.js'
+import createRoomBedroom from '@spec/factories/Room/BedroomFactory.js'
import createRoomDen from '@spec/factories/Room/DenFactory.js'
+import createRoomKitchen from '@spec/factories/Room/KitchenFactory.js'
describe('Room', () => {
it('has many LocalizedTexts', async () => {
@@ -37,4 +40,17 @@ describe('Room', () => {
expect(room.currentLocalizedText).toMatchDreamModel(esLocalizedText)
})
+
+ describe('position', () => {
+ it('is automatically set and scoped to Place', async () => {
+ const place = await createPlace()
+ const kitchen = await createRoomKitchen({ place })
+ const otherBedroom = await createRoomBedroom()
+ const bedroom = await createRoomBedroom({ place })
+
+ expect(kitchen.position).toEqual(1)
+ expect(bedroom.position).toEqual(2)
+ expect(otherBedroom.position).toEqual(1)
+ })
+ })
})
diff --git a/api/src/app/models/Place.ts b/api/src/app/models/Place.ts
index 596e4c3..23f0515 100644
--- a/api/src/app/models/Place.ts
+++ b/api/src/app/models/Place.ts
@@ -37,7 +37,7 @@ export default class Place extends ApplicationModel {
@deco.HasMany('Host', { through: 'hostPlaces' })
public hosts: Host[]
- @deco.HasMany('Room', { dependent: 'destroy' })
+ @deco.HasMany('Room', { order: 'position', dependent: 'destroy' })
// make sure this imports from `import Room from '@models/Room.js'`
// not from `import { Room } from 'socket.io-adapter'`
public rooms: Room[]
diff --git a/api/src/app/models/Room.ts b/api/src/app/models/Room.ts
index eb177b3..0eb640b 100644
--- a/api/src/app/models/Room.ts
+++ b/api/src/app/models/Room.ts
@@ -14,7 +14,10 @@ export default class Room extends ApplicationModel {
public id: DreamColumn<Room, 'id'>
public type: DreamColumn<Room, 'type'>
+
+ @deco.Sortable({ scope: 'place' })
public position: DreamColumn<Room, 'position'>
+
public deletedAt: DreamColumn<Room, 'deletedAt'>
public createdAt: DreamColumn<Room, 'createdAt'>
public updatedAt: DreamColumn<Room, 'updatedAt'>
diff --git a/api/src/db/migrations/1765422215354-add-deferrable-unique-constraint-to-rooms.ts b/api/src/db/migrations/1765422215354-add-deferrable-unique-constraint-to-rooms.ts
new file mode 100644
index 0000000..acf0882
--- /dev/null
+++ b/api/src/db/migrations/1765422215354-add-deferrable-unique-constraint-to-rooms.ts
@@ -0,0 +1,19 @@
+import { DreamMigrationHelpers } from '@rvoh/dream/db'
+import { Kysely } from 'kysely'
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export async function up(db: Kysely<any>): Promise<void> {
+ // To prevent more than one Room within a Place having the same
+ // position, we add a unique constraint. Sortable requires that
+ // this be a deferrable unique constraint since, during the
+ // re-sorting transaction, the position may temporarily collide.
+ await DreamMigrationHelpers.addDeferrableUniqueConstraint(db, 'room_position_contraint', {
+ table: 'rooms',
+ columns: ['place_id', 'position'],
+ })
+}
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export async function down(db: Kysely<any>): Promise<void> {
+ await DreamMigrationHelpers.dropConstraint(db, 'room_position_contraint', { table: 'rooms' })
+}
\ No newline at end of file