subcategories and editing for tasks

This commit is contained in:
2026-02-10 19:31:57 +01:00
parent b2f26b66bb
commit 6902ff3373
7 changed files with 389 additions and 81 deletions

View File

@@ -1,15 +1,20 @@
{% extends "layout.html" %}
{% block content %}
<!-- Active Timer Section -->
{% if current_entry %}
<div class="card active-section">
<h3 style="text-align: center; color: {{ current_entry.activity_color }}">
Currently Tracking: {{ current_entry.activity_name }}
<div class="card active-section" id="activeSection" style="{% if not current_entry %}display:none;{% endif %}">
<h3 id="activeName" style="text-align: center; color: {{ current_entry.activity_color if current_entry else '#333' }}">
Currently Tracking: {{ current_entry.activity_name if current_entry else '' }}
</h3>
{% if current_entry.note %}
<p style="text-align: center; color: #666; font-style: italic;">"{{ current_entry.note }}"</p>
{% endif %}
<div id="activeSubcatContainer" style="text-align: center; {% if not current_entry or not current_entry.subcategory %}display:none;{% endif %} margin: 0 auto 10px; display: table;">
<div id="activeSubcatDisplay" style="background: #eee; padding: 2px 10px; border-radius: 10px;">
{{ current_entry.subcategory if current_entry else '' }}
</div>
</div>
<p id="activeNote" style="text-align: center; color: #666; font-style: italic; {% if not current_entry or not current_entry.note %}display:none;{% endif %}">
"{{ current_entry.note if current_entry else '' }}"
</p>
<div class="timer-display" id="timer">00:00:00</div>
@@ -33,56 +38,58 @@
<form action="{{ url_for('stop_timer') }}" method="POST" style="text-align: center;">
<button type="submit" class="btn btn-danger" style="font-size: 1.2rem; width: 100%;">Stop Activity</button>
</form>
</div>
<script>
// Timer Logic
const startTime = new Date("{{ current_entry.start_time }}").getTime();
<!-- Script for Timer -->
<script>
let startTime = {% if current_entry %}{{ current_entry.start_time.timestamp() * 1000 }}{% else %}null{% endif %};
let timerInterval;
function updateTimer() {
if (!startTime) return;
const now = new Date().getTime();
const diff = now - startTime;
function updateTimer() {
const now = new Date().getTime();
const diff = now - startTime;
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
document.getElementById("timer").innerHTML =
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
const timerEl = document.getElementById("timer");
if (timerEl) {
timerEl.innerHTML =
(hours < 10 ? "0" + hours : hours) + ":" +
(minutes < 10 ? "0" + minutes : minutes) + ":" +
(seconds < 10 ? "0" + seconds : seconds);
}
setInterval(updateTimer, 1000);
updateTimer(); // run immediately
}
// AJAX for Tasks
function toggleTask(taskId, checkbox) {
const formData = new FormData();
formData.append('task_id', taskId);
formData.append('is_checked', checkbox.checked);
if (startTime) {
timerInterval = setInterval(updateTimer, 1000);
updateTimer();
}
</script>
<!-- End Script -->
fetch('/complete_task', { method: 'POST', body: formData })
.then(r => r.json())
.then(data => {
const label = checkbox.nextElementSibling;
if(checkbox.checked) {
label.style.textDecoration = "line-through";
} else {
label.style.textDecoration = "none";
}
});
}
</script>
</div>
{% endif %}
<!-- Start Activity Section -->
<div class="activity-grid">
<div class="activity-grid" style="margin-top: 2rem;">
{% for act in activities %}
<!-- Clicking opens a small modal or submits form directly. Let's do a form with a prompt for note -->
<div class="activity-card"
style="background-color: {{ act.color }}"
onclick="startActivity('{{ act._id }}', '{{ act.name }}')">
<span>{{ act.name }}</span>
<div style="position: relative;">
<div class="activity-card"
style="background-color: {{ act.color }}"
data-name="{{ act.name }}"
data-id="{{ act._id }}"
data-color="{{ act.color }}"
data-subcategories='{{ act.subcategories|default([])|tojson }}'
onclick="startActivityImmediate(this)">
<span>{{ act.name }}</span>
</div>
<!-- Small Edit Link -->
<a href="{{ url_for('edit_activity', activity_id=act._id) }}"
style="position: absolute; top: 5px; right: 5px; color: rgba(255,255,255,0.8); text-decoration: none; font-size: 0.8rem; padding: 5px;"
title="Edit Activity">
Edit
</a>
</div>
{% endfor %}
@@ -93,19 +100,89 @@
</div>
<!-- Hidden Form for Starting Activity with Note -->
<form id="startForm" method="POST" style="display:none;">
<input type="hidden" name="note" id="startNote">
</form>
<!-- Custom Start Modal -->
<div id="startModal" style="display:none; position: fixed; top:0; left:0; width:100%; height:100%; background: rgba(0,0,0,0.5); z-index: 1000; justify-content: center; align-items: center;">
<div class="card" style="width: 90%; max-width: 400px;">
<h3 id="modalTitle">Activity Started</h3>
<form id="startForm" action="{{ url_for('update_active_entry') }}" method="POST">
<div id="subcategoryContainer" style="display: none; margin-bottom: 1rem;">
<label>Subcategory / Context</label>
<select name="subcategory" id="modalSubcatSelect" style="width: 100%; padding: 0.5rem;">
<option value="">-- None --</option>
</select>
</div>
<label>Note (Optional)</label>
<input type="text" name="note" placeholder="What are you working on?" autocomplete="off">
<div style="margin-top: 1rem; display: flex; justify-content: space-between;">
<!-- Cancel just closes modal, timer keeps running -->
<button type="button" class="btn" style="background: #95a5a6;" onclick="document.getElementById('startModal').style.display='none'">Skip</button>
<button type="submit" class="btn" style="background: #27ae60;">Update Details</button>
</div>
</form>
</div>
</div>
<script>
function startActivity(id, name) {
let note = prompt("Start " + name + "?\nAdd a note (optional):");
if (note === null) return; // Cancelled
function startActivityImmediate(element) {
const id = element.getAttribute('data-id');
const name = element.getAttribute('data-name');
const color = element.getAttribute('data-color');
const subcats = JSON.parse(element.getAttribute('data-subcategories') || '[]');
let form = document.getElementById('startForm');
form.action = "/toggle_timer/" + id;
document.getElementById('startNote').value = note;
form.submit();
// 1. Start Background Timer
fetch('/start_timer_bg/' + id, { method: 'POST' })
.then(res => res.json())
.then(data => {
if(data.status === 'success') {
// 2. Start Local Timer UI
startTime = new Date(data.start_time).getTime();
clearInterval(timerInterval);
timerInterval = setInterval(updateTimer, 1000);
updateTimer();
// Update UI State
document.getElementById('activeSection').style.display = 'block';
document.getElementById('activeName').innerText = "Currently Tracking: " + name;
document.getElementById('activeName').style.color = color;
// Reset Note/Subcat display
document.getElementById('activeNote').style.display = 'none';
document.getElementById('activeSubcatContainer').style.display = 'none';
// 3. Open Modal for Details
openDetailsModal(name, subcats);
}
});
}
function openDetailsModal(name, subcats) {
document.getElementById('modalTitle').innerText = name + " Started";
// Setup Subcategories
const subcatContainer = document.getElementById('subcategoryContainer');
const select = document.getElementById('modalSubcatSelect');
select.innerHTML = '<option value="">-- None --</option>';
if (subcats && subcats.length > 0) {
subcatContainer.style.display = 'block';
subcats.forEach(sc => {
const opt = document.createElement('option');
opt.value = sc;
opt.innerText = sc;
select.appendChild(opt);
});
} else {
subcatContainer.style.display = 'none';
}
// Show Modal
document.getElementById('startModal').style.display = 'flex';
document.querySelector('#startForm input[name="note"]').focus();
}
</script>