From f2c8af4680785813a98599cbab91c50c81e5b3f6 Mon Sep 17 00:00:00 2001 From: Christian Date: Fri, 1 May 2026 20:58:13 +0200 Subject: [PATCH] feat(task-templates): implement task template MVP with modal selector and tag actions - Added task template and task template items tables to the database. - Introduced case template runs and run items tables for tracking template executions. - Created a new JavaScript module for task template selection with a modal interface. - Integrated tag actions to open the task template selector modal upon tag addition. - Updated backend to resolve tag actions and return them in the response when adding tags. - Enhanced the tag picker to handle actions and trigger the appropriate modal. - Added permissions and group permissions for managing task templates. --- app/modules/sag/templates/detail.html | 167 +++- app/modules/sag/templates/detail_v3.html | 436 +++++++++- .../task_templates/backend/__init__.py | 1 + app/modules/task_templates/backend/models.py | 97 +++ app/modules/task_templates/backend/router.py | 664 ++++++++++++++ app/settings/frontend/settings.html | 812 ++++++++++++++++++ app/shared/frontend/base.html | 3 +- app/tags/backend/models.py | 1 + app/tags/backend/router.py | 28 + main.py | 2 + migrations/183_task_templates_mvp.sql | 125 +++ static/js/tag-picker.js | 28 +- static/js/task-template-selector.js | 518 +++++++++++ 13 files changed, 2851 insertions(+), 31 deletions(-) create mode 100644 app/modules/task_templates/backend/__init__.py create mode 100644 app/modules/task_templates/backend/models.py create mode 100644 app/modules/task_templates/backend/router.py create mode 100644 migrations/183_task_templates_mvp.sql create mode 100644 static/js/task-template-selector.js diff --git a/app/modules/sag/templates/detail.html b/app/modules/sag/templates/detail.html index 9246c37..0e7a77d 100644 --- a/app/modules/sag/templates/detail.html +++ b/app/modules/sag/templates/detail.html @@ -4190,7 +4190,21 @@ // Set default context for keyboard shortcuts (Option+Shift+T) if (window.setTagPickerContext) { - window.setTagPickerContext('case', {{ case.id }}, () => syncCaseTagsUi()); + window.setTagPickerContext('case', {{ case.id }}, (tag, addResult) => { + syncCaseTagsUi(); + + const actionType = addResult && addResult.action ? addResult.action.type : null; + const shouldOpenTemplateModal = actionType === 'open_task_template_modal' + || String((tag && tag.name) || '').toLowerCase() === 'opgave_template'; + + if (shouldOpenTemplateModal && typeof window.openTaskTemplateSelectorModal === 'function') { + window.openTaskTemplateSelectorModal({ + action: addResult ? addResult.action : null, + entity_type: 'case', + entity_id: caseId, + }); + } + }); } // Load Hardware & Locations @@ -5381,7 +5395,7 @@ return normalizeLegacyTags(legacyTags); }; - const response = await fetch(`/api/v1/tags/entity/case/${caseId}`); + const response = await fetch(`/api/v1/tags/entity/case/${caseId}`, { credentials: 'include' }); if (response.ok) { const genericTags = await response.json(); tags = Array.isArray(genericTags) ? genericTags : []; @@ -5434,7 +5448,7 @@ if (!suggestionsContainer) return; try { - const response = await fetch(`/api/v1/tags/entity/case/${caseId}/suggestions`); + const response = await fetch(`/api/v1/tags/entity/case/${caseId}/suggestions`, { credentials: 'include' }); if (!response.ok) throw new Error('Kunne ikke hente forslag'); const suggestions = await response.json(); @@ -5470,6 +5484,7 @@ const response = await fetch('/api/v1/tags/entity', { method: 'POST', headers: { 'Content-Type': 'application/json' }, + credentials: 'include', body: JSON.stringify({ entity_type: 'case', entity_id: caseId, tag_id: tagId }) }); @@ -5478,6 +5493,17 @@ throw new Error(error.detail || 'Kunne ikke tilfoeje tag'); } + const addResult = await response.json().catch(() => ({})); + if (addResult && addResult.action) { + window.dispatchEvent(new CustomEvent('hub:tag-action', { + detail: { + action: addResult.action, + entity_type: 'case', + entity_id: caseId, + } + })); + } + await syncCaseTagsUi(); if (typeof showNotification === 'function') { showNotification('Tag tilfoejet', 'success'); @@ -5520,6 +5546,8 @@ await loadCaseTagSuggestions(); } + window.syncCaseTagsUi = syncCaseTagsUi; + let todoUserId = null; function getTodoUserId() { @@ -6152,14 +6180,22 @@
TAGS
-
Indlaeser tags...
+ {% if tags and tags|length > 0 %} + {% for tag in tags %} + + {{ tag.tag_navn or tag.name or 'Tag' }} + + {% endfor %} + {% else %} +
Ingen tags paa sagen endnu
+ {% endif %}
Forslag (brand/type)
@@ -15509,6 +15545,127 @@ })(); + + + + + + + + + +