2026-02-01 11:58:44 +01:00
|
|
|
{% extends "shared/frontend/base.html" %}
|
2026-02-01 00:38:10 +01:00
|
|
|
|
2026-02-01 11:58:44 +01:00
|
|
|
{% block title %}Sager - BMC Hub{% endblock %}
|
2026-01-29 23:07:33 +01:00
|
|
|
|
2026-02-01 11:58:44 +01:00
|
|
|
{% block extra_css %}
|
|
|
|
|
<style>
|
|
|
|
|
.search-bar {
|
|
|
|
|
margin-bottom: 1.5rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.search-bar input {
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
border: 1px solid rgba(0,0,0,0.1);
|
|
|
|
|
padding: 0.6rem 1rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.table-wrapper {
|
|
|
|
|
background: var(--bg-card);
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sag-table {
|
|
|
|
|
width: 100%;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sag-table thead {
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sag-table thead th {
|
|
|
|
|
padding: 0.8rem 1rem;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
border: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sag-table tbody tr {
|
|
|
|
|
border-bottom: 1px solid rgba(0,0,0,0.05);
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sag-table tbody tr:hover {
|
|
|
|
|
background: var(--accent-light);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sag-table tbody td {
|
|
|
|
|
padding: 0.6rem 1rem;
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sag-id {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
font-size: 0.95rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sag-titel {
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
color: var(--text-primary);
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.sag-beskrivelse {
|
|
|
|
|
color: var(--text-secondary);
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
margin-top: 0.2rem;
|
|
|
|
|
display: -webkit-box;
|
|
|
|
|
-webkit-line-clamp: 1;
|
|
|
|
|
-webkit-box-orient: vertical;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Tree view styles */
|
|
|
|
|
.tree-row {
|
|
|
|
|
position: relative;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tree-row.has-children {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tree-row.has-children:before {
|
|
|
|
|
content: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tree-row.has-children td:first-child {
|
|
|
|
|
position: relative;
|
|
|
|
|
padding-left: 2.5rem !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tree-toggle {
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
width: 20px;
|
|
|
|
|
height: 20px;
|
|
|
|
|
background: var(--accent-light);
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0.5rem;
|
|
|
|
|
top: 50%;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tree-toggle:hover {
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
color: white;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tree-child {
|
|
|
|
|
background: rgba(0,0,0,0.02);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tree-child td {
|
|
|
|
|
padding: 0.5rem 1rem !important;
|
|
|
|
|
border-top: none !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tree-child td:first-child {
|
|
|
|
|
position: relative;
|
|
|
|
|
padding-left: 2.5rem !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.tree-child td:first-child:before {
|
|
|
|
|
content: '└';
|
|
|
|
|
position: absolute;
|
|
|
|
|
left: 0.5rem;
|
|
|
|
|
top: 50%;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
color: rgba(0,0,0,0.3);
|
|
|
|
|
font-size: 1.2rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.relation-badge {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
font-size: 0.65rem;
|
|
|
|
|
padding: 0.25rem 0.6rem;
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
color: white;
|
|
|
|
|
border-radius: 12px;
|
|
|
|
|
margin-right: 0.5rem;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-badge {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
padding: 0.35rem 0.8rem;
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-åben {
|
|
|
|
|
background: #fff3cd;
|
|
|
|
|
color: #856404;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.status-lukket {
|
|
|
|
|
background: #d4edda;
|
|
|
|
|
color: #155724;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-pills {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 0.5rem;
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-pill {
|
|
|
|
|
padding: 0.5rem 1rem;
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
border: 1px solid rgba(0,0,0,0.1);
|
|
|
|
|
background: var(--bg-card);
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
transition: all 0.2s;
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-pill:hover {
|
|
|
|
|
background: var(--accent-light);
|
|
|
|
|
border-color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.filter-pill.active {
|
|
|
|
|
background: var(--accent);
|
|
|
|
|
color: white;
|
|
|
|
|
border-color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stats-bar {
|
|
|
|
|
display: flex;
|
|
|
|
|
gap: 2rem;
|
|
|
|
|
margin-bottom: 1.5rem;
|
|
|
|
|
padding: 1rem;
|
|
|
|
|
background: var(--bg-card);
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-item {
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-value {
|
|
|
|
|
font-size: 1.5rem;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: var(--accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.stat-label {
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
color: var(--text-secondary);
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.empty-state {
|
|
|
|
|
text-align: center;
|
|
|
|
|
padding: 3rem;
|
|
|
|
|
color: var(--text-secondary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.empty-state i {
|
|
|
|
|
font-size: 3rem;
|
|
|
|
|
margin-bottom: 1rem;
|
|
|
|
|
opacity: 0.3;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
{% endblock %}
|
2026-02-01 00:38:10 +01:00
|
|
|
|
2026-02-01 11:58:44 +01:00
|
|
|
{% block content %}
|
|
|
|
|
<div class="container-fluid" style="max-width: 1400px; padding-top: 2rem;">
|
|
|
|
|
<!-- Header -->
|
|
|
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
|
|
|
<h1 style="margin: 0; color: var(--accent);">
|
|
|
|
|
<i class="bi bi-list-check me-2"></i>Sager
|
|
|
|
|
</h1>
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
<div class="d-flex gap-2">
|
|
|
|
|
<button class="btn btn-outline-primary" onclick="window.location.href='/sag/varekob-salg'">
|
|
|
|
|
<i class="bi bi-basket3 me-2"></i>Varekøb & Salg
|
|
|
|
|
</button>
|
|
|
|
|
<button class="btn btn-primary" style="background: var(--accent); border: none;" onclick="window.location.href='/sag/new'">
|
|
|
|
|
<i class="bi bi-plus-lg me-2"></i>Ny Sag
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2026-02-01 11:58:44 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Stats Bar -->
|
|
|
|
|
<div class="stats-bar">
|
|
|
|
|
<div class="stat-item">
|
|
|
|
|
<div class="stat-value">{{ sager|length }}</div>
|
|
|
|
|
<div class="stat-label">Total</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-item">
|
|
|
|
|
<div class="stat-value">{{ sager|selectattr('status', 'equalto', 'åben')|list|length }}</div>
|
|
|
|
|
<div class="stat-label">Åbne</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="stat-item">
|
|
|
|
|
<div class="stat-value">{{ sager|selectattr('status', 'equalto', 'lukket')|list|length }}</div>
|
|
|
|
|
<div class="stat-label">Lukkede</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Search & Filters -->
|
|
|
|
|
<div class="search-bar">
|
|
|
|
|
<input type="text"
|
|
|
|
|
class="form-control"
|
|
|
|
|
id="searchInput"
|
|
|
|
|
placeholder="🔍 Søg efter sag ID, titel, beskrivelse..."
|
|
|
|
|
autocomplete="off">
|
|
|
|
|
</div>
|
|
|
|
|
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
<div class="d-flex flex-wrap align-items-center gap-3 mb-3">
|
|
|
|
|
<div class="filter-pills">
|
|
|
|
|
<div class="filter-pill active" data-filter="all">Alle</div>
|
|
|
|
|
<div class="filter-pill" data-filter="åben">Åbne</div>
|
|
|
|
|
<div class="filter-pill" data-filter="lukket">Lukkede</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div style="min-width: 200px;">
|
|
|
|
|
<select class="form-select" id="typeFilter">
|
|
|
|
|
<option value="all">Alle typer</option>
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
2026-02-06 10:47:14 +01:00
|
|
|
<a class="btn btn-sm btn-outline-secondary" href="{{ toggle_include_deferred_url }}">
|
|
|
|
|
{% if include_deferred %}Skjul udsatte{% else %}Vis udsatte{% endif %}
|
|
|
|
|
</a>
|
2026-02-01 11:58:44 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Table -->
|
|
|
|
|
<div class="table-wrapper">
|
|
|
|
|
{% if sager %}
|
|
|
|
|
<table class="sag-table">
|
|
|
|
|
<thead>
|
|
|
|
|
<tr>
|
|
|
|
|
<th style="width: 90px;">ID</th>
|
|
|
|
|
<th>Titel & Beskrivelse</th>
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
<th style="width: 120px;">Type</th>
|
2026-02-01 11:58:44 +01:00
|
|
|
<th style="width: 180px;">Kunde</th>
|
|
|
|
|
<th style="width: 150px;">Hovedkontakt</th>
|
|
|
|
|
<th style="width: 100px;">Status</th>
|
2026-02-06 10:47:14 +01:00
|
|
|
<th style="width: 120px;">Udsat start</th>
|
2026-02-01 11:58:44 +01:00
|
|
|
<th style="width: 120px;">Oprettet</th>
|
|
|
|
|
<th style="width: 120px;">Opdateret</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody id="sagTableBody">
|
|
|
|
|
{% for sag in sager %}
|
|
|
|
|
{% if sag.id not in child_ids %}
|
|
|
|
|
{% set has_relations = sag.id in relations_map and relations_map[sag.id]|length > 0 %}
|
|
|
|
|
<tr class="tree-row {% if has_relations %}has-children{% endif %}"
|
|
|
|
|
data-sag-id="{{ sag.id }}"
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
data-status="{{ sag.status }}"
|
|
|
|
|
data-type="{{ sag.type or 'ticket' }}">
|
2026-02-01 11:58:44 +01:00
|
|
|
<td>
|
|
|
|
|
{% if has_relations %}
|
|
|
|
|
<span class="tree-toggle" onclick="toggleTreeNode(event, {{ sag.id }})">+</span>
|
|
|
|
|
{% endif %}
|
|
|
|
|
<span class="sag-id">#{{ sag.id }}</span>
|
|
|
|
|
</td>
|
|
|
|
|
<td onclick="window.location.href='/sag/{{ sag.id }}'">
|
|
|
|
|
<div class="sag-titel">{{ sag.titel }}</div>
|
2026-02-01 00:38:10 +01:00
|
|
|
{% if sag.beskrivelse %}
|
2026-02-01 11:58:44 +01:00
|
|
|
<div class="sag-beskrivelse">{{ sag.beskrivelse }}</div>
|
2026-02-01 00:38:10 +01:00
|
|
|
{% endif %}
|
2026-02-01 11:58:44 +01:00
|
|
|
</td>
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
<td onclick="window.location.href='/sag/{{ sag.id }}'">
|
|
|
|
|
<span class="badge bg-light text-dark border">{{ sag.type or 'ticket' }}</span>
|
|
|
|
|
</td>
|
2026-02-01 11:58:44 +01:00
|
|
|
<td onclick="window.location.href='/sag/{{ sag.id }}'" style="color: var(--text-secondary); font-size: 0.85rem;">
|
|
|
|
|
{{ sag.customer_name if sag.customer_name else '-' }}
|
|
|
|
|
</td>
|
|
|
|
|
<td onclick="window.location.href='/sag/{{ sag.id }}'" style="color: var(--text-secondary); font-size: 0.85rem;">
|
|
|
|
|
{{ sag.kontakt_navn if sag.kontakt_navn and sag.kontakt_navn.strip() else '-' }}
|
|
|
|
|
</td>
|
|
|
|
|
<td onclick="window.location.href='/sag/{{ sag.id }}'">
|
|
|
|
|
<span class="status-badge status-{{ sag.status }}">{{ sag.status }}</span>
|
|
|
|
|
</td>
|
2026-02-06 10:47:14 +01:00
|
|
|
<td onclick="window.location.href='/sag/{{ sag.id }}'" style="color: var(--text-secondary);">
|
|
|
|
|
{{ sag.deferred_until.strftime('%d/%m-%Y') if sag.deferred_until else '-' }}
|
|
|
|
|
</td>
|
2026-02-01 11:58:44 +01:00
|
|
|
<td onclick="window.location.href='/sag/{{ sag.id }}'" style="color: var(--text-secondary);">
|
|
|
|
|
{{ sag.created_at.strftime('%d/%m-%Y') if sag.created_at else '-' }}
|
|
|
|
|
</td>
|
|
|
|
|
<td onclick="window.location.href='/sag/{{ sag.id }}'" style="color: var(--text-secondary);">
|
|
|
|
|
{{ sag.updated_at.strftime('%d/%m-%Y') if sag.updated_at else '-' }}
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
{% if has_relations %}
|
|
|
|
|
{% set seen_targets = [] %}
|
|
|
|
|
{% for rel in relations_map[sag.id] %}
|
|
|
|
|
{% set related_sag = sager|selectattr('id', 'equalto', rel.target_id)|first %}
|
|
|
|
|
{% if related_sag and rel.target_id not in seen_targets %}
|
|
|
|
|
{% set _ = seen_targets.append(rel.target_id) %}
|
|
|
|
|
{% set all_rel_types = relations_map[sag.id]|selectattr('target_id', 'equalto', rel.target_id)|map(attribute='type')|list %}
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
<tr class="tree-child" data-parent="{{ sag.id }}" data-status="{{ related_sag.status }}" data-type="{{ related_sag.type or 'ticket' }}" style="display: none;">
|
2026-02-01 11:58:44 +01:00
|
|
|
<td>
|
|
|
|
|
<span class="sag-id">#{{ related_sag.id }}</span>
|
|
|
|
|
</td>
|
|
|
|
|
<td onclick="window.location.href='/sag/{{ related_sag.id }}'">
|
|
|
|
|
{% for rt in all_rel_types %}
|
|
|
|
|
<span class="relation-badge">{{ rt }}</span>
|
|
|
|
|
{% endfor %}
|
|
|
|
|
<div class="sag-titel" style="display: inline;">{{ related_sag.titel }}</div>
|
|
|
|
|
{% if related_sag.beskrivelse %}
|
|
|
|
|
<div class="sag-beskrivelse">{{ related_sag.beskrivelse }}</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</td>
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
<td onclick="window.location.href='/sag/{{ related_sag.id }}'">
|
|
|
|
|
<span class="badge bg-light text-dark border">{{ related_sag.type or 'ticket' }}</span>
|
|
|
|
|
</td>
|
2026-02-01 11:58:44 +01:00
|
|
|
<td onclick="window.location.href='/sag/{{ related_sag.id }}'" style="color: var(--text-secondary); font-size: 0.85rem;">
|
|
|
|
|
{{ related_sag.customer_name if related_sag.customer_name else '-' }}
|
|
|
|
|
</td>
|
|
|
|
|
<td onclick="window.location.href='/sag/{{ related_sag.id }}'" style="color: var(--text-secondary); font-size: 0.85rem;">
|
|
|
|
|
{{ related_sag.kontakt_navn if related_sag.kontakt_navn and related_sag.kontakt_navn.strip() else '-' }}
|
|
|
|
|
</td>
|
|
|
|
|
<td onclick="window.location.href='/sag/{{ related_sag.id }}'">
|
|
|
|
|
<span class="status-badge status-{{ related_sag.status }}">{{ related_sag.status }}</span>
|
|
|
|
|
</td>
|
2026-02-06 10:47:14 +01:00
|
|
|
<td onclick="window.location.href='/sag/{{ related_sag.id }}'" style="color: var(--text-secondary);">
|
|
|
|
|
{{ related_sag.deferred_until.strftime('%d/%m-%Y') if related_sag.deferred_until else '-' }}
|
|
|
|
|
</td>
|
2026-02-01 11:58:44 +01:00
|
|
|
<td onclick="window.location.href='/sag/{{ related_sag.id }}'" style="color: var(--text-secondary);">
|
|
|
|
|
{{ related_sag.created_at.strftime('%d/%m-%Y') if related_sag.created_at else '-' }}
|
|
|
|
|
</td>
|
|
|
|
|
<td onclick="window.location.href='/sag/{{ related_sag.id }}'" style="color: var(--text-secondary);">
|
|
|
|
|
{{ related_sag.updated_at.strftime('%d/%m-%Y') if related_sag.updated_at else '-' }}
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
{% endif %}
|
2026-02-01 00:38:10 +01:00
|
|
|
{% endfor %}
|
|
|
|
|
{% endif %}
|
2026-02-01 11:58:44 +01:00
|
|
|
{% endif %}
|
|
|
|
|
{% endfor %}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
{% else %}
|
|
|
|
|
<div class="empty-state">
|
|
|
|
|
<i class="bi bi-inbox"></i>
|
|
|
|
|
<p>Ingen sager fundet</p>
|
2026-02-01 00:38:10 +01:00
|
|
|
</div>
|
2026-02-01 11:58:44 +01:00
|
|
|
{% endif %}
|
2026-01-29 23:07:33 +01:00
|
|
|
</div>
|
2026-02-01 11:58:44 +01:00
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
// Tree toggle functionality
|
|
|
|
|
function toggleTreeNode(event, sagId) {
|
|
|
|
|
event.stopPropagation();
|
|
|
|
|
const toggle = event.currentTarget || event.target;
|
|
|
|
|
const row = toggle.closest('tr');
|
2026-01-31 23:16:24 +01:00
|
|
|
|
2026-02-01 11:58:44 +01:00
|
|
|
if (!row) {
|
|
|
|
|
return;
|
2026-02-01 00:38:10 +01:00
|
|
|
}
|
2026-02-01 11:58:44 +01:00
|
|
|
|
|
|
|
|
if (row.classList.contains('expanded')) {
|
|
|
|
|
row.classList.remove('expanded');
|
|
|
|
|
toggle.textContent = '+';
|
|
|
|
|
} else {
|
|
|
|
|
row.classList.add('expanded');
|
|
|
|
|
toggle.textContent = '-';
|
2026-02-01 00:38:10 +01:00
|
|
|
}
|
2026-02-01 11:58:44 +01:00
|
|
|
|
|
|
|
|
applyFilters();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Search functionality
|
|
|
|
|
const searchInput = document.getElementById('searchInput');
|
|
|
|
|
const allRows = document.querySelectorAll('.tree-row');
|
|
|
|
|
let currentSearch = '';
|
|
|
|
|
let currentFilter = 'all';
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
let currentType = 'all';
|
2026-02-01 11:58:44 +01:00
|
|
|
|
|
|
|
|
function applyFilters() {
|
|
|
|
|
const search = currentSearch;
|
|
|
|
|
|
|
|
|
|
allRows.forEach(row => {
|
|
|
|
|
const text = row.textContent.toLowerCase();
|
|
|
|
|
const status = row.dataset.status;
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
const type = row.dataset.type || 'ticket';
|
2026-02-01 11:58:44 +01:00
|
|
|
const matchesSearch = text.includes(search);
|
|
|
|
|
const matchesFilter = currentFilter === 'all' || status === currentFilter;
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
const matchesType = currentType === 'all' || type === currentType;
|
|
|
|
|
const visible = matchesSearch && matchesFilter && matchesType;
|
2026-02-01 11:58:44 +01:00
|
|
|
|
|
|
|
|
row.style.display = visible ? '' : 'none';
|
|
|
|
|
|
|
|
|
|
const sagId = row.dataset.sagId;
|
|
|
|
|
if (sagId) {
|
|
|
|
|
const children = document.querySelectorAll(`tr[data-parent="${sagId}"]`);
|
|
|
|
|
children.forEach(child => {
|
|
|
|
|
const childText = child.textContent.toLowerCase();
|
|
|
|
|
const childStatus = child.dataset.status;
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
const childType = child.dataset.type || 'ticket';
|
2026-02-01 11:58:44 +01:00
|
|
|
const childMatchesSearch = childText.includes(search);
|
|
|
|
|
const childMatchesFilter = currentFilter === 'all' || childStatus === currentFilter;
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
const childMatchesType = currentType === 'all' || childType === currentType;
|
|
|
|
|
const childVisible = visible && row.classList.contains('expanded') && childMatchesSearch && childMatchesFilter && childMatchesType;
|
2026-02-01 11:58:44 +01:00
|
|
|
child.style.display = childVisible ? '' : 'none';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (searchInput) {
|
|
|
|
|
searchInput.addEventListener('input', function(e) {
|
|
|
|
|
currentSearch = e.target.value.toLowerCase();
|
|
|
|
|
applyFilters();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Filter functionality
|
|
|
|
|
const filterPills = document.querySelectorAll('.filter-pill');
|
|
|
|
|
|
|
|
|
|
filterPills.forEach(pill => {
|
|
|
|
|
pill.addEventListener('click', function() {
|
|
|
|
|
// Update active state
|
|
|
|
|
filterPills.forEach(p => p.classList.remove('active'));
|
|
|
|
|
this.classList.add('active');
|
|
|
|
|
|
|
|
|
|
currentFilter = this.dataset.filter || 'all';
|
|
|
|
|
applyFilters();
|
2026-01-29 23:07:33 +01:00
|
|
|
});
|
2026-02-01 11:58:44 +01:00
|
|
|
});
|
feat(sag): Add Varekøb & Salg module with database migration and frontend template
- Created a new SQL migration for the sag_salgsvarer table to manage sales and purchase items.
- Implemented a new HTML template for the Varekøb & Salg module, including summary cards and tables for sales and purchases.
- Added JavaScript functions for loading and rendering order data dynamically.
- Introduced a new backend search module for customers, contacts, hardware, and locations with autocomplete functionality.
- Developed an email templates API for managing system and customer-specific email templates.
- Created multiple migrations for Nextcloud instances, cache, audit logs, email templates, sag comments, hardware locations, and billing methods.
- Enhanced the sag module with solutions, order lines, work types, and 2FA support for user authentication.
2026-02-02 20:23:56 +01:00
|
|
|
|
|
|
|
|
const typeFilter = document.getElementById('typeFilter');
|
|
|
|
|
if (typeFilter) {
|
|
|
|
|
typeFilter.addEventListener('change', function() {
|
|
|
|
|
currentType = this.value || 'all';
|
|
|
|
|
applyFilters();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function loadTypeFilters() {
|
|
|
|
|
if (!typeFilter) return;
|
|
|
|
|
try {
|
|
|
|
|
const res = await fetch('/api/v1/settings/case_types');
|
|
|
|
|
if (!res.ok) return;
|
|
|
|
|
const setting = await res.json();
|
|
|
|
|
const types = JSON.parse(setting.value || '[]');
|
|
|
|
|
if (!Array.isArray(types) || types.length === 0) return;
|
|
|
|
|
|
|
|
|
|
typeFilter.innerHTML = `<option value="all">Alle typer</option>` +
|
|
|
|
|
types.map(type => `<option value="${type}">${type}</option>`).join('');
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Failed to load case types', err);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
loadTypeFilters();
|
2026-02-01 11:58:44 +01:00
|
|
|
</script>
|
|
|
|
|
{% endblock %}
|