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 <cursoragent@cursor.com>
This commit is contained in:
@@ -43,6 +43,7 @@ import {
|
|||||||
generateTravelDaySummary,
|
generateTravelDaySummary,
|
||||||
TravelDaySummaryApiError
|
TravelDaySummaryApiError
|
||||||
} from '../services/aiSummary.js'
|
} from '../services/aiSummary.js'
|
||||||
|
import { tryDecryptEntryPayload } from '../services/quickEventLog.js'
|
||||||
import {
|
import {
|
||||||
getDecryptedTrack,
|
getDecryptedTrack,
|
||||||
saveUploadedTrack,
|
saveUploadedTrack,
|
||||||
@@ -457,13 +458,28 @@ export default function LogEntryEditor({
|
|||||||
const eventsOverride = normalized.eventsOverride
|
const eventsOverride = normalized.eventsOverride
|
||||||
const skipperToSave = normalized.signSkipper !== undefined ? normalized.signSkipper : signSkipper
|
const skipperToSave = normalized.signSkipper !== undefined ? normalized.signSkipper : signSkipper
|
||||||
const crewToSave = normalized.signCrew !== undefined ? normalized.signCrew : signCrew
|
const crewToSave = normalized.signCrew !== undefined ? normalized.signCrew : signCrew
|
||||||
const summaryToSave = normalized.aiSummary !== undefined ? normalized.aiSummary : aiSummary
|
let summaryToSave = normalized.aiSummary !== undefined ? normalized.aiSummary : aiSummary
|
||||||
const summaryAtToSave =
|
let summaryAtToSave =
|
||||||
normalized.aiSummaryGeneratedAt !== undefined ? normalized.aiSummaryGeneratedAt : aiSummaryGeneratedAt
|
normalized.aiSummaryGeneratedAt !== undefined ? normalized.aiSummaryGeneratedAt : aiSummaryGeneratedAt
|
||||||
|
|
||||||
const masterKey = await getLogbookKey(logbookId) || getActiveMasterKey()
|
const masterKey = await getLogbookKey(logbookId) || getActiveMasterKey()
|
||||||
if (!masterKey) throw new Error('Encryption key not found. Please log in.')
|
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<string, unknown> = {
|
const entryData: Record<string, unknown> = {
|
||||||
...buildPayloadForSigning(eventsOverride),
|
...buildPayloadForSigning(eventsOverride),
|
||||||
signSkipper: normalizedSerializedSignature(skipperToSave),
|
signSkipper: normalizedSerializedSignature(skipperToSave),
|
||||||
@@ -1551,6 +1567,11 @@ export default function LogEntryEditor({
|
|||||||
<Sparkles size={20} className="form-icon" />
|
<Sparkles size={20} className="form-icon" />
|
||||||
<h3>{t('logs.ai_summary_title')}</h3>
|
<h3>{t('logs.ai_summary_title')}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
{aiSummary.trim() && !canSignSkipper && (
|
||||||
|
<p style={{ margin: '0 0 12px', fontSize: '0.9rem', opacity: 0.8 }}>
|
||||||
|
{t('logs.ai_summary_read_only')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
{aiSummary.trim() ? (
|
{aiSummary.trim() ? (
|
||||||
<p style={{ whiteSpace: 'pre-wrap', margin: '0 0 16px', lineHeight: 1.5 }}>{aiSummary}</p>
|
<p style={{ whiteSpace: 'pre-wrap', margin: '0 0 16px', lineHeight: 1.5 }}>{aiSummary}</p>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -373,6 +373,7 @@
|
|||||||
"export_pdf": "Download PDF.",
|
"export_pdf": "Download PDF.",
|
||||||
"exporting_pdf": "PDF er genereret...",
|
"exporting_pdf": "PDF er genereret...",
|
||||||
"ai_summary_title": "AI-resumé",
|
"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_empty": "Intet resumé endnu.",
|
||||||
"ai_summary_generate": "Generér resumé",
|
"ai_summary_generate": "Generér resumé",
|
||||||
"ai_summary_regenerate": "Generér igen",
|
"ai_summary_regenerate": "Generér igen",
|
||||||
|
|||||||
@@ -373,6 +373,7 @@
|
|||||||
"export_pdf": "PDF herunterladen",
|
"export_pdf": "PDF herunterladen",
|
||||||
"exporting_pdf": "PDF wird generiert...",
|
"exporting_pdf": "PDF wird generiert...",
|
||||||
"ai_summary_title": "KI-Zusammenfassung",
|
"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_empty": "Noch keine Zusammenfassung vorhanden.",
|
||||||
"ai_summary_generate": "Zusammenfassung generieren",
|
"ai_summary_generate": "Zusammenfassung generieren",
|
||||||
"ai_summary_regenerate": "Neu generieren",
|
"ai_summary_regenerate": "Neu generieren",
|
||||||
|
|||||||
@@ -373,6 +373,7 @@
|
|||||||
"export_pdf": "Download PDF",
|
"export_pdf": "Download PDF",
|
||||||
"exporting_pdf": "Generating PDF...",
|
"exporting_pdf": "Generating PDF...",
|
||||||
"ai_summary_title": "AI Summary",
|
"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_empty": "No summary yet.",
|
||||||
"ai_summary_generate": "Generate summary",
|
"ai_summary_generate": "Generate summary",
|
||||||
"ai_summary_regenerate": "Regenerate",
|
"ai_summary_regenerate": "Regenerate",
|
||||||
|
|||||||
@@ -373,6 +373,7 @@
|
|||||||
"export_pdf": "Last ned PDF",
|
"export_pdf": "Last ned PDF",
|
||||||
"exporting_pdf": "PDF genereres...",
|
"exporting_pdf": "PDF genereres...",
|
||||||
"ai_summary_title": "AI-sammendrag",
|
"ai_summary_title": "AI-sammendrag",
|
||||||
|
"ai_summary_read_only": "Opprettet av skipperen — kun lesbar for mannskapet.",
|
||||||
"ai_summary_empty": "Ingen sammendrag ennå.",
|
"ai_summary_empty": "Ingen sammendrag ennå.",
|
||||||
"ai_summary_generate": "Generer sammendrag",
|
"ai_summary_generate": "Generer sammendrag",
|
||||||
"ai_summary_regenerate": "Generer på nytt",
|
"ai_summary_regenerate": "Generer på nytt",
|
||||||
|
|||||||
@@ -373,6 +373,7 @@
|
|||||||
"export_pdf": "Hämta PDF.",
|
"export_pdf": "Hämta PDF.",
|
||||||
"exporting_pdf": "PDF genereras...",
|
"exporting_pdf": "PDF genereras...",
|
||||||
"ai_summary_title": "AI-sammanfattning",
|
"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_empty": "Ingen sammanfattning ännu.",
|
||||||
"ai_summary_generate": "Generera sammanfattning",
|
"ai_summary_generate": "Generera sammanfattning",
|
||||||
"ai_summary_regenerate": "Generera igen",
|
"ai_summary_regenerate": "Generera igen",
|
||||||
|
|||||||
Reference in New Issue
Block a user