Files
lifeos-prod/templates/time_entries.html
Michael 1c2279664c feat: consistent compact density across all list views
- Reduce capture-item, files table, date group labels to 6px 12px padding
- Set font-size 0.80rem on capture-text, file table cells

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 01:54:02 +00:00

156 lines
6.4 KiB
HTML

{% extends "base.html" %}
{% block content %}
<div class="page-header">
<div>
<h1 class="page-title">Time Log</h1>
<div class="text-muted text-sm mt-1">
{{ entries | length }} entries
&middot; {{ (total_minutes / 60) | round(1) }}h total
(last {{ days }} days)
</div>
</div>
</div>
<!-- Running timer banner -->
{% if running %}
<div class="card mb-3" style="border-color: var(--green); background: var(--green-soft);">
<div style="display: flex; align-items: center; gap: 12px;">
<div style="width: 10px; height: 10px; border-radius: 50%; background: var(--green); animation: pulse 1.5s infinite;"></div>
<div style="flex: 1;">
<div style="font-weight: 600; color: var(--text);">
Timer running: {{ running.task_title }}
</div>
{% if running.project_name %}
<div class="text-muted text-sm">{{ running.project_name }}</div>
{% endif %}
</div>
<div style="font-size: 1.25rem; font-weight: 700; font-family: var(--font-mono); color: var(--green);"
id="running-banner-elapsed" data-start="{{ running.start_at.isoformat() }}">
--:--
</div>
<form method="POST" action="/time/stop">
<input type="hidden" name="entry_id" value="{{ running.id }}">
<button type="submit" class="btn btn-danger btn-sm">Stop</button>
</form>
</div>
</div>
{% endif %}
<!-- Filters -->
<div class="filters-bar">
<a href="/time?days=1" class="btn {{ 'btn-primary' if days == 1 else 'btn-secondary' }} btn-sm">Today</a>
<a href="/time?days=7" class="btn {{ 'btn-primary' if days == 7 else 'btn-secondary' }} btn-sm">7 Days</a>
<a href="/time?days=30" class="btn {{ 'btn-primary' if days == 30 else 'btn-secondary' }} btn-sm">30 Days</a>
<a href="/time?days=90" class="btn {{ 'btn-primary' if days == 90 else 'btn-secondary' }} btn-sm">90 Days</a>
</div>
<!-- Daily totals summary -->
{% if daily_totals %}
<div class="card mb-3">
<div class="card-title mb-2">Daily Summary</div>
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
{% for day, mins in daily_totals | dictsort(reverse=true) %}
<div style="text-align: center; padding: 8px 14px; background: var(--surface2); border-radius: var(--radius); min-width: 80px;">
<div style="font-size: 1.1rem; font-weight: 700;">{{ (mins / 60) | round(1) }}h</div>
<div class="text-muted text-xs">{{ day }}</div>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<!-- Time entries -->
{% if entries %}
<div class="card">
{% set current_date = namespace(value='') %}
{% for entry in entries %}
{% set entry_date = entry.start_at.strftime('%A, %B %-d') if entry.start_at else 'Unknown' %}
{% if entry_date != current_date.value %}
<div style="padding: 6px 12px 4px; font-size: 0.78rem; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; {% if not loop.first %}border-top: 1px solid var(--border); margin-top: 4px;{% endif %}">
{{ entry_date }}
</div>
{% set current_date.value = entry_date %}
{% endif %}
<div class="list-row">
{% if entry.end_at is none %}
<div style="width: 10px; height: 10px; border-radius: 50%; background: var(--green); animation: pulse 1.5s infinite; flex-shrink: 0;"></div>
{% endif %}
<div class="row-title">
<a href="/tasks/{{ entry.task_id }}">{{ entry.task_title }}</a>
</div>
{% if entry.project_name %}
<span class="row-tag">{{ entry.project_name }}</span>
{% endif %}
{% if entry.domain_name %}
<span class="row-domain-tag" style="background: {{ entry.domain_color or 'var(--accent)' }}20; color: {{ entry.domain_color or 'var(--accent)' }};">
{{ entry.domain_name }}
</span>
{% endif %}
<span style="font-family: var(--font-mono); font-size: 0.82rem; color: var(--text-secondary); min-width: 50px; text-align: right;">
{% if entry.end_at is none %}
<span style="color: var(--green);">running</span>
{% elif entry.duration_minutes %}
{% if entry.duration_minutes >= 60 %}
{{ (entry.duration_minutes / 60) | int }}h {{ entry.duration_minutes % 60 }}m
{% else %}
{{ entry.duration_minutes }}m
{% endif %}
{% else %}
--
{% endif %}
</span>
<span class="row-meta" style="min-width: 100px; text-align: right;">
{% if entry.start_at %}
{{ entry.start_at.strftime('%-I:%M %p') }}
{% if entry.end_at %}
- {{ entry.end_at.strftime('%-I:%M %p') }}
{% endif %}
{% endif %}
</span>
<div class="row-actions">
<form method="POST" action="/time/{{ entry.id }}/delete" data-confirm="Delete this time entry?">
<button type="submit" class="btn btn-ghost btn-xs" style="color: var(--red);">Delete</button>
</form>
</div>
</div>
{% endfor %}
</div>
{% else %}
<div class="empty-state">
<div class="empty-state-icon">&#9201;</div>
<div class="empty-state-text">No time entries in the last {{ days }} days</div>
<div class="text-muted text-sm">Start a timer from any task to begin tracking time</div>
</div>
{% endif %}
<style>
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
</style>
<script>
// Update running banner elapsed time
(function() {
const el = document.getElementById('running-banner-elapsed');
if (!el) return;
const startAt = new Date(el.dataset.start);
function update() {
const now = new Date();
const secs = Math.floor((now - startAt) / 1000);
const h = Math.floor(secs / 3600);
const m = Math.floor((secs % 3600) / 60);
const s = secs % 60;
el.textContent = (h > 0 ? h + ':' : '') +
String(m).padStart(2, '0') + ':' +
String(s).padStart(2, '0');
}
update();
setInterval(update, 1000);
})();
</script>
{% endblock %}