Files
OpenTimeTracker/templates/logbook_detail.html

185 lines
9.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "layout.html" %}
{% block content %}
<!-- ID wrapper for replacement -->
<div id="detail-content">
<div class="card" style="border-left: 5px solid {{ entry.activity.color }}; {% if entry.is_running %}border: 1px solid #ffd700; border-left-width: 5px; background: #fffdf8;{% endif %}">
<div style="display: flex; justify-content: space-between; align-items: start;">
<div>
<h2>
{{ entry.activity.name }}
{% if entry.subcategory %}
<span style="font-size: 0.8rem; background: #eee; padding: 2px 8px; border-radius: 10px; color: #555; vertical-align: middle;">
{{ entry.subcategory }}
</span>
{% endif %}
{% if entry.is_running %}
<span style="font-size: 0.8rem; background: #27ae60; padding: 2px 8px; border-radius: 4px; color: white; vertical-align: middle; margin-left: 10px;">
RUNNING
</span>
{% endif %}
</h2>
<p style="color: #666;">
{{ entry.start_time.strftime('%A, %d. %B %Y') }}
</p>
</div>
<div style="text-align: right;">
<div style="font-weight: bold; font-size: 2rem; color: #333;">
<span class="{% if entry.is_running %}live-timer{% endif %}" {% if entry.is_running %}data-start="{{ entry.start_time.timestamp() * 1000 }}"{% endif %}>
{{ entry.duration_str }}
</span>
</div>
{% if entry.is_running %}
<form action="{{ url_for('stop_timer') }}" method="POST" style="display: inline-block;">
<button type="submit" class="btn btn-danger" style="font-size: 0.8rem; margin-top: 5px;">Stop Timer</button>
</form>
{% endif %}
<br>
<a href="{{ url_for('logbook') }}" class="btn" style="background: #95a5a6; font-size: 0.8rem; margin-top: 15px; display:inline-block;">Back to Log</a>
</div>
</div>
</div>
<div class="card">
<h3>Edit Entry</h3>
<form method="POST">
<input type="hidden" name="action" value="update">
<div style="display: flex; gap: 1rem; margin-bottom: 1rem;">
<div style="flex: 1;">
<label>Start Time</label>
<input type="datetime-local" name="start_time" value="{{ entry.start_time.strftime('%Y-%m-%dT%H:%M') }}" required>
</div>
<div style="flex: 1;">
<label>End Time</label>
<!-- If currently running, leave blank or show placeholder -->
{% if entry.is_running %}
<input type="datetime-local" name="end_time" placeholder="Currently Running (Leave empty)" title="Set a time to stop the timer manually">
<small style="color: #666; display: block; margin-top: 5px;">Timer is active. Select a date to stop it retroactively.</small>
{% else %}
<input type="datetime-local" name="end_time" value="{{ entry.end_time.strftime('%Y-%m-%dT%H:%M') if entry.end_time else '' }}">
<small style="color: #666; display: block; margin-top: 5px;">Session ended. Modifying this changes duration.</small>
{% endif %}
</div>
</div>
<!-- Subcategory Edit -->
<label>Subcategory</label>
<select name="subcategory">
<option value="">-- None --</option>
{% if entry.activity and entry.activity.subcategories %}
{% for sub in entry.activity.subcategories %}
<option value="{{ sub }}" {% if sub == entry.subcategory %}selected{% endif %}>{{ sub }}</option>
{% endfor %}
{% endif %}
</select>
<label>Note</label>
<textarea name="note" rows="3" style="width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 5px; font-family: inherit;">{{ entry.note }}</textarea>
<div style="margin-top: 10px; display: flex; justify-content: space-between;">
<button type="submit" class="btn">Save Changes</button>
</div>
</form>
<div style="border-top: 1px solid #eee; margin-top: 2rem; padding-top: 1rem;">
<form method="POST" onsubmit="return confirm('Are you sure you want to delete this entry? This action cannot be undone.');">
<input type="hidden" name="action" value="delete">
<button type="submit" class="btn btn-danger">Delete Entry</button>
</form>
</div>
</div>
<div class="card">
<h3>Tasks in this Session</h3>
{% if not tasks %}
<p style="color: var(--text-secondary);">No tasks were tracked for this session.</p>
{% else %}
<div class="checklist-container">
{% for task in tasks %}
<div class="checklist-item {% if task.status == 'completed' %}completed{% endif %}" style="padding: 12px 0;">
<!-- Visual Checkbox (Read Only) -->
<div style="
width: 18px; height: 18px;
border: 2px solid {% if task.status == 'completed' %}var(--primary-color){% else %}var(--text-secondary){% endif %};
border-radius: 4px;
background: {% if task.status == 'completed' %}var(--primary-color){% else %}transparent{% endif %};
margin-right: 12px; margin-top: 3px;
display: flex; align-items: center; justify-content: center;
">
{% if task.status == 'completed' %}
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
{% endif %}
</div>
<div style="flex-grow: 1;">
<a href="{{ url_for('task_detail', task_id=task._id) }}" style="text-decoration: none; color: inherit; font-weight: 500; display: block;">
{{ task.name }}
</a>
{% if task.completed_at %}
<small style="color: var(--text-secondary); font-size: 0.8rem;">Completed at {{ task.completed_at.strftime('%H:%M') }}</small>
{% endif %}
</div>
<a href="{{ url_for('task_detail', task_id=task._id) }}" style="color: var(--text-secondary); text-decoration: none; font-size: 1.2rem; line-height: 1;">
</a>
</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<script>
// Live Timer Update
setInterval(() => {
document.querySelectorAll('.live-timer').forEach(el => {
const start = parseInt(el.getAttribute('data-start'));
if (!start) return;
const diff = Date.now() - start;
if (diff < 0) return;
const hours = Math.floor(diff / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
let timeStr = (hours < 10 ? "0" + hours : hours) + ":" +
(minutes < 10 ? "0" + minutes : minutes) + ":" +
(seconds < 10 ? "0" + seconds : seconds);
// Only update the time part, keep (Running) suffix if handled elsewhere or replace
el.textContent = timeStr;
});
}, 1000);
// Background Sync Detail View
let lastDetailHash = "";
setInterval(() => {
if (document.hidden) return;
// Skip if user is focusing an input (editing)
if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') return;
fetch(window.location.href)
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const doc = parser.parseFromString(html, 'text/html');
const newContentWrapper = doc.getElementById('detail-content');
const oldContentWrapper = document.getElementById('detail-content');
if (newContentWrapper && oldContentWrapper) {
const newContent = newContentWrapper.innerHTML;
if (newContent !== lastDetailHash && lastDetailHash !== "") {
oldContentWrapper.innerHTML = newContent;
}
lastDetailHash = newContent;
}
})
.catch(e => console.error("Detail sync failed", e));
}, 4000); // Check every 4 seconds
</script>
{% endblock %}