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)}")