feat(persons): add formatLifeDateRange + formatDocumentStatus utility functions
Unit tests for both; i18n keys for doc status and person stats bar; PERSON_NOT_FOUND added to frontend ErrorCode type. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -320,5 +320,22 @@
|
||||
"dashboard_needs_metadata_show_all": "Alle anzeigen",
|
||||
"dashboard_recent_heading": "Zuletzt aktiv",
|
||||
"dashboard_resume_label": "Zuletzt geöffnet:",
|
||||
"dashboard_resume_fallback": "Unbekanntes Dokument"
|
||||
"dashboard_resume_fallback": "Unbekanntes Dokument",
|
||||
"doc_status_placeholder": "Platzhalter",
|
||||
"doc_status_uploaded": "Hochgeladen",
|
||||
"doc_status_transcribed": "Transkribiert",
|
||||
"doc_status_reviewed": "Geprüft",
|
||||
"doc_status_archived": "Archiviert",
|
||||
"doc_status_unknown": "Unbekannt",
|
||||
"persons_stats_persons_one": "1 Person",
|
||||
"persons_stats_persons_many": "{count} Personen",
|
||||
"persons_stats_documents_one": "1 Dokument",
|
||||
"persons_stats_documents_many": "{count} Dokumente",
|
||||
"error_person_not_found": "Die Person wurde nicht gefunden.",
|
||||
"person_btn_edit": "Bearbeiten",
|
||||
"person_discard_changes": "Änderungen verwerfen",
|
||||
"person_danger_zone_heading": "Gefahrenzone",
|
||||
"persons_new_birth_year": "Geburtsjahr",
|
||||
"persons_new_death_year": "Todesjahr",
|
||||
"persons_new_notes": "Notizen"
|
||||
}
|
||||
|
||||
@@ -320,5 +320,22 @@
|
||||
"dashboard_needs_metadata_show_all": "Show all",
|
||||
"dashboard_recent_heading": "Recent Activity",
|
||||
"dashboard_resume_label": "Last opened:",
|
||||
"dashboard_resume_fallback": "Unknown document"
|
||||
"dashboard_resume_fallback": "Unknown document",
|
||||
"doc_status_placeholder": "Placeholder",
|
||||
"doc_status_uploaded": "Uploaded",
|
||||
"doc_status_transcribed": "Transcribed",
|
||||
"doc_status_reviewed": "Reviewed",
|
||||
"doc_status_archived": "Archived",
|
||||
"doc_status_unknown": "Unknown",
|
||||
"persons_stats_persons_one": "1 person",
|
||||
"persons_stats_persons_many": "{count} persons",
|
||||
"persons_stats_documents_one": "1 document",
|
||||
"persons_stats_documents_many": "{count} documents",
|
||||
"error_person_not_found": "Person not found.",
|
||||
"person_btn_edit": "Edit",
|
||||
"person_discard_changes": "Discard changes",
|
||||
"person_danger_zone_heading": "Danger zone",
|
||||
"persons_new_birth_year": "Birth year",
|
||||
"persons_new_death_year": "Death year",
|
||||
"persons_new_notes": "Notes"
|
||||
}
|
||||
|
||||
@@ -320,5 +320,22 @@
|
||||
"dashboard_needs_metadata_show_all": "Ver todos",
|
||||
"dashboard_recent_heading": "Actividad reciente",
|
||||
"dashboard_resume_label": "Último abierto:",
|
||||
"dashboard_resume_fallback": "Documento desconocido"
|
||||
"dashboard_resume_fallback": "Documento desconocido",
|
||||
"doc_status_placeholder": "Marcador",
|
||||
"doc_status_uploaded": "Cargado",
|
||||
"doc_status_transcribed": "Transcrito",
|
||||
"doc_status_reviewed": "Revisado",
|
||||
"doc_status_archived": "Archivado",
|
||||
"doc_status_unknown": "Desconocido",
|
||||
"persons_stats_persons_one": "1 persona",
|
||||
"persons_stats_persons_many": "{count} personas",
|
||||
"persons_stats_documents_one": "1 documento",
|
||||
"persons_stats_documents_many": "{count} documentos",
|
||||
"error_person_not_found": "Persona no encontrada.",
|
||||
"person_btn_edit": "Editar",
|
||||
"person_discard_changes": "Descartar cambios",
|
||||
"person_danger_zone_heading": "Zona de peligro",
|
||||
"persons_new_birth_year": "Año de nacimiento",
|
||||
"persons_new_death_year": "Año de fallecimiento",
|
||||
"persons_new_notes": "Notas"
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import * as m from '$lib/paraglide/messages.js';
|
||||
* Keep in sync with backend/src/main/java/org/raddatz/familienarchiv/exception/ErrorCode.java
|
||||
*/
|
||||
export type ErrorCode =
|
||||
| 'PERSON_NOT_FOUND'
|
||||
| 'DOCUMENT_NOT_FOUND'
|
||||
| 'DOCUMENT_NO_FILE'
|
||||
| 'FILE_NOT_FOUND'
|
||||
@@ -47,6 +48,8 @@ export async function parseBackendError(res: Response): Promise<BackendError | n
|
||||
/** Returns a localised message for the given error code via Paraglide. Falls back to INTERNAL_ERROR. */
|
||||
export function getErrorMessage(code: ErrorCode | string | undefined): string {
|
||||
switch (code) {
|
||||
case 'PERSON_NOT_FOUND':
|
||||
return m.error_person_not_found();
|
||||
case 'DOCUMENT_NOT_FOUND':
|
||||
return m.error_document_not_found();
|
||||
case 'DOCUMENT_NO_FILE':
|
||||
|
||||
28
frontend/src/lib/utils/documentStatusLabel.spec.ts
Normal file
28
frontend/src/lib/utils/documentStatusLabel.spec.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { formatDocumentStatus } from './documentStatusLabel';
|
||||
|
||||
describe('formatDocumentStatus', () => {
|
||||
it('maps PLACEHOLDER to correct label', () => {
|
||||
expect(formatDocumentStatus('PLACEHOLDER')).toBe('Platzhalter');
|
||||
});
|
||||
|
||||
it('maps UPLOADED to correct label', () => {
|
||||
expect(formatDocumentStatus('UPLOADED')).toBe('Hochgeladen');
|
||||
});
|
||||
|
||||
it('maps TRANSCRIBED to correct label', () => {
|
||||
expect(formatDocumentStatus('TRANSCRIBED')).toBe('Transkribiert');
|
||||
});
|
||||
|
||||
it('maps REVIEWED to correct label', () => {
|
||||
expect(formatDocumentStatus('REVIEWED')).toBe('Geprüft');
|
||||
});
|
||||
|
||||
it('maps ARCHIVED to correct label', () => {
|
||||
expect(formatDocumentStatus('ARCHIVED')).toBe('Archiviert');
|
||||
});
|
||||
|
||||
it('returns fallback for unknown status', () => {
|
||||
expect(formatDocumentStatus('SOMETHING_NEW')).toBe('Unbekannt');
|
||||
});
|
||||
});
|
||||
22
frontend/src/lib/utils/documentStatusLabel.ts
Normal file
22
frontend/src/lib/utils/documentStatusLabel.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { m } from '$lib/paraglide/messages.js';
|
||||
|
||||
/**
|
||||
* Maps a document status string to a localised human-readable label.
|
||||
* Falls back to "Unknown" for unrecognised values.
|
||||
*/
|
||||
export function formatDocumentStatus(status: string): string {
|
||||
switch (status) {
|
||||
case 'PLACEHOLDER':
|
||||
return m.doc_status_placeholder();
|
||||
case 'UPLOADED':
|
||||
return m.doc_status_uploaded();
|
||||
case 'TRANSCRIBED':
|
||||
return m.doc_status_transcribed();
|
||||
case 'REVIEWED':
|
||||
return m.doc_status_reviewed();
|
||||
case 'ARCHIVED':
|
||||
return m.doc_status_archived();
|
||||
default:
|
||||
return m.doc_status_unknown();
|
||||
}
|
||||
}
|
||||
24
frontend/src/lib/utils/personLifeDates.spec.ts
Normal file
24
frontend/src/lib/utils/personLifeDates.spec.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { formatLifeDateRange } from './personLifeDates';
|
||||
|
||||
describe('formatLifeDateRange', () => {
|
||||
it('returns both dates when birth and death year are given', () => {
|
||||
expect(formatLifeDateRange(1882, 1944)).toBe('* 1882 – † 1944');
|
||||
});
|
||||
|
||||
it('returns only birth year when only birthYear is given', () => {
|
||||
expect(formatLifeDateRange(1882, undefined)).toBe('* 1882');
|
||||
});
|
||||
|
||||
it('returns only death year when only deathYear is given', () => {
|
||||
expect(formatLifeDateRange(undefined, 1944)).toBe('† 1944');
|
||||
});
|
||||
|
||||
it('returns empty string when neither year is given', () => {
|
||||
expect(formatLifeDateRange(undefined, undefined)).toBe('');
|
||||
});
|
||||
|
||||
it('returns empty string when both are null', () => {
|
||||
expect(formatLifeDateRange(null, null)).toBe('');
|
||||
});
|
||||
});
|
||||
20
frontend/src/lib/utils/personLifeDates.ts
Normal file
20
frontend/src/lib/utils/personLifeDates.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Formats the life date range for a person.
|
||||
* Examples:
|
||||
* * 1882 – † 1944 (both)
|
||||
* * 1882 (birth only)
|
||||
* † 1944 (death only)
|
||||
* "" (neither)
|
||||
*/
|
||||
export function formatLifeDateRange(birthYear?: number | null, deathYear?: number | null): string {
|
||||
if (birthYear && deathYear) {
|
||||
return `* ${birthYear} – † ${deathYear}`;
|
||||
}
|
||||
if (birthYear) {
|
||||
return `* ${birthYear}`;
|
||||
}
|
||||
if (deathYear) {
|
||||
return `† ${deathYear}`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
Reference in New Issue
Block a user