fix(logs): Ereignisprotokoll chronologisch nach Uhrzeit sortieren
Einträge werden beim Laden, Speichern und Export älteste-oben angezeigt (sortLogEventsByTime). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -20,7 +20,7 @@ import {
|
|||||||
hasAnySignature
|
hasAnySignature
|
||||||
} from '../utils/signatures.js'
|
} from '../utils/signatures.js'
|
||||||
import type { SignatureValue } from '../types/signatures.js'
|
import type { SignatureValue } from '../types/signatures.js'
|
||||||
import { buildLogEntryPayload, type LogEventPayload } from '../utils/logEntryPayload.js'
|
import { buildLogEntryPayload, sortLogEventsByTime, type LogEventPayload } from '../utils/logEntryPayload.js'
|
||||||
import { hashEntryForSigning } from '../utils/entryCanonicalHash.js'
|
import { hashEntryForSigning } from '../utils/entryCanonicalHash.js'
|
||||||
import { signLogEntry } from '../services/entrySigning.js'
|
import { signLogEntry } from '../services/entrySigning.js'
|
||||||
import { getLogbookAccess } from '../services/logbookAccess.js'
|
import { getLogbookAccess } from '../services/logbookAccess.js'
|
||||||
@@ -483,7 +483,7 @@ export default function LogEntryEditor({
|
|||||||
setSignSkipper(normalizeSignature(preloadedEntry.signSkipper) || '')
|
setSignSkipper(normalizeSignature(preloadedEntry.signSkipper) || '')
|
||||||
setSignCrew(normalizeSignature(preloadedEntry.signCrew) || '')
|
setSignCrew(normalizeSignature(preloadedEntry.signCrew) || '')
|
||||||
loadTrackStatsFromEntry(preloadedEntry)
|
loadTrackStatsFromEntry(preloadedEntry)
|
||||||
setEvents(preloadedEntry.events || [])
|
setEvents(sortLogEventsByTime(preloadedEntry.events || []))
|
||||||
setSavedFingerprint(fingerprintFromStoredEntry(preloadedEntry))
|
setSavedFingerprint(fingerprintFromStoredEntry(preloadedEntry))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -516,7 +516,7 @@ export default function LogEntryEditor({
|
|||||||
setSignSkipper(normalizeSignature(decrypted.signSkipper) || '')
|
setSignSkipper(normalizeSignature(decrypted.signSkipper) || '')
|
||||||
setSignCrew(normalizeSignature(decrypted.signCrew) || '')
|
setSignCrew(normalizeSignature(decrypted.signCrew) || '')
|
||||||
loadTrackStatsFromEntry(decrypted)
|
loadTrackStatsFromEntry(decrypted)
|
||||||
setEvents(decrypted.events || [])
|
setEvents(sortLogEventsByTime(decrypted.events || []))
|
||||||
setSavedFingerprint(fingerprintFromStoredEntry(decrypted))
|
setSavedFingerprint(fingerprintFromStoredEntry(decrypted))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -871,7 +871,7 @@ export default function LogEntryEditor({
|
|||||||
if (editingEventIndex !== null) {
|
if (editingEventIndex !== null) {
|
||||||
const hadSkipperSignature = !!signSkipper
|
const hadSkipperSignature = !!signSkipper
|
||||||
markSkipperSignatureClearedForEventChange()
|
markSkipperSignatureClearedForEventChange()
|
||||||
nextEvents = events.map((ev, idx) => (idx === editingEventIndex ? eventData : ev))
|
nextEvents = sortLogEventsByTime(events.map((ev, idx) => (idx === editingEventIndex ? eventData : ev)))
|
||||||
if (hadSkipperSignature) {
|
if (hadSkipperSignature) {
|
||||||
void showAlertRef.current(
|
void showAlertRef.current(
|
||||||
t('logs.sign_cleared_skipper_re_sign'),
|
t('logs.sign_cleared_skipper_re_sign'),
|
||||||
@@ -879,7 +879,7 @@ export default function LogEntryEditor({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nextEvents = [...events, eventData]
|
nextEvents = sortLogEventsByTime([...events, eventData])
|
||||||
}
|
}
|
||||||
|
|
||||||
setEvents(nextEvents)
|
setEvents(nextEvents)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { getActiveMasterKey } from './auth.js'
|
|||||||
import { getLogbookKey } from './logbookKeys.js'
|
import { getLogbookKey } from './logbookKeys.js'
|
||||||
import { decryptJson } from './crypto.js'
|
import { decryptJson } from './crypto.js'
|
||||||
import { formatSignatureForExport, normalizeSignature } from '../utils/signatures.js'
|
import { formatSignatureForExport, normalizeSignature } from '../utils/signatures.js'
|
||||||
|
import { sortLogEventsByTime } from '../utils/logEntryPayload.js'
|
||||||
import i18n from '../i18n/index.js'
|
import i18n from '../i18n/index.js'
|
||||||
|
|
||||||
function escapeCsvValue(val: string | number | undefined | null): string {
|
function escapeCsvValue(val: string | number | undefined | null): string {
|
||||||
@@ -134,7 +135,7 @@ export async function exportLogbookToCsv(logbookId: string, preloadedData?: { ya
|
|||||||
].map(escapeCsvValue));
|
].map(escapeCsvValue));
|
||||||
} else {
|
} else {
|
||||||
// Sort events chronologically by time
|
// Sort events chronologically by time
|
||||||
const sortedEvents = [...eventsList].sort((a, b) => (a.time || '').localeCompare(b.time || ''));
|
const sortedEvents = sortLogEventsByTime(eventsList);
|
||||||
for (const ev of sortedEvents) {
|
for (const ev of sortedEvents) {
|
||||||
rows.push([
|
rows.push([
|
||||||
dateVal, travelDay, dep, dest,
|
dateVal, travelDay, dep, dest,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { getActiveMasterKey } from './auth.js'
|
|||||||
import { getLogbookKey } from './logbookKeys.js'
|
import { getLogbookKey } from './logbookKeys.js'
|
||||||
import { decryptJson } from './crypto.js'
|
import { decryptJson } from './crypto.js'
|
||||||
import { isSignatureImage, isPasskeySignature } from '../utils/signatures.js'
|
import { isSignatureImage, isPasskeySignature } from '../utils/signatures.js'
|
||||||
|
import { sortLogEventsByTime } from '../utils/logEntryPayload.js'
|
||||||
import i18n from '../i18n/index.js'
|
import i18n from '../i18n/index.js'
|
||||||
|
|
||||||
function formatPasskeySignDate(signedAt: string): string {
|
function formatPasskeySignDate(signedAt: string): string {
|
||||||
@@ -132,7 +133,7 @@ export async function generateLogbookPagePdf(logbookId: string, entryId: string,
|
|||||||
// Draw Data Rows
|
// Draw Data Rows
|
||||||
const events = entry.events || [];
|
const events = entry.events || [];
|
||||||
const maxRows = 16;
|
const maxRows = 16;
|
||||||
const sortedEvents = [...events].sort((a: any, b: any) => (a.time || '').localeCompare(b.time || ''));
|
const sortedEvents = sortLogEventsByTime(events);
|
||||||
|
|
||||||
doc.setFont('Helvetica', 'normal');
|
doc.setFont('Helvetica', 'normal');
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ export interface LogEventPayload {
|
|||||||
remarks: string
|
remarks: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Chronological order: earliest time first (HH:MM). */
|
||||||
|
export function sortLogEventsByTime<T extends Pick<LogEventPayload, 'time'>>(events: T[]): T[] {
|
||||||
|
return [...events].sort((a, b) => (a.time || '').localeCompare(b.time || ''))
|
||||||
|
}
|
||||||
|
|
||||||
export interface LogEntryPayloadInput {
|
export interface LogEntryPayloadInput {
|
||||||
date: string
|
date: string
|
||||||
dayOfTravel: string
|
dayOfTravel: string
|
||||||
@@ -38,7 +43,7 @@ export function buildLogEntryPayload(input: LogEntryPayloadInput): Record<string
|
|||||||
destination: input.destination.trim(),
|
destination: input.destination.trim(),
|
||||||
freshwater: { ...input.freshwater },
|
freshwater: { ...input.freshwater },
|
||||||
fuel: { ...input.fuel },
|
fuel: { ...input.fuel },
|
||||||
events: input.events.map((e) => ({ ...e }))
|
events: sortLogEventsByTime(input.events.map((e) => ({ ...e })))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.trackDistanceNm !== undefined) payload.trackDistanceNm = input.trackDistanceNm
|
if (input.trackDistanceNm !== undefined) payload.trackDistanceNm = input.trackDistanceNm
|
||||||
|
|||||||
Reference in New Issue
Block a user