diff --git a/frontend/messages/de.json b/frontend/messages/de.json index e614ac93..95bbbc3e 100644 --- a/frontend/messages/de.json +++ b/frontend/messages/de.json @@ -215,5 +215,23 @@ "reset_password_submit": "Passwort speichern", "reset_password_mismatch": "Die Passwörter stimmen nicht überein.", "reset_password_success": "Ihr Passwort wurde erfolgreich geändert. Sie können sich jetzt anmelden.", - "login_forgot_password": "Passwort vergessen?" + "login_forgot_password": "Passwort vergessen?", + "history_section_title": "Verlauf", + "history_loading": "Lade Verlauf…", + "history_empty": "Noch keine Versionen vorhanden.", + "history_version_label": "Version", + "history_compare_mode": "Vergleichen", + "history_compare_select_a": "Version A", + "history_compare_select_b": "Version B", + "history_compare_apply": "Vergleichen", + "history_diff_no_changes": "Keine Änderungen zwischen diesen Versionen.", + "history_field_title": "Titel", + "history_field_document_date": "Datum", + "history_field_location": "Ort", + "history_field_document_location": "Archivstandort", + "history_field_transcription": "Transkription", + "history_field_summary": "Zusammenfassung", + "history_field_sender": "Absender", + "history_field_receivers": "Empfänger", + "history_field_tags": "Schlagworte" } diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 79d96300..ce0441cb 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -215,5 +215,23 @@ "reset_password_submit": "Save password", "reset_password_mismatch": "The passwords do not match.", "reset_password_success": "Your password has been changed successfully. You can now log in.", - "login_forgot_password": "Forgot password?" + "login_forgot_password": "Forgot password?", + "history_section_title": "History", + "history_loading": "Loading history…", + "history_empty": "No versions yet.", + "history_version_label": "Version", + "history_compare_mode": "Compare", + "history_compare_select_a": "Version A", + "history_compare_select_b": "Version B", + "history_compare_apply": "Compare", + "history_diff_no_changes": "No changes between these versions.", + "history_field_title": "Title", + "history_field_document_date": "Date", + "history_field_location": "Location", + "history_field_document_location": "Archive location", + "history_field_transcription": "Transcription", + "history_field_summary": "Summary", + "history_field_sender": "Sender", + "history_field_receivers": "Receivers", + "history_field_tags": "Tags" } diff --git a/frontend/messages/es.json b/frontend/messages/es.json index f12d6e40..29c1f750 100644 --- a/frontend/messages/es.json +++ b/frontend/messages/es.json @@ -215,5 +215,23 @@ "reset_password_submit": "Guardar contraseña", "reset_password_mismatch": "Las contraseñas no coinciden.", "reset_password_success": "Su contraseña ha sido cambiada con éxito. Ahora puede iniciar sesión.", - "login_forgot_password": "¿Olvidó su contraseña?" + "login_forgot_password": "¿Olvidó su contraseña?", + "history_section_title": "Historial", + "history_loading": "Cargando historial…", + "history_empty": "Aún no hay versiones.", + "history_version_label": "Versión", + "history_compare_mode": "Comparar", + "history_compare_select_a": "Versión A", + "history_compare_select_b": "Versión B", + "history_compare_apply": "Comparar", + "history_diff_no_changes": "No hay cambios entre estas versiones.", + "history_field_title": "Título", + "history_field_document_date": "Fecha", + "history_field_location": "Lugar", + "history_field_document_location": "Ubicación en archivo", + "history_field_transcription": "Transcripción", + "history_field_summary": "Resumen", + "history_field_sender": "Remitente", + "history_field_receivers": "Destinatarios", + "history_field_tags": "Etiquetas" } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index de2738e0..6795fc10 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend", "version": "0.0.1", "dependencies": { + "diff": "^8.0.3", "openapi-fetch": "^0.13.5" }, "devDependencies": { @@ -21,6 +22,7 @@ "@tailwindcss/forms": "^0.5.10", "@tailwindcss/typography": "^0.5.19", "@tailwindcss/vite": "^4.1.17", + "@types/diff": "^7.0.2", "@types/node": "^24", "@vitest/browser-playwright": "^4.0.10", "eslint": "^9.39.1", @@ -1895,6 +1897,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/diff": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/diff/-/diff-7.0.2.tgz", + "integrity": "sha512-JSWRMozjFKsGlEjiiKajUjIJVKuKdE3oVy2DNtK+fUo8q82nhFZ2CPQwicAIkXrofahDXrWJ7mjelvZphMS98Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2780,6 +2789,15 @@ "dev": true, "license": "MIT" }, + "node_modules/diff": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", + "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/enhanced-resolve": { "version": "5.20.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 8485aecf..7e8ded46 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,6 +20,7 @@ "generate:api": "openapi-typescript http://localhost:8080/v3/api-docs -o ./src/lib/generated/api.ts" }, "dependencies": { + "diff": "^8.0.3", "openapi-fetch": "^0.13.5" }, "devDependencies": { @@ -33,6 +34,7 @@ "@tailwindcss/forms": "^0.5.10", "@tailwindcss/typography": "^0.5.19", "@tailwindcss/vite": "^4.1.17", + "@types/diff": "^7.0.2", "@types/node": "^24", "@vitest/browser-playwright": "^4.0.10", "eslint": "^9.39.1", diff --git a/frontend/src/lib/generated/api.ts b/frontend/src/lib/generated/api.ts index 46539816..f86f61dc 100644 --- a/frontend/src/lib/generated/api.ts +++ b/frontend/src/lib/generated/api.ts @@ -308,6 +308,38 @@ export interface paths { patch?: never; trace?: never; }; + "/api/documents/{id}/versions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getVersions"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/api/documents/{id}/versions/{versionId}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["getVersion"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/api/documents/{id}/file": { parameters: { query?: never; @@ -516,6 +548,27 @@ export interface components { /** Format: date-time */ startedAt?: string; }; + DocumentVersionSummary: { + /** Format: uuid */ + id: string; + /** Format: date-time */ + savedAt: string; + editorName: string; + changedFields: string[]; + }; + DocumentVersion: { + /** Format: uuid */ + id: string; + /** Format: uuid */ + documentId: string; + /** Format: date-time */ + savedAt: string; + /** Format: uuid */ + editorId?: string; + editorName: string; + snapshot: string; + changedFields: string; + }; }; responses: never; parameters: never; @@ -1189,6 +1242,51 @@ export interface operations { }; }; }; + getVersions: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["DocumentVersionSummary"][]; + }; + }; + }; + }; + getVersion: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + versionId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "*/*": components["schemas"]["DocumentVersion"]; + }; + }; + }; + }; getDocumentFile: { parameters: { query?: never; diff --git a/frontend/src/routes/documents/[id]/+page.svelte b/frontend/src/routes/documents/[id]/+page.svelte index 545fc804..1f65532a 100644 --- a/frontend/src/routes/documents/[id]/+page.svelte +++ b/frontend/src/routes/documents/[id]/+page.svelte @@ -1,6 +1,7 @@
@@ -346,6 +578,212 @@ async function loadFile(id: string) {
{/if} + +
+
+

+ {m.history_section_title()} +

+ +
+ + {#if historyOpen} +
+ {#if historyLoading} +

{m.history_loading()}

+ {:else if versions.length === 0} +

{m.history_empty()}

+ {:else} + +
+ +
+ + {#if compareMode} + +
+
+ + +
+
+ + +
+ +
+ {:else} + + + {/if} + + + {#if diffLoading} +

{m.history_loading()}

+ {:else if noDiff} +
+ {m.history_diff_no_changes()} +
+ {:else if diffEntries.length > 0} +
+ {#each diffEntries as entry (entry.field)} +
+ {entry.label} + {#if entry.kind === 'text'} +

+ {#each entry.parts as part, partIdx (partIdx)} + {#if part.added} + {part.value} + {:else if part.removed} + {part.value} + {:else} + {part.value} + {/if} + {/each} +

+ {:else if entry.kind === 'scalar'} +
+ {entry.oldVal || '—'} + + {entry.newVal || '—'} +
+ {:else if entry.kind === 'relation'} +
+ {#each entry.removed as item (item)} + {item} + {/each} + {#each entry.added as item (item)} + {item} + {/each} +
+ {/if} +
+ {/each} +
+ {/if} + {/if} +
+ {/if} +
+

ID: {doc.id}

diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 2081246d..386ec56f 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -452,6 +452,11 @@ resolved "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz" integrity sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw== +"@types/diff@^7.0.2": + version "7.0.2" + resolved "https://registry.npmjs.org/@types/diff/-/diff-7.0.2.tgz" + integrity sha512-JSWRMozjFKsGlEjiiKajUjIJVKuKdE3oVy2DNtK+fUo8q82nhFZ2CPQwicAIkXrofahDXrWJ7mjelvZphMS98Q== + "@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.5", "@types/estree@^1.0.6", "@types/estree@1.0.8": version "1.0.8" resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz" @@ -888,6 +893,11 @@ devalue@^5.6.4: resolved "https://registry.npmjs.org/devalue/-/devalue-5.6.4.tgz" integrity sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA== +diff@^8.0.3: + version "8.0.3" + resolved "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz" + integrity sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ== + enhanced-resolve@^5.19.0: version "5.20.0" resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz"