import re with open("app/modules/sag/templates/detail.html", "r", encoding="utf-8") as f: text = f.read() old_css_pattern = r"\.time-v1-track \{.*?\n \}" new_css = """ .time-v1-global-timeline { position: relative; padding-left: 2rem; margin-bottom: 2rem; } .time-v1-global-timeline::before { content: ''; position: absolute; top: 0; bottom: 0; left: 0.75rem; width: 2px; background-color: var(--accent, #0f4c75); opacity: 0.2; } .time-v1-date-node { position: relative; margin-bottom: 1.5rem; } .time-v1-date-badge { display: inline-block; background-color: var(--accent, #0f4c75); color: #fff; padding: 0.25rem 0.75rem; border-radius: 1rem; font-size: 0.85rem; font-weight: 600; margin-bottom: 1rem; margin-left: -2.5rem; position: relative; z-index: 1; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .time-v1-item { position: relative; background: #fff; border-radius: 0.5rem; padding: 1rem; margin-bottom: 0.75rem; box-shadow: 0 1px 3px rgba(0,0,0,0.05); border: 1px solid rgba(0,0,0,0.05); transition: all 0.2s ease; } .time-v1-item::before { content: ''; position: absolute; top: 1.5rem; left: -2rem; width: 1rem; height: 2px; background-color: var(--accent, #0f4c75); opacity: 0.2; } .time-v1-item:hover { box-shadow: 0 4px 6px rgba(0,0,0,0.1); transform: translateY(-2px); } .time-v1-avatar { width: 2.5rem; height: 2.5rem; border-radius: 50%; background-color: color-mix(in srgb, var(--accent, #0f4c75) 10%, white); color: var(--accent, #0f4c75); display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 0.9rem; flex-shrink: 0; border: 2px solid white; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } """ new_js = """function renderTimeV1Timeline(entries) { const timeline = document.getElementById('timeV1Timeline'); if (!timeline) return; if (!entries || entries.length === 0) { timeline.innerHTML = '
Ingen tidsregistreringer endnu
'; return; } // Saml og sortér alle tidsregistreringer efter dato, nyeste først const sortedEntries = [...entries].sort((a, b) => { const dateA = new Date(a.worked_date || a.start_tid || 0); const dateB = new Date(b.worked_date || b.start_tid || 0); return dateB - dateA; }); // Gruppér efter formatert dato const groupedByDate = {}; sortedEntries.forEach((entry) => { const rawDate = new Date(entry.worked_date || entry.start_tid || 0); const dateKey = !isNaN(rawDate.getTime()) ? rawDate.toLocaleDateString('da-DK', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' }) : 'Ukendt dato'; if (!groupedByDate[dateKey]) groupedByDate[dateKey] = []; groupedByDate[dateKey].push(entry); }); // Byg HTML for den overordnede tidslinje let html = '
'; Object.entries(groupedByDate).forEach(([dateLabel, dateEntries]) => { // Konverter det første bogstav i dato-strengen til stort const formattedDateLab = dateLabel.charAt(0).toUpperCase() + dateLabel.slice(1); html += `
${formattedDateLab}
`; dateEntries.forEach(entry => { const desc = escapeHtml(entry.beskrivelse || 'Ingen beskrivelse'); const userName = escapeHtml(entry.bruger_navn || 'Ukendt'); // Lav initialer til Avatar const initials = userName.split(' ').map(n => n[0]).join('').slice(0, 2).toUpperCase() || '?'; // Formatér tid let timeOutput = '0 t'; let isRunning = false; let clockClass = "text-muted"; if (entry.kilde === 'live' && !entry.faktisk_tid_min && !entry.stop_tid) { timeOutput = 'Kører...'; isRunning = true; clockClass = "text-success fw-bold"; } else if (entry.is_running) { timeOutput = 'Kører...'; isRunning = true; clockClass = "text-success fw-bold"; } else if (entry.faktisk_tid_min !== null && entry.faktisk_tid_min !== undefined) { const h = Math.floor(entry.faktisk_tid_min / 60); const m = Math.floor(entry.faktisk_tid_min % 60); timeOutput = `${h}t ${m}m`; } else { // Reservere for original_hours fallback const origHours = parseFloat(entry.original_hours || 0); const h = Math.floor(origHours); const m = Math.round((origHours - h) * 60); timeOutput = `${h}t ${m}m`; } // Tjek synlighed for kunden (intern markering) const isInternal = entry.is_internal ? true : false; const internalBadge = isInternal ? ` Intern ` : ''; html += `
${initials}
${userName}
${timeOutput} ${entry.entry_type ? ` · ${escapeHtml(entry.entry_type)}` : ''}
${internalBadge}
${desc}
`; }); html += `
`; // Luk time-v1-date-node }); html += '
'; // Luk time-v1-global-timeline timeline.innerHTML = html; }""" old_js_pattern = r'function renderTimeV1Timeline\(entries\).*?\n }' orig_text_len = len(text) import sys if re.search(old_css_pattern, text, re.DOTALL): text = re.sub(old_css_pattern, new_css.strip(), text, flags=re.DOTALL) else: print("Could NOT find old CSS!") if re.search(old_js_pattern, text, re.DOTALL): text = re.sub(old_js_pattern, new_js.strip(), text, flags=re.DOTALL) else: print("Could NOT find old JS!") with open("app/modules/sag/templates/detail.html", "w", encoding="utf-8") as f: f.write(text) print(f"Replacement complete! Original length {orig_text_len}, new length {len(text)}")