fix(timer): load tasks when selecting project on Timer and Log Time

- Use /api/projects/<id>/tasks instead of /api/tasks?project_id= so task
  loading matches the working Edit Logged Time flow.
- Add credentials: 'same-origin' and response.ok checks for reliable
  session auth and error handling.
- Render JS-embedded i18n strings with |tojson to avoid breakage in
  non-English locales.

Fixes #480

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Dries Peeters
2026-02-02 14:11:02 +01:00
parent 7160ce88e5
commit 689700d260
2 changed files with 15 additions and 10 deletions
+4 -4
View File
@@ -139,8 +139,8 @@ document.addEventListener('DOMContentLoaded', async function(){
const projectSelect = document.getElementById('project_id');
const clientSelect = document.getElementById('client_id');
const taskSelect = document.getElementById('task_id');
const noTaskText = '{{ _('No task') }}';
const failedToLoadTasksText = '{{ _('Failed to load tasks') }}';
const noTaskText = {{ _('No task')|tojson }};
const failedToLoadTasksText = {{ _('Failed to load tasks')|tojson }};
// Single-client mode: when project cleared, restore client (Issue #467)
const onlyOneClient = {{ 'true' if only_one_client else 'false' }};
@@ -176,10 +176,10 @@ document.addEventListener('DOMContentLoaded', async function(){
return;
}
try{
const resp = await fetch(`/api/tasks?project_id=${projectId}`);
const resp = await fetch(`/api/projects/${projectId}/tasks`, { credentials: 'same-origin' });
if (!resp.ok) throw new Error(failedToLoadTasksText);
const data = await resp.json();
const tasks = Array.isArray(data.tasks) ? data.tasks : [];
const tasks = Array.isArray(data?.tasks) ? data.tasks : [];
taskSelect.innerHTML = '<option value="">' + noTaskText + '</option>';
tasks.forEach(t => {
const opt = document.createElement('option');
+11 -6
View File
@@ -241,20 +241,25 @@
const projectSelectEl = document.getElementById('project_id');
const clientSelectEl = document.getElementById('client_id');
const taskSelectEl = document.getElementById('task_id');
const noTaskText = {{ _('No task')|tojson }};
const failedToLoadTasksText = {{ _('Failed to load tasks')|tojson }};
async function loadTasksForProject(projectId) {
if (!taskSelectEl) return;
if (!projectId) {
taskSelectEl.innerHTML = '<option value=\"\">{{ _('No task') }}</option>';
taskSelectEl.innerHTML = `<option value="">${noTaskText}</option>`;
taskSelectEl.disabled = false;
return;
}
try {
const response = await fetch(`/api/tasks?project_id=${projectId}`);
const response = await fetch(`/api/projects/${projectId}/tasks`, { credentials: 'same-origin' });
if (!response.ok) throw new Error(failedToLoadTasksText);
const data = await response.json();
taskSelectEl.innerHTML = '<option value=\"\">{{ _('No task') }}</option>';
(data.tasks || []).forEach(task => {
const tasks = Array.isArray(data?.tasks) ? data.tasks : [];
taskSelectEl.innerHTML = `<option value="">${noTaskText}</option>`;
tasks.forEach(task => {
const option = document.createElement('option');
option.value = task.id;
option.textContent = task.name;
@@ -263,7 +268,7 @@ async function loadTasksForProject(projectId) {
taskSelectEl.disabled = false;
} catch (error) {
console.error('Error loading tasks:', error);
taskSelectEl.innerHTML = '<option value=\"\">{{ _('No task') }}</option>';
taskSelectEl.innerHTML = `<option value="">${noTaskText}</option>`;
taskSelectEl.disabled = true;
}
}
@@ -289,7 +294,7 @@ if (projectSelectEl && clientSelectEl) {
projectSelectEl.value = '';
}
if (taskSelectEl) {
taskSelectEl.innerHTML = '<option value=\"\">{{ _('No task') }}</option>';
taskSelectEl.innerHTML = `<option value="">${noTaskText}</option>`;
taskSelectEl.disabled = true;
}
} else if (taskSelectEl) {