// assets/scripts/entries.js import { parseAndValidate, initDurationBlurHandler } from './duration.js'; import { esc, createTranslator, ANIMATION_MS, FADE_MS, MINUTES_PER_DAY, removeWithAnimation, animateIn } from './utils.js'; const LAST_PROJECT_KEY = 'tt_last_project_id'; const LAST_SERVICE_KEY = 'tt_last_service_id'; const NOTE_KEY = 'tt_minimal_note_open'; const LOCK_SVG = ``; const t = createTranslator('TT'); // ── Select-Builder ─────────────────────────────────────────────────────────── function buildProjectOptions(selectedId = null) { const groups = {}; (window.TT?.projects ?? []).forEach(p => { if (!groups[p.clientName]) groups[p.clientName] = []; groups[p.clientName].push(p); }); let html = ``; for (const [client, projects] of Object.entries(groups)) { html += `'; } return html; } function buildServiceOptions(selectedId = null) { const billable = (window.TT?.services ?? []).filter(s => s.billable); const notBillable = (window.TT?.services ?? []).filter(s => !s.billable); let html = ``; const addGroup = (label, list) => { if (!list.length) return; html += `'; }; addGroup(t('billable'), billable); addGroup(t('notBillable'), notBillable); return html; } // ── Row HTML ───────────────────────────────────────────────────────────────── function buildEntryRowHTML(entry, animate = false) { const servicePart = entry.serviceName ? ` / ${esc(entry.serviceName)}` : ''; const notePart = entry.note ? `
${t('noEntries')}
${t('noEntries')}