Goals working well

This commit is contained in:
2026-02-10 19:46:25 +01:00
parent 37190679ed
commit 9ef70e0436
5 changed files with 398 additions and 15 deletions

View File

@@ -189,18 +189,83 @@
<!-- Create Activity Form -->
<div id="newActivityForm" class="card" style="display:none; margin-top: 2rem;">
<h3>Create New Activity Category</h3>
<form action="{{ url_for('add_activity') }}" method="POST">
<form action="{{ url_for('add_activity') }}" method="POST" onsubmit="prepareTasksList()">
<label>Name</label>
<input type="text" name="name" required placeholder="e.g. Household">
<label>Color</label>
<input type="color" name="color" value="#3498db" style="width:100%; height:40px; border:none;">
<label>Default Tasks (comma separated)</label>
<input type="text" name="tasks" placeholder="e.g. Laundry, Dishes, Trash">
<label>Default Tasks (Template)</label>
<p style="font-size: 0.8rem; color: var(--text-secondary); margin-top: -10px;">
These tasks will be created automatically every time you start this activity.
</p>
<button type="submit" class="btn">Create</button>
<button type="button" class="btn" style="background: #7f8c8d" onclick="this.parentElement.parentElement.style.display='none'">Cancel</button>
<div style="display: flex; gap: 5px; margin-bottom: 10px;">
<input type="text" id="newTaskInput" placeholder="Add task name..." style="margin-bottom: 0;">
<button type="button" class="btn" style="background: #27ae60;" onclick="addNewTask()">Add</button>
</div>
<ul id="newTasksListDisplay" style="list-style: none; padding: 0; margin-bottom: 1rem;">
<!-- Items will be injected here -->
</ul>
<input type="hidden" name="tasks_list_data" id="tasksListData">
<div style="margin-top: 1rem;">
<button type="submit" class="btn">Create</button>
<button type="button" class="btn" style="background: var(--text-secondary)" onclick="document.getElementById('newActivityForm').style.display='none'">Cancel</button>
</div>
</form>
</div>
<script>
// Existing timer scripts...
// ...existing code...
// New Task List Logic for Activity Creation
let newActivityTasks = [];
function addNewTask() {
const input = document.getElementById('newTaskInput');
const val = input.value.trim();
if (val) {
newActivityTasks.push(val);
input.value = '';
renderNewActivityTasks();
}
}
function removeNewTask(index) {
newActivityTasks.splice(index, 1);
renderNewActivityTasks();
}
function renderNewActivityTasks() {
const list = document.getElementById('newTasksListDisplay');
list.innerHTML = '';
newActivityTasks.forEach((item, index) => {
const li = document.createElement('li');
li.style.background = '#f7f7f5';
li.style.border = '1px solid var(--border-dim)';
li.style.margin = '5px 0';
li.style.padding = '8px';
li.style.borderRadius = '4px';
li.style.display = 'flex';
li.style.justifyContent = 'space-between';
li.style.alignItems = 'center';
li.style.fontSize = '0.9rem';
li.innerHTML = `
<span>${item}</span>
<span onclick="removeNewTask(${index})" style="cursor: pointer; color: var(--danger-color); font-weight: bold; padding: 0 5px;">&times;</span>
`;
list.appendChild(li);
});
}
function prepareTasksList() {
document.getElementById('tasksListData').value = newActivityTasks.join(',');
}
</script>
{% endblock %}

84
templates/edit_goal.html Normal file
View File

@@ -0,0 +1,84 @@
{% extends "layout.html" %}
{% block content %}
<div class="card" style="max-width: 500px; margin: auto;">
<h2>Edit Goal</h2>
<form method="POST">
<label>Goal Name</label>
<input type="text" name="name" value="{{ goal.name }}" required>
<label>Activity</label>
<select name="activity_id" id="activitySelect" required onchange="updateSubcategories()">
<option value="">-- Select Activity --</option>
{% for act in activities %}
<option value="{{ act._id }}"
data-subcats='{{ act.subcategories|default([])|tojson }}'
{% if act._id|string == goal.activity_id|string %}selected{% endif %}>
{{ act.name }}
</option>
{% endfor %}
</select>
<div id="subcatWrapper" style="display:none;">
<label>Subcategory (Optional)</label>
<select name="subcategory" id="subcategorySelect">
<option value="">-- All --</option>
</select>
</div>
<label>Frequency</label>
<select name="frequency">
<option value="daily" {% if goal.frequency == 'daily' %}selected{% endif %}>Daily</option>
<option value="weekly" {% if goal.frequency == 'weekly' %}selected{% endif %}>Weekly</option>
<option value="monthly" {% if goal.frequency == 'monthly' %}selected{% endif %}>Monthly</option>
<option value="yearly" {% if goal.frequency == 'yearly' %}selected{% endif %}>Yearly</option>
</select>
<label>Target Hours</label>
<input type="number" name="target_hours" step="0.1" value="{{ goal.target_hours }}" required>
<div style="margin-top: 1rem; display: flex; gap: 10px;">
<button type="submit" class="btn">Save Changes</button>
<a href="{{ url_for('goals') }}" class="btn" style="background: var(--text-secondary); text-decoration: none;">Cancel</a>
</div>
</form>
</div>
<script>
// Logic to populate subcategories and set current value
const currentSubcat = "{{ goal.subcategory }}";
function updateSubcategories() {
const actSelect = document.getElementById('activitySelect');
const subWrapper = document.getElementById('subcatWrapper');
const subSelect = document.getElementById('subcategorySelect');
const selectedOption = actSelect.options[actSelect.selectedIndex];
if (!selectedOption.value) {
subWrapper.style.display = 'none';
return;
}
const subcats = JSON.parse(selectedOption.getAttribute('data-subcats') || '[]');
subSelect.innerHTML = '<option value="">-- All --</option>';
if (subcats && subcats.length > 0) {
subWrapper.style.display = 'block';
subcats.forEach(s => {
const opt = document.createElement('option');
opt.value = s;
opt.innerText = s;
if (s === currentSubcat && actSelect.value === "{{ goal.activity_id }}") {
opt.selected = true;
}
subSelect.appendChild(opt);
});
} else {
subWrapper.style.display = 'none';
}
}
// Initialize on load
updateSubcategories();
</script>
{% endblock %}

