From 9cec9b64c665fd9943f22d64b472da7ce1d50cda Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Sun, 24 May 2026 12:31:36 +0100 Subject: [PATCH] feat(bases): allow updateRow to set position atomically --- .../client/src/features/base/queries/base-row-query.ts | 3 +++ apps/client/src/features/base/types/base.types.ts | 1 + apps/server/src/core/base/dto/update-row.dto.ts | 5 +++++ apps/server/src/core/base/services/base-row.service.ts | 10 ++++++++++ apps/server/src/database/repos/base/base-row.repo.ts | 2 ++ 5 files changed, 21 insertions(+) diff --git a/apps/client/src/features/base/queries/base-row-query.ts b/apps/client/src/features/base/queries/base-row-query.ts index 7d305b51e..fba975bd1 100644 --- a/apps/client/src/features/base/queries/base-row-query.ts +++ b/apps/client/src/features/base/queries/base-row-query.ts @@ -150,6 +150,9 @@ export function useUpdateRowMutation() { ? { ...row, cells: { ...row.cells, ...variables.cells }, + ...(variables.position !== undefined && { + position: variables.position, + }), } : row, ), diff --git a/apps/client/src/features/base/types/base.types.ts b/apps/client/src/features/base/types/base.types.ts index 391eea5d0..010107a4b 100644 --- a/apps/client/src/features/base/types/base.types.ts +++ b/apps/client/src/features/base/types/base.types.ts @@ -272,6 +272,7 @@ export type UpdateRowInput = { rowId: string; pageId: string; cells: Record; + position?: string; requestId?: string; }; diff --git a/apps/server/src/core/base/dto/update-row.dto.ts b/apps/server/src/core/base/dto/update-row.dto.ts index 0b4a4969b..a87bcfede 100644 --- a/apps/server/src/core/base/dto/update-row.dto.ts +++ b/apps/server/src/core/base/dto/update-row.dto.ts @@ -25,6 +25,11 @@ export class UpdateRowDto { @IsObject() cells: Record; + @IsOptional() + @IsString() + @IsNotEmpty() + position?: string; + @IsOptional() @IsString() requestId?: string; diff --git a/apps/server/src/core/base/services/base-row.service.ts b/apps/server/src/core/base/services/base-row.service.ts index c6ce95a5a..11725d354 100644 --- a/apps/server/src/core/base/services/base-row.service.ts +++ b/apps/server/src/core/base/services/base-row.service.ts @@ -135,6 +135,15 @@ export class BaseRowService { const properties = await this.basePropertyRepo.findByPageId(dto.pageId); const validatedCells = this.validateCells(dto.cells, properties); + // Smoke-check the position (same guard as `reorder`). + if (dto.position !== undefined) { + try { + generateJitteredKeyBetween(dto.position, null); + } catch { + throw new BadRequestException('Invalid position value'); + } + } + const existing = await this.baseRowRepo.findById(dto.rowId, { workspaceId }); const mergedRow = { ...((existing?.cells as Record) ?? {}), @@ -154,6 +163,7 @@ export class BaseRowService { pageId: dto.pageId, workspaceId, actorId: userId, + position: dto.position, }, ); diff --git a/apps/server/src/database/repos/base/base-row.repo.ts b/apps/server/src/database/repos/base/base-row.repo.ts index 462be8afd..d1740aa3d 100644 --- a/apps/server/src/database/repos/base/base-row.repo.ts +++ b/apps/server/src/database/repos/base/base-row.repo.ts @@ -275,6 +275,7 @@ export class BaseRowRepo { pageId: string; workspaceId: string; actorId?: string; + position?: string; trx?: KyselyTransaction; }, ): Promise { @@ -287,6 +288,7 @@ export class BaseRowRepo { .updateTable('baseRows') .set({ cells: sql`jsonb_set_many(cells, ${patchJson}::text::jsonb)`, + ...(opts.position !== undefined && { position: opts.position }), updatedAt: new Date(), lastUpdatedById: opts.actorId ?? null, })