mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-18 20:29:44 -05:00
fix(timer): use url_for for task API URLs and improve timer page init
Build task API URLs from url_for in timer page, bulk entry, edit timer, and time entry template edit to support subpath deployment. Wrap timer page script in DOMContentLoaded, load tasks when project is pre-selected, expose selectRecentProject on window, and fix client/project select attachment logic. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -145,6 +145,12 @@
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const tasksApiUrlTemplate = {{ url_for('api.get_project_tasks', project_id=0)|tojson }};
|
||||
function buildTasksUrl(projectId) {
|
||||
const pid = String(projectId || '').trim();
|
||||
if (!pid) return tasksApiUrlTemplate;
|
||||
return String(tasksApiUrlTemplate).replace(/\/0\/tasks$/, '/' + encodeURIComponent(pid) + '/tasks');
|
||||
}
|
||||
function loadProjectTasks(projectId) {
|
||||
const taskSelect = document.getElementById('task_id');
|
||||
const selectedTaskId = taskSelect.dataset.selected;
|
||||
@@ -157,7 +163,7 @@ function loadProjectTasks(projectId) {
|
||||
}
|
||||
|
||||
// Fetch tasks for the selected project
|
||||
fetch(`/api/projects/${projectId}/tasks`)
|
||||
fetch(buildTasksUrl(projectId))
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
data.tasks.forEach(task => {
|
||||
|
||||
@@ -237,12 +237,20 @@
|
||||
|
||||
{% block scripts_extra %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Load tasks when project is selected and sync with client selection
|
||||
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 }};
|
||||
const tasksApiUrlTemplate = {{ url_for('api.get_project_tasks', project_id=0)|tojson }};
|
||||
|
||||
function buildTasksUrl(projectId) {
|
||||
const pid = String(projectId || '').trim();
|
||||
if (!pid) return tasksApiUrlTemplate;
|
||||
return String(tasksApiUrlTemplate).replace(/\/0\/tasks$/, '/' + encodeURIComponent(pid) + '/tasks');
|
||||
}
|
||||
|
||||
async function loadTasksForProject(projectId) {
|
||||
if (!taskSelectEl) return;
|
||||
@@ -253,7 +261,7 @@ async function loadTasksForProject(projectId) {
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/projects/${projectId}/tasks`, { credentials: 'same-origin' });
|
||||
const response = await fetch(buildTasksUrl(projectId), { credentials: 'same-origin' });
|
||||
if (!response.ok) throw new Error(failedToLoadTasksText);
|
||||
const data = await response.json();
|
||||
const tasks = Array.isArray(data?.tasks) ? data.tasks : [];
|
||||
@@ -276,7 +284,8 @@ async function loadTasksForProject(projectId) {
|
||||
const onlyOneClient = {{ 'true' if only_one_client|default(false) else 'false' }};
|
||||
const singleClientId = {{ ('"' ~ single_client.id ~ '"') if single_client else 'null' }};
|
||||
|
||||
if (projectSelectEl && clientSelectEl) {
|
||||
// Task loading: attach when project and task selects exist (independent of client)
|
||||
if (projectSelectEl && taskSelectEl) {
|
||||
projectSelectEl.addEventListener('change', () => {
|
||||
const pid = projectSelectEl.value;
|
||||
if (pid && clientSelectEl) {
|
||||
@@ -286,7 +295,14 @@ if (projectSelectEl && clientSelectEl) {
|
||||
}
|
||||
loadTasksForProject(pid);
|
||||
});
|
||||
// Initial load when project is pre-selected
|
||||
if (projectSelectEl.value) {
|
||||
loadTasksForProject(projectSelectEl.value);
|
||||
}
|
||||
}
|
||||
|
||||
// Client/project mutual exclusivity (when client select exists)
|
||||
if (clientSelectEl) {
|
||||
clientSelectEl.addEventListener('change', () => {
|
||||
const cid = clientSelectEl.value;
|
||||
if (cid) {
|
||||
@@ -341,14 +357,16 @@ if (timerStartForm) {
|
||||
}, true); // Use capture phase to run before other handlers
|
||||
}
|
||||
|
||||
// Select recent project
|
||||
function selectRecentProject(projectId, projectName) {
|
||||
// Select recent project (exposed for onclick handlers)
|
||||
window.selectRecentProject = function(projectId, projectName) {
|
||||
const projectSelect = document.getElementById('project_id');
|
||||
if (projectSelect) {
|
||||
projectSelect.value = projectId;
|
||||
projectSelect.dispatchEvent(new Event('change'));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}); // end DOMContentLoaded
|
||||
|
||||
// Update timer display every second
|
||||
{% if active_timer %}
|
||||
|
||||
@@ -399,6 +399,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
entriesFor: i18nTimerBulk.entries_for || 'Entries will be created for'
|
||||
};
|
||||
|
||||
const tasksApiUrlTemplate = {{ url_for('api.list_tasks_for_project', project_id=0)|tojson }};
|
||||
function buildTasksUrl(projectId) {
|
||||
const pid = String(projectId || '').trim();
|
||||
if (!pid) return tasksApiUrlTemplate;
|
||||
return String(tasksApiUrlTemplate).replace(/project_id=0/, 'project_id=' + encodeURIComponent(pid));
|
||||
}
|
||||
async function loadTasksForProject(projectId) {
|
||||
if (!projectId) {
|
||||
taskSelect.innerHTML = '<option value="">' + L.noTask + '</option>';
|
||||
@@ -406,7 +412,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const resp = await fetch(`/api/tasks?project_id=${projectId}`);
|
||||
const resp = await fetch(buildTasksUrl(projectId));
|
||||
if (!resp.ok) throw new Error(L.failedLoad);
|
||||
const data = await resp.json();
|
||||
const tasks = Array.isArray(data.tasks) ? data.tasks : [];
|
||||
|
||||
@@ -14,6 +14,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const projectSelect = document.getElementById('project_id');
|
||||
const taskSelect = document.getElementById('task_id');
|
||||
const form = document.querySelector('form');
|
||||
const tasksApiUrlTemplate = {{ url_for('api.get_project_tasks', project_id=0)|tojson }};
|
||||
|
||||
function buildTasksUrl(projectId) {
|
||||
const pid = String(projectId || '').trim();
|
||||
if (!pid) return tasksApiUrlTemplate;
|
||||
return String(tasksApiUrlTemplate).replace(/\/0\/tasks$/, '/' + encodeURIComponent(pid) + '/tasks');
|
||||
}
|
||||
|
||||
if (projectSelect && taskSelect) {
|
||||
projectSelect.addEventListener('change', function() {
|
||||
@@ -24,7 +31,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
|
||||
if (projectId) {
|
||||
// Fetch tasks for the selected project
|
||||
fetch(`/api/projects/${projectId}/tasks`, { credentials: 'same-origin' })
|
||||
fetch(buildTasksUrl(projectId), { credentials: 'same-origin' })
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success && data.tasks) {
|
||||
|
||||
Reference in New Issue
Block a user