From d654aad937c381b35e466fd97ec597f5d4c17890 Mon Sep 17 00:00:00 2001 From: elpatron Date: Wed, 3 Jun 2026 12:42:17 +0200 Subject: [PATCH] Allow crew to read AI summaries without losing them on save. Preserve existing summaries when crew edits entries and show a read-only hint in the editor. Co-authored-by: Cursor --- client/src/components/LogEntryEditor.tsx | 25 ++++++++++++++++++++++-- client/src/i18n/locales/da.json | 1 + client/src/i18n/locales/de.json | 1 + client/src/i18n/locales/en.json | 1 + client/src/i18n/locales/nb.json | 1 + client/src/i18n/locales/sv.json | 1 + 6 files changed, 28 insertions(+), 2 deletions(-) diff --git a/client/src/components/LogEntryEditor.tsx b/client/src/components/LogEntryEditor.tsx index 01ac61b..9e57797 100644 --- a/client/src/components/LogEntryEditor.tsx +++ b/client/src/components/LogEntryEditor.tsx @@ -43,6 +43,7 @@ import { generateTravelDaySummary, TravelDaySummaryApiError } from '../services/aiSummary.js' +import { tryDecryptEntryPayload } from '../services/quickEventLog.js' import { getDecryptedTrack, saveUploadedTrack, @@ -457,13 +458,28 @@ export default function LogEntryEditor({ const eventsOverride = normalized.eventsOverride const skipperToSave = normalized.signSkipper !== undefined ? normalized.signSkipper : signSkipper const crewToSave = normalized.signCrew !== undefined ? normalized.signCrew : signCrew - const summaryToSave = normalized.aiSummary !== undefined ? normalized.aiSummary : aiSummary - const summaryAtToSave = + let summaryToSave = normalized.aiSummary !== undefined ? normalized.aiSummary : aiSummary + let summaryAtToSave = normalized.aiSummaryGeneratedAt !== undefined ? normalized.aiSummaryGeneratedAt : aiSummaryGeneratedAt const masterKey = await getLogbookKey(logbookId) || getActiveMasterKey() if (!masterKey) throw new Error('Encryption key not found. Please log in.') + // Crew edits must not drop the skipper's AI summary when it is not loaded into editor state. + if (!summaryToSave.trim()) { + const local = await db.entries.get(entryId) + if (local) { + const decrypted = await tryDecryptEntryPayload(local, masterKey) + const existing = + decrypted && typeof decrypted.aiSummary === 'string' ? decrypted.aiSummary.trim() : '' + if (existing) { + summaryToSave = existing + summaryAtToSave = + typeof decrypted.aiSummaryGeneratedAt === 'string' ? decrypted.aiSummaryGeneratedAt : '' + } + } + } + const entryData: Record = { ...buildPayloadForSigning(eventsOverride), signSkipper: normalizedSerializedSignature(skipperToSave), @@ -1551,6 +1567,11 @@ export default function LogEntryEditor({

{t('logs.ai_summary_title')}

+ {aiSummary.trim() && !canSignSkipper && ( +

+ {t('logs.ai_summary_read_only')} +

+ )} {aiSummary.trim() ? (

{aiSummary}

) : ( diff --git a/client/src/i18n/locales/da.json b/client/src/i18n/locales/da.json index c81e906..487d58e 100644 --- a/client/src/i18n/locales/da.json +++ b/client/src/i18n/locales/da.json @@ -373,6 +373,7 @@ "export_pdf": "Download PDF.", "exporting_pdf": "PDF er genereret...", "ai_summary_title": "AI-resumé", + "ai_summary_read_only": "Oprettet af skipperen — kun læsning for besætningen.", "ai_summary_empty": "Intet resumé endnu.", "ai_summary_generate": "Generér resumé", "ai_summary_regenerate": "Generér igen", diff --git a/client/src/i18n/locales/de.json b/client/src/i18n/locales/de.json index b316719..c633a90 100644 --- a/client/src/i18n/locales/de.json +++ b/client/src/i18n/locales/de.json @@ -373,6 +373,7 @@ "export_pdf": "PDF herunterladen", "exporting_pdf": "PDF wird generiert...", "ai_summary_title": "KI-Zusammenfassung", + "ai_summary_read_only": "Vom Skipper erstellt — nur lesbar für die Crew.", "ai_summary_empty": "Noch keine Zusammenfassung vorhanden.", "ai_summary_generate": "Zusammenfassung generieren", "ai_summary_regenerate": "Neu generieren", diff --git a/client/src/i18n/locales/en.json b/client/src/i18n/locales/en.json index 020d5d2..aba57f3 100644 --- a/client/src/i18n/locales/en.json +++ b/client/src/i18n/locales/en.json @@ -373,6 +373,7 @@ "export_pdf": "Download PDF", "exporting_pdf": "Generating PDF...", "ai_summary_title": "AI Summary", + "ai_summary_read_only": "Created by the skipper — read-only for crew.", "ai_summary_empty": "No summary yet.", "ai_summary_generate": "Generate summary", "ai_summary_regenerate": "Regenerate", diff --git a/client/src/i18n/locales/nb.json b/client/src/i18n/locales/nb.json index f543dac..ea26ced 100644 --- a/client/src/i18n/locales/nb.json +++ b/client/src/i18n/locales/nb.json @@ -373,6 +373,7 @@ "export_pdf": "Last ned PDF", "exporting_pdf": "PDF genereres...", "ai_summary_title": "AI-sammendrag", + "ai_summary_read_only": "Opprettet av skipperen — kun lesbar for mannskapet.", "ai_summary_empty": "Ingen sammendrag ennå.", "ai_summary_generate": "Generer sammendrag", "ai_summary_regenerate": "Generer på nytt", diff --git a/client/src/i18n/locales/sv.json b/client/src/i18n/locales/sv.json index b4cbe56..580769f 100644 --- a/client/src/i18n/locales/sv.json +++ b/client/src/i18n/locales/sv.json @@ -373,6 +373,7 @@ "export_pdf": "Hämta PDF.", "exporting_pdf": "PDF genereras...", "ai_summary_title": "AI-sammanfattning", + "ai_summary_read_only": "Skapad av skepparen — endast läsning för besättningen.", "ai_summary_empty": "Ingen sammanfattning ännu.", "ai_summary_generate": "Generera sammanfattning", "ai_summary_regenerate": "Generera igen",