diff --git a/frontend/messages/de.json b/frontend/messages/de.json index 99c9c1b7..2767cc01 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -1174,5 +1174,36 @@ "journey_item_open_aria_undated": "Brief öffnen", "journey_empty_state": "Diese Lesereise ist noch leer.", "journey_interlude_aria_label": "Kuratorennotiz", - "journey_selector_aria_live_hint": "Bitte wähle einen Typ aus, um fortzufahren." + "journey_selector_aria_live_hint": "Bitte wähle einen Typ aus, um fortzufahren.", + "journey_add_document": "Brief hinzufügen", + "journey_add_interlude": "Zwischentext hinzufügen", + "journey_note_add": "Notiz hinzufügen", + "journey_note_remove": "Notiz entfernen", + "journey_note_save_hint": "Wird gespeichert, wenn du das Feld verlässt.", + "journey_intro_save_hint": "Wird mit 'Speichern' gesichert.", + "journey_already_added": "Bereits enthalten", + "journey_note_aria_label": "Kuratoren-Notiz für {title}", + "journey_drag_aria_label": "Reihenfolge von '{title}' ändern", + "journey_move_up": "'{title}' nach oben verschieben", + "journey_move_down": "'{title}' nach unten verschieben", + "journey_note_error": "Notiz konnte nicht gespeichert werden", + "journey_item_moved": "Eintrag {position} von {total} — nach Position {newPosition} verschoben", + "journey_remove_confirm": "Wirklich entfernen?", + "journey_remove_confirm_yes": "Bestätigen", + "journey_remove_confirm_cancel": "Abbrechen", + "journey_mutation_error_reload": "Aktion fehlgeschlagen – bitte Seite neu laden.", + "journey_item_pending_add": "wird hinzugefügt…", + "journey_item_pending_remove": "wird entfernt…", + "journey_published_empty_warning": "Diese Reise wird ohne Einträge veröffentlicht bleiben.", + "journey_intro_placeholder": "Einleitung (optional)", + "journey_interlude_placeholder": "Zwischentext eingeben…", + "journey_add_interlude_confirm": "Hinzufügen", + "journey_edit_title_story": "Geschichte bearbeiten", + "journey_edit_title_journey": "Lesereise bearbeiten", + "journey_publish_disabled_title": "Titel und mindestens ein Eintrag erforderlich", + "journey_save_hint_published": "Änderungen werden sofort für alle Leser sichtbar.", + "error_journey_item_not_in_journey": "Dieser Eintrag gehört nicht zu dieser Lesereise.", + "error_journey_note_too_long": "Die Notiz ist zu lang (maximal 2000 Zeichen).", + "error_journey_document_already_added": "Dieser Brief ist bereits in der Lesereise enthalten.", + "error_geschichte_type_immutable": "Der Typ einer Geschichte kann nach der Erstellung nicht mehr geändert werden." } diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 0c4686a4..f2e4af9e 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -1174,5 +1174,36 @@ "journey_item_open_aria_undated": "Open letter", "journey_empty_state": "This reading journey is still empty.", "journey_interlude_aria_label": "Curator's note", - "journey_selector_aria_live_hint": "Please select a type to continue." + "journey_selector_aria_live_hint": "Please select a type to continue.", + "journey_add_document": "Add letter", + "journey_add_interlude": "Add interlude", + "journey_note_add": "Add note", + "journey_note_remove": "Remove note", + "journey_note_save_hint": "Saved when you leave the field.", + "journey_intro_save_hint": "Saved when you click 'Save'.", + "journey_already_added": "Already included", + "journey_note_aria_label": "Curator note for {title}", + "journey_drag_aria_label": "Change order of '{title}'", + "journey_move_up": "Move '{title}' up", + "journey_move_down": "Move '{title}' down", + "journey_note_error": "Could not save note", + "journey_item_moved": "Entry {position} of {total} — moved to position {newPosition}", + "journey_remove_confirm": "Really remove?", + "journey_remove_confirm_yes": "Confirm", + "journey_remove_confirm_cancel": "Cancel", + "journey_mutation_error_reload": "Action failed – please reload the page.", + "journey_item_pending_add": "adding…", + "journey_item_pending_remove": "removing…", + "journey_published_empty_warning": "This journey will remain published without any entries.", + "journey_intro_placeholder": "Introduction (optional)", + "journey_interlude_placeholder": "Enter interlude text…", + "journey_add_interlude_confirm": "Add", + "journey_edit_title_story": "Edit story", + "journey_edit_title_journey": "Edit reading journey", + "journey_publish_disabled_title": "Title and at least one entry required", + "journey_save_hint_published": "Changes will be immediately visible to all readers.", + "error_journey_item_not_in_journey": "This entry does not belong to this reading journey.", + "error_journey_note_too_long": "The note is too long (maximum 2000 characters).", + "error_journey_document_already_added": "This letter is already included in the reading journey.", + "error_geschichte_type_immutable": "The type of a story cannot be changed after creation." } diff --git a/frontend/messages/es.json b/frontend/messages/es.json index 3c8a361e..af712f47 100644 --- a/frontend/messages/es.json +++ b/frontend/messages/es.json @@ -1174,5 +1174,36 @@ "journey_item_open_aria_undated": "Abrir carta", "journey_empty_state": "Este viaje de lectura está vacío.", "journey_interlude_aria_label": "Nota del curador", - "journey_selector_aria_live_hint": "Por favor, selecciona un tipo para continuar." + "journey_selector_aria_live_hint": "Por favor, selecciona un tipo para continuar.", + "journey_add_document": "Añadir carta", + "journey_add_interlude": "Añadir interludio", + "journey_note_add": "Añadir nota", + "journey_note_remove": "Eliminar nota", + "journey_note_save_hint": "Se guarda al salir del campo.", + "journey_intro_save_hint": "Se guarda al hacer clic en 'Guardar'.", + "journey_already_added": "Ya incluido", + "journey_note_aria_label": "Nota del curador para {title}", + "journey_drag_aria_label": "Cambiar el orden de '{title}'", + "journey_move_up": "Subir '{title}'", + "journey_move_down": "Bajar '{title}'", + "journey_note_error": "No se pudo guardar la nota", + "journey_item_moved": "Entrada {position} de {total} — movida a la posición {newPosition}", + "journey_remove_confirm": "¿Realmente eliminar?", + "journey_remove_confirm_yes": "Confirmar", + "journey_remove_confirm_cancel": "Cancelar", + "journey_mutation_error_reload": "Acción fallida – por favor recarga la página.", + "journey_item_pending_add": "añadiendo…", + "journey_item_pending_remove": "eliminando…", + "journey_published_empty_warning": "Este viaje permanecerá publicado sin entradas.", + "journey_intro_placeholder": "Introducción (opcional)", + "journey_interlude_placeholder": "Escribe el texto del interludio…", + "journey_add_interlude_confirm": "Añadir", + "journey_edit_title_story": "Editar historia", + "journey_edit_title_journey": "Editar viaje de lectura", + "journey_publish_disabled_title": "Se requiere título y al menos una entrada", + "journey_save_hint_published": "Los cambios serán visibles inmediatamente para todos los lectores.", + "error_journey_item_not_in_journey": "Esta entrada no pertenece a este viaje de lectura.", + "error_journey_note_too_long": "La nota es demasiado larga (máximo 2000 caracteres).", + "error_journey_document_already_added": "Esta carta ya está incluida en el viaje de lectura.", + "error_geschichte_type_immutable": "El tipo de una historia no se puede cambiar después de su creación." } diff --git a/frontend/src/lib/shared/errors.ts b/frontend/src/lib/shared/errors.ts index 59a0a846..203e35fc 100644 --- a/frontend/src/lib/shared/errors.ts +++ b/frontend/src/lib/shared/errors.ts @@ -47,9 +47,13 @@ export type ErrorCode = | 'DUPLICATE_RELATIONSHIP' | 'GESCHICHTE_NOT_FOUND' | 'JOURNEY_ITEM_NOT_FOUND' + | 'JOURNEY_ITEM_NOT_IN_JOURNEY' | 'JOURNEY_ITEM_POSITION_CONFLICT' | 'JOURNEY_AT_CAPACITY' + | 'JOURNEY_NOTE_TOO_LONG' + | 'JOURNEY_DOCUMENT_ALREADY_ADDED' | 'GESCHICHTE_TYPE_MISMATCH' + | 'GESCHICHTE_TYPE_IMMUTABLE' | 'INVALID_CREDENTIALS' | 'SESSION_EXPIRED' | 'MISSING_CREDENTIALS' @@ -170,12 +174,20 @@ export function getErrorMessage(code: ErrorCode | string | undefined): string { return m.error_geschichte_not_found(); case 'JOURNEY_ITEM_NOT_FOUND': return m.error_journey_item_not_found(); + case 'JOURNEY_ITEM_NOT_IN_JOURNEY': + return m.error_journey_item_not_in_journey(); case 'JOURNEY_ITEM_POSITION_CONFLICT': return m.error_journey_item_position_conflict(); case 'JOURNEY_AT_CAPACITY': return m.error_journey_at_capacity(); + case 'JOURNEY_NOTE_TOO_LONG': + return m.error_journey_note_too_long(); + case 'JOURNEY_DOCUMENT_ALREADY_ADDED': + return m.error_journey_document_already_added(); case 'GESCHICHTE_TYPE_MISMATCH': return m.error_geschichte_type_mismatch(); + case 'GESCHICHTE_TYPE_IMMUTABLE': + return m.error_geschichte_type_immutable(); case 'INVALID_CREDENTIALS': return m.error_invalid_credentials(); case 'SESSION_EXPIRED': diff --git a/frontend/src/routes/layout.css b/frontend/src/routes/layout.css index 8990d355..f3b6fe5c 100644 --- a/frontend/src/routes/layout.css +++ b/frontend/src/routes/layout.css @@ -82,6 +82,11 @@ --color-journey: var(--c-journey-text); --color-journey-border: var(--c-journey-border); + /* Interlude row — neutral surface with left accent border; ZWISCHENTEXT label */ + --color-interlude-bg: var(--c-interlude-bg); + --color-interlude-border: var(--c-interlude-border); + --color-interlude-label: var(--c-interlude-label); + /* Static brand tokens (not themed) */ --color-brand-navy: var(--palette-navy); --color-brand-mint: var(--palette-mint); @@ -139,6 +144,11 @@ --c-journey-text: #7a3f0e; --c-journey-border: #f0c99a; + /* Interlude (Zwischentext) — neutral warm surface with left accent border */ + --c-interlude-bg: #f5f4f0; + --c-interlude-border: #a1dcd8; + --c-interlude-label: #6b7280; + /* Tag color tokens — decorative dot colors on tag chips */ --c-tag-sage: #5a8a6a; --c-tag-sienna: #a0522d; @@ -263,6 +273,11 @@ --c-journey-bg: #3a2a1a; --c-journey-text: #e8862a; --c-journey-border: #7a4a1e; + + /* Interlude (Zwischentext) — KEEP IN SYNC with :root[data-theme='dark'] */ + --c-interlude-bg: #151c22; + --c-interlude-border: #00c7b1; + --c-interlude-label: #8b97a5; } } @@ -343,6 +358,11 @@ --c-journey-bg: #3a2a1a; --c-journey-text: #e8862a; --c-journey-border: #7a4a1e; + + /* Interlude (Zwischentext) — KEEP IN SYNC with the @media block above */ + --c-interlude-bg: #151c22; + --c-interlude-border: #00c7b1; + --c-interlude-label: #8b97a5; } /* ─── 6. Icon inversion — De Gruyter icons are black SVGs loaded as ──── */