feat(logbook): attribute log events to creator and show in exports
This commit is contained in:
@@ -77,7 +77,7 @@ export async function exportLogbookToCsv(logbookId: string, preloadedData?: { ya
|
||||
'Date', 'Day of Travel', 'Departure Port', 'Destination Port', 'AI Summary',
|
||||
'Skipper Signature', 'Crew Signature',
|
||||
'Track Distance (nm)', 'Track Max Speed (kn)', 'Track Avg Speed (kn)', 'Motor Hours (h)',
|
||||
'Event Time', 'MgK Course', 'RwK Course',
|
||||
'Event Time', 'Event Creator', 'MgK Course', 'RwK Course',
|
||||
'Wind Dir', 'Wind Str', 'Barometer (hPa)', 'Sea State', 'Visibility',
|
||||
'Current', 'Heel Angle', 'Sails/Motor', 'Log (nm)', 'Distance (nm)',
|
||||
'Latitude', 'Longitude', 'Remarks',
|
||||
@@ -122,6 +122,7 @@ export async function exportLogbookToCsv(logbookId: string, preloadedData?: { ya
|
||||
const greywaterLevel = entry.greywater?.level ?? '';
|
||||
const aiSummary = entry.aiSummary ?? '';
|
||||
|
||||
const crewSnapshots = (entry.crewSnapshotsById as Record<string, any>) || {};
|
||||
const eventsList = entry.events || [];
|
||||
if (eventsList.length === 0) {
|
||||
// Create one row even if there are no events for the day
|
||||
@@ -129,7 +130,7 @@ export async function exportLogbookToCsv(logbookId: string, preloadedData?: { ya
|
||||
dateVal, travelDay, dep, dest, aiSummary,
|
||||
signS, signC,
|
||||
trackDist, trackMax, trackAvg, motorH,
|
||||
'', '', '',
|
||||
'', '', '', '',
|
||||
'', '', '', '', '',
|
||||
'', '', '', '', '',
|
||||
'', '', '',
|
||||
@@ -142,11 +143,21 @@ export async function exportLogbookToCsv(logbookId: string, preloadedData?: { ya
|
||||
// Sort events chronologically by time
|
||||
const sortedEvents = sortLogEventsByTime(eventsList);
|
||||
for (const ev of sortedEvents) {
|
||||
const creatorSnap = ev.creatorId ? crewSnapshots[ev.creatorId] : null;
|
||||
let creatorName = '';
|
||||
if (creatorSnap) {
|
||||
creatorName = creatorSnap.name || '';
|
||||
} else if (ev.creatorId === 'skipper') {
|
||||
creatorName = 'Skipper';
|
||||
} else if (ev.creatorId) {
|
||||
creatorName = ev.creatorId;
|
||||
}
|
||||
|
||||
rows.push([
|
||||
dateVal, travelDay, dep, dest, aiSummary,
|
||||
signS, signC,
|
||||
trackDist, trackMax, trackAvg, motorH,
|
||||
ev.time || '', ev.mgk || '', ev.rwk || '',
|
||||
ev.time || '', creatorName, ev.mgk || '', ev.rwk || '',
|
||||
ev.windDirection || '', ev.windStrength || '', ev.windPressure || '', ev.seaState || '',
|
||||
ev.visibility || '',
|
||||
ev.current || '', ev.heel || '', ev.sailsOrMotor || '', ev.logReading || '', ev.distance || '',
|
||||
|
||||
@@ -13,12 +13,13 @@ function formatPasskeySignDate(signedAt: string): string {
|
||||
}
|
||||
|
||||
export async function generateLogbookPagePdf(logbookId: string, entryId: string, preloadedData?: { yacht: any; entry: any }): Promise<jsPDF> {
|
||||
let yachtName = '', homePort = '', registration = '', callsign = '', atis = '', mmsi = '';
|
||||
let yachtName = '', owner = '', homePort = '', registration = '', callsign = '', atis = '', mmsi = '';
|
||||
let entry: any = null;
|
||||
|
||||
if (preloadedData) {
|
||||
const yacht = preloadedData.yacht || {};
|
||||
yachtName = yacht.name || '';
|
||||
owner = yacht.owner || '';
|
||||
homePort = yacht.port || '';
|
||||
registration = yacht.registrationNumber || yacht.registration || '';
|
||||
callsign = yacht.callSign || '';
|
||||
@@ -35,6 +36,7 @@ export async function generateLogbookPagePdf(logbookId: string, entryId: string,
|
||||
const yacht = await resolveVesselForLogbook(logbookId)
|
||||
if (yacht) {
|
||||
yachtName = yacht.name || ''
|
||||
owner = yacht.owner || ''
|
||||
homePort = yacht.homePort || ''
|
||||
registration = yacht.registrationNumber || ''
|
||||
callsign = yacht.callSign || ''
|
||||
@@ -74,24 +76,56 @@ export async function generateLogbookPagePdf(logbookId: string, entryId: string,
|
||||
doc.setFontSize(8.5);
|
||||
doc.setFont('Helvetica', 'normal');
|
||||
doc.text(`Yachtname: ${yachtName || '—'}`, 10, 21);
|
||||
doc.text(`Heimathafen: ${homePort || '—'}`, 60, 21);
|
||||
doc.text(`Kennzeichen: ${registration || '—'}`, 110, 21);
|
||||
doc.text(`Rufzeichen: ${callsign || '—'}`, 160, 21);
|
||||
doc.text(`ATIS: ${atis || '—'}`, 210, 21);
|
||||
doc.text(`MMSI: ${mmsi || '—'}`, 250, 21);
|
||||
doc.text(`Eigner: ${owner || '—'}`, 55, 21);
|
||||
doc.text(`Heimathafen: ${homePort || '—'}`, 100, 21);
|
||||
doc.text(`Kennzeichen: ${registration || '—'}`, 145, 21);
|
||||
doc.text(`Rufzeichen: ${callsign || '—'}`, 190, 21);
|
||||
doc.text(`ATIS: ${atis || '—'}`, 230, 21);
|
||||
doc.text(`MMSI: ${mmsi || '—'}`, 260, 21);
|
||||
|
||||
doc.text(`Datum: ${entry.date || '—'}`, 10, 23);
|
||||
doc.text(`Reisetag: ${entry.dayOfTravel || '—'}`, 60, 23);
|
||||
doc.text(`Reise von (Departure): ${entry.departure || '—'}`, 110, 23);
|
||||
doc.text(`nach (Destination): ${entry.destination || '—'}`, 200, 23);
|
||||
doc.text(`Datum: ${entry.date || '—'}`, 10, 24);
|
||||
doc.text(`Reisetag: ${entry.dayOfTravel || '—'}`, 60, 24);
|
||||
doc.text(`Reise von (Departure): ${entry.departure || '—'}`, 110, 24);
|
||||
doc.text(`nach (Destination): ${entry.destination || '—'}`, 200, 24);
|
||||
|
||||
// Format Crew names with initials
|
||||
const crewSnapshots = (entry.crewSnapshotsById as Record<string, any>) || {}
|
||||
const crewList: string[] = []
|
||||
|
||||
if (entry.selectedSkipperId && crewSnapshots[entry.selectedSkipperId]) {
|
||||
const name = crewSnapshots[entry.selectedSkipperId].name || 'Skipper'
|
||||
const initial = name.trim().split(/\s+/)[0]?.charAt(0).toUpperCase() || 'S'
|
||||
crewList.push(`${name} [${initial}] (Skipper)`)
|
||||
} else if (crewSnapshots['skipper']) {
|
||||
const name = crewSnapshots['skipper'].name || 'Skipper'
|
||||
crewList.push(`${name} [S] (Skipper)`)
|
||||
}
|
||||
|
||||
if (Array.isArray(entry.selectedCrewIds)) {
|
||||
for (const crewId of entry.selectedCrewIds) {
|
||||
const snap = crewSnapshots[crewId]
|
||||
if (snap) {
|
||||
const name = snap.name || ''
|
||||
const initial = name.trim().split(/\s+/)[0]?.charAt(0).toUpperCase() || '?'
|
||||
crewList.push(`${name} [${initial}]`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const crewText = crewList.length > 0 ? `Besatzung (Crew): ${crewList.join(', ')}` : ''
|
||||
|
||||
doc.setFont('Helvetica', 'normal');
|
||||
if (entry.trackDistanceNm) {
|
||||
doc.setFont('Helvetica', 'normal');
|
||||
doc.text(
|
||||
`GPS-Track: ${entry.trackDistanceNm} sm · max. ${entry.trackSpeedMaxKn ?? '—'} kn · Ø ${entry.trackSpeedAvgKn ?? '—'} kn`,
|
||||
10,
|
||||
27
|
||||
);
|
||||
if (crewText) {
|
||||
doc.text(crewText, 140, 27);
|
||||
}
|
||||
} else if (crewText) {
|
||||
doc.text(crewText, 10, 27);
|
||||
}
|
||||
|
||||
// Divider line
|
||||
@@ -175,8 +209,28 @@ export async function generateLogbookPagePdf(logbookId: string, entryId: string,
|
||||
doc.text(gps, writeX + 1, y + 4.2);
|
||||
writeX += colWidths[11];
|
||||
|
||||
const crewSnapshots = (entry.crewSnapshotsById as Record<string, any>) || {};
|
||||
let initial = '';
|
||||
if (ev.creatorId) {
|
||||
const snap = crewSnapshots[ev.creatorId];
|
||||
let name = '';
|
||||
if (snap) {
|
||||
name = snap.name || '';
|
||||
} else if (ev.creatorId === 'skipper') {
|
||||
name = 'Skipper';
|
||||
} else {
|
||||
name = ev.creatorId;
|
||||
}
|
||||
if (name) {
|
||||
initial = name.trim().split(/\s+/)[0]?.charAt(0).toUpperCase() || '';
|
||||
}
|
||||
}
|
||||
|
||||
// Clip remarks to fit within the 94mm bounds
|
||||
const remarks = ev.remarks || '';
|
||||
let remarks = ev.remarks || '';
|
||||
if (initial) {
|
||||
remarks = `[${initial}] ${remarks}`;
|
||||
}
|
||||
const maxChars = 65;
|
||||
const clippedRemarks = remarks.length > maxChars ? remarks.substring(0, maxChars) + '...' : remarks;
|
||||
doc.text(clippedRemarks, writeX + 1, y + 4.2);
|
||||
|
||||
@@ -97,6 +97,14 @@ function buildEncryptedPayload(
|
||||
consumption: fuel.consumption ?? 0
|
||||
}
|
||||
|
||||
const entryCrew = data.selectedSkipperId
|
||||
? {
|
||||
selectedSkipperId: String(data.selectedSkipperId),
|
||||
selectedCrewIds: Array.isArray(data.selectedCrewIds) ? data.selectedCrewIds.map(String) : [],
|
||||
crewSnapshotsById: (data.crewSnapshotsById as Record<string, any>) || {}
|
||||
}
|
||||
: undefined
|
||||
|
||||
const payload = buildLogEntryPayload({
|
||||
date: String(data.date || ''),
|
||||
dayOfTravel: String(data.dayOfTravel || ''),
|
||||
@@ -121,7 +129,8 @@ function buildEncryptedPayload(
|
||||
motorHoursRaw != null && motorHoursRaw !== ''
|
||||
? parseFloat(String(motorHoursRaw))
|
||||
: undefined,
|
||||
events: options.events
|
||||
events: options.events,
|
||||
entryCrew
|
||||
})
|
||||
|
||||
const clear = options.clearSignatures
|
||||
|
||||
Reference in New Issue
Block a user