111
templates/goals.html Normal file
View File

@@ -0,0 +1,111 @@
{% extends "layout.html" %}
{% block content %}
<div class="card" style="margin-bottom: 2rem;">
<h2>My Goals</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1.5rem;">
{% for goal in goals %}
<div style="border: 1px solid var(--border-dim); border-radius: 8px; padding: 1.5rem; position: relative;">
<div style="position: absolute; top: 10px; right: 10px;">
<a href="{{ url_for('edit_goal', goal_id=goal._id) }}" style="color: var(--text-secondary); text-decoration: none; font-size: 0.9rem; margin-right: 8px;">Edit</a>
<a href="{{ url_for('delete_goal', goal_id=goal._id) }}" style="color: var(--danger-color); text-decoration: none; font-size: 1.2rem;">&times;</a>
</div>
<h3 style="font-size: 1.2rem; margin-bottom: 0.5rem; padding-right: 40px;">{{ goal.name }}</h3>
<div style="font-size: 0.9rem; color: var(--text-secondary); margin-bottom: 1rem;">
{{ goal.frequency|capitalize }} Target: {{ goal.target_hours }}h
<br>
<div style="margin-top: 5px;">
<span style="background: {{ goal.activity_color }}; color: white; padding: 2px 6px; border-radius: 4px; font-size: 0.8rem;">
{{ goal.activity_name }}
</span>
{% if goal.subcategory %}
<span style="color: var(--text-secondary); font-size: 0.8rem;"> &gt; {{ goal.subcategory }}</span>
{% endif %}
</div>
</div>
<div style="display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 5px;">
<!-- Use smart formatted progress (mins or hours) -->
<span style="font-weight: bold; font-size: 1.5rem; color: var(--primary-color);">{{ goal.display_progress }}</span>
<span style="font-size: 0.9rem; color: var(--text-secondary);">{{ goal.percent }}%</span>
</div>
<!-- Progress Bar -->
<div style="width: 100%; height: 8px; background: var(--bg-input); border-radius: 4px; overflow: hidden;">
<div style="width: {{ goal.percent }}%; height: 100%; background: {% if goal.percent >= 100 %}#27ae60{% else %}var(--primary-color){% endif %}; transition: width 0.5s;"></div>
</div>
</div>
{% else %}
<p style="color: var(--text-secondary);">No goals set yet.</p>
{% endfor %}
</div>
</div>
<div class="card" style="max-width: 500px;">
<h3>Set New Goal</h3>
<form method="POST">
<label>Goal Name</label>
<input type="text" name="name" placeholder="e.g. Learn Python" required>
<label>Activity (Required)</label>
<select name="activity_id" id="activitySelect" required onchange="updateSubcategories()">
<option value="">-- Select Activity --</option>
{% for act in activities %}
<option value="{{ act._id }}" data-subcats='{{ act.subcategories|default([])|tojson }}'>{{ act.name }}</option>
{% endfor %}
</select>
<div id="subcatWrapper" style="display:none;">
<label>Subcategory (Optional)</label>
<select name="subcategory" id="subcategorySelect">
<!-- Javascript will populate this -->
</select>
</div>
<label>Frequency</label>
<select name="frequency">
<option value="daily">Daily</option>
<option value="weekly">Weekly</option>
<option value="monthly">Monthly</option>
<option value="yearly">Yearly</option>
</select>
<label>Target Hours</label>
<input type="number" name="target_hours" step="0.5" placeholder="e.g. 1.0" required>
<button type="submit" class="btn" style="margin-top: 1rem;">Create Goal</button>
</form>
</div>
<script>
function updateSubcategories() {
const actSelect = document.getElementById('activitySelect');
const subWrapper = document.getElementById('subcatWrapper');
const subSelect = document.getElementById('subcategorySelect');
const selectedOption = actSelect.options[actSelect.selectedIndex];
// Handle case where no activity is selected
if (!selectedOption.value) {
subWrapper.style.display = 'none';
return;
}
const subcats = JSON.parse(selectedOption.getAttribute('data-subcats') || '[]');
subSelect.innerHTML = '<option value="">-- All --</option>';
if (subcats && subcats.length > 0) {
subWrapper.style.display = 'block';
subcats.forEach(s => {
const opt = document.createElement('option');
opt.value = s;
opt.innerText = s;
subSelect.appendChild(opt);
});
} else {
subWrapper.style.display = 'none';
}
}
</script>
{% endblock %}

View File

@@ -160,6 +160,7 @@
{% if session.user_id %}
<a href="{{ url_for('index') }}">Tracker</a>
<a href="{{ url_for('tasks') }}">Tasks</a>
<a href="{{ url_for('goals') }}">Goals</a>
<a href="{{ url_for('logbook') }}">Logbook</a>
<a href="{{ url_for('logout') }}">Logout</a>
{% else %}