From 4bc4267e5a36e37e54b9afb271f9304420a84463 Mon Sep 17 00:00:00 2001 From: Marcel Date: Tue, 28 Apr 2026 20:33:06 +0200 Subject: [PATCH] feat(person): ErrorCode.PERSON_RENAME_CONFLICT for optimistic-lock conflicts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the structured error code returned when a rename rolls back because a referenced transcription block was edited concurrently (OptimisticLockException on transcription_blocks.version). Mirrors the contract in frontend src/lib/errors.ts and adds the localised message keys error_person_rename_conflict in de/en/es so the UI surfaces a retry hint instead of a generic 500. The actual translation of OptimisticLockException → DomainException (PERSON_RENAME_CONFLICT) lands in the next commit alongside the integration test that proves the rollback semantics. Refs #362 Co-Authored-By: Claude Opus 4.7 --- .../java/org/raddatz/familienarchiv/exception/ErrorCode.java | 4 ++++ frontend/messages/de.json | 1 + frontend/messages/en.json | 1 + frontend/messages/es.json | 1 + frontend/src/lib/errors.ts | 3 +++ 5 files changed, 10 insertions(+) diff --git a/backend/src/main/java/org/raddatz/familienarchiv/exception/ErrorCode.java b/backend/src/main/java/org/raddatz/familienarchiv/exception/ErrorCode.java index 6d4e2533..d9a3a30f 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/exception/ErrorCode.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/exception/ErrorCode.java @@ -15,6 +15,10 @@ public enum ErrorCode { ALIAS_NOT_FOUND, /** The submitted personType value is not allowed (e.g. SKIP is import-only). 400 */ INVALID_PERSON_TYPE, + /** A concurrent edit on a referenced transcription block prevented the rename + * from committing (optimistic-lock conflict). The whole rename rolls back; the + * client should refetch and retry. 409 */ + PERSON_RENAME_CONFLICT, // --- Documents --- /** A document with the given ID does not exist. 404 */ diff --git a/frontend/messages/de.json b/frontend/messages/de.json index f8cf9f8c..5d17e947 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -542,6 +542,7 @@ "person_alias_btn_delete": "Entfernen", "error_alias_not_found": "Der Namensalias wurde nicht gefunden.", "error_invalid_person_type": "Der angegebene Personentyp ist ungültig.", + "error_person_rename_conflict": "Eine andere Bearbeitung hat einen verknüpften Transkriptionsblock gleichzeitig geändert. Bitte erneut versuchen.", "validation_last_name_required": "Nachname ist Pflichtfeld.", "validation_first_name_required": "Vorname ist Pflichtfeld.", "error_ocr_service_unavailable": "Der OCR-Dienst ist nicht verfügbar.", diff --git a/frontend/messages/en.json b/frontend/messages/en.json index afa6adfa..604aa617 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -542,6 +542,7 @@ "person_alias_btn_delete": "Remove", "error_alias_not_found": "The name alias was not found.", "error_invalid_person_type": "The specified person type is not valid.", + "error_person_rename_conflict": "Another edit modified a referenced transcription block at the same time. Please try again.", "validation_last_name_required": "Last name is required.", "validation_first_name_required": "First name is required.", "error_ocr_service_unavailable": "The OCR service is not available.", diff --git a/frontend/messages/es.json b/frontend/messages/es.json index 87d26d83..9e769d44 100644 --- a/frontend/messages/es.json +++ b/frontend/messages/es.json @@ -542,6 +542,7 @@ "person_alias_btn_delete": "Eliminar", "error_alias_not_found": "No se encontro el alias de nombre.", "error_invalid_person_type": "El tipo de persona especificado no es válido.", + "error_person_rename_conflict": "Otra edición modificó un bloque de transcripción referenciado al mismo tiempo. Por favor, inténtalo de nuevo.", "validation_last_name_required": "El apellido es obligatorio.", "validation_first_name_required": "El nombre es obligatorio.", "error_ocr_service_unavailable": "El servicio OCR no está disponible.", diff --git a/frontend/src/lib/errors.ts b/frontend/src/lib/errors.ts index e57f212e..3c80a1da 100644 --- a/frontend/src/lib/errors.ts +++ b/frontend/src/lib/errors.ts @@ -8,6 +8,7 @@ export type ErrorCode = | 'PERSON_NOT_FOUND' | 'ALIAS_NOT_FOUND' | 'INVALID_PERSON_TYPE' + | 'PERSON_RENAME_CONFLICT' | 'DOCUMENT_NOT_FOUND' | 'DOCUMENT_NO_FILE' | 'FILE_NOT_FOUND' @@ -79,6 +80,8 @@ export function getErrorMessage(code: ErrorCode | string | undefined): string { return m.error_alias_not_found(); case 'INVALID_PERSON_TYPE': return m.error_invalid_person_type(); + case 'PERSON_RENAME_CONFLICT': + return m.error_person_rename_conflict(); case 'DOCUMENT_NOT_FOUND': return m.error_document_not_found(); case 'DOCUMENT_NO_FILE':