// A single entry's read-only row in the day editor's list: icon, title, // price, sub-line (type/time/flight-chain/location), span text for multi-day // entries, flight-segment/rental detail lines, cost line, and edit/delete // actions. Split out of dayEditor.js to keep it under the repo's 500-line cap. import { el } from '../dom.js'; import { typeInfo, formatDate, formatTimeRange, formatMoney, flightChain, hasSegments, hasRental, isMultiDay, entrySpanDays, splitModeLabel, } from '../format.js'; import { renderSegmentLines } from './segments.js'; import { renderRentalLine } from './rental.js'; // Returns { node, handle } — handle is the drag grip (null unless reorderable). export function renderEntryRow(entry, { reorderable, currency, memberName, onEdit, onDelete }) { const info = typeInfo(entry.type); const time = formatTimeRange(entry.start_time, entry.end_time); const hasPrice = entry.price != null; const flight = hasSegments(entry); const handle = reorderable ? el('span', { class: 'entry-drag-handle', title: 'Drag to reorder', 'aria-hidden': 'true' }, '⋮⋮') : null; const node = el( 'div', { class: 'entry-row', style: { '--chip': info.color } }, handle, el('span', { class: 'entry-icon' }, info.icon), el( 'div', { class: 'entry-body' }, el( 'div', { class: 'entry-title-row' }, el('span', { class: 'entry-title' }, entry.title), hasPrice ? el('span', { class: 'entry-price' }, formatMoney(entry.price, currency, { compact: true })) : null, ), el( 'div', { class: 'entry-sub muted' }, info.label, time ? ` · ${time}` : '', flight ? ` · ✈️ ${flightChain(entry.segments)}` : '', !flight && entry.location_name ? ` · 📍 ${entry.location_name}` : '', entry.auto_ref ? ' · ↻ auto' : '', ), isMultiDay(entry) ? el('div', { class: 'entry-span muted' }, spanText(entry)) : null, flight ? renderSegmentLines(entry.segments) : null, hasRental(entry) ? renderRentalLine(entry.rental) : null, hasPrice ? el( 'div', { class: 'entry-cost muted' }, `💰 ${splitModeLabel(entry.split_mode)}`, entry.paid_by != null ? ` · paid by ${memberName(entry.paid_by)}` : ' · no payer set', ) : null, entry.details ? el('div', { class: 'entry-details' }, entry.details) : null, ), el( 'div', { class: 'entry-actions' }, el('button', { class: 'icon-btn', title: 'Edit', onClick: () => onEdit(entry) }, '✎'), el('button', { class: 'icon-btn danger', title: 'Delete', onClick: () => onDelete(entry) }, '🗑'), ), ); return { node, handle }; } // "until 8 Aug · 3 days" for stays; "5 Aug 20:50 → 7 Aug 06:30" otherwise. function spanText(entry) { if (entry.type === 'stay') { return `until ${formatDate(entry.end_date)} · ${entrySpanDays(entry)} days`; } const a = `${formatDate(entry.date)}${entry.start_time ? ` ${entry.start_time}` : ''}`; const b = `${formatDate(entry.end_date)}${entry.end_time ? ` ${entry.end_time}` : ''}`; return `${a} → ${b}`; }