Fix SAG detail right column nesting

This commit is contained in:
Christian 2026-03-18 09:58:31 +01:00
parent 60d692c085
commit 73803f894b
2 changed files with 172 additions and 169 deletions

14
RELEASE_NOTES_v2.2.62.md Normal file
View File

@ -0,0 +1,14 @@
# Release Notes v2.2.62
Dato: 18. marts 2026
## Fixes
- Rettet grid/nesting i SAG detaljevisning, så højre kolonne ligger i samme row som venstre/center.
- `Hardware`, `Salgspipeline`, `Opkaldshistorik` og `Todo-opgaver` vises nu i højre kolonne som forventet.
- Fjernet en for tidlig afsluttende `</div>` i detaljer-layoutet, som tidligere fik højre modulkolonne til at falde ned under venstre indhold.
## Berørte filer
- `app/modules/sag/templates/detail.html`
- `RELEASE_NOTES_v2.2.62.md`

View File

@ -1429,161 +1429,6 @@
{% endif %}
</div>
</div></div>
<div class="row mb-3">
<div class="col-12 mb-3">
<div class="card h-100 d-flex flex-column" data-module="pipeline" data-has-content="{{ 'true' if case.pipeline_stage_id or case.pipeline_amount or case.pipeline_probability else 'false' }}">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0" style="color: var(--accent);">📈 Salgspipeline</h6>
<button id="pipelineEditToggle" class="btn btn-sm btn-outline-primary" onclick="togglePipelineEdit()">
<i class="bi bi-pencil"></i>
</button>
</div>
<div class="card-body">
<div id="pipelineViewMode">
<div class="row g-3">
<div class="col-md-4">
<div class="summary-item h-100">
<div class="summary-label">Stage</div>
<div class="summary-value">
{% set ns = namespace(selected_stage=None) %}
{% for stage in pipeline_stages or [] %}
{% if case.pipeline_stage_id == stage.id %}
{% set ns.selected_stage = stage %}
{% endif %}
{% endfor %}
{% if ns.selected_stage %}
<span class="badge" style="background: {{ ns.selected_stage.color or '#0f4c75' }};">{{ ns.selected_stage.name }}</span>
{% else %}
<span class="text-muted">Ikke sat</span>
{% endif %}
</div>
</div>
</div>
<div class="col-md-4">
<div class="summary-item h-100">
<div class="summary-label">Sandsynlighed</div>
<div class="summary-value">{{ case.pipeline_probability if case.pipeline_probability is not none else 0 }}%</div>
</div>
</div>
<div class="col-md-4">
<div class="summary-item h-100">
<div class="summary-label">Beløb</div>
<div class="summary-value">
{% if case.pipeline_amount is not none %}
{{ "{:,.2f}".format(case.pipeline_amount|float).replace(',', 'X').replace('.', ',').replace('X', '.') }} kr.
{% else %}
<span class="text-muted">Ikke sat</span>
{% endif %}
</div>
</div>
</div>
</div>
<div class="mt-3">
<div class="summary-item">
<div class="summary-label">Beskrivelse</div>
<div class="summary-value" style="white-space: pre-wrap;">{{ case.pipeline_description or 'Ingen beskrivelse' }}</div>
</div>
</div>
</div>
<div id="pipelineEditMode" class="d-none">
<div class="row g-3">
<div class="col-md-4">
<label class="form-label small text-muted">Stage</label>
<select id="pipelineStageSelect" class="form-select form-select-sm">
<option value="">Ikke sat</option>
{% for stage in pipeline_stages or [] %}
<option value="{{ stage.id }}" {% if case.pipeline_stage_id == stage.id %}selected{% endif %}>{{ stage.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-4">
<label class="form-label small text-muted">Sandsynlighed (%)</label>
<input id="pipelineProbabilityInput" type="number" min="0" max="100" class="form-control form-control-sm" value="{{ case.pipeline_probability if case.pipeline_probability is not none else '' }}">
</div>
<div class="col-md-4">
<label class="form-label small text-muted">Beløb (kr.)</label>
<input id="pipelineAmountInput" type="number" min="0" step="0.01" class="form-control form-control-sm" value="{{ case.pipeline_amount if case.pipeline_amount is not none else '' }}">
</div>
<div class="col-12">
<label class="form-label small text-muted">Beskrivelse</label>
<textarea id="pipelineDescriptionInput" rows="3" class="form-control form-control-sm" placeholder="Skriv kort note for denne pipeline-entry...">{{ case.pipeline_description or '' }}</textarea>
</div>
</div>
<div class="d-flex justify-content-end gap-2 mt-3">
<button class="btn btn-sm btn-outline-secondary" onclick="togglePipelineEdit(false)">Annuller</button>
<button class="btn btn-sm btn-primary" onclick="savePipeline()">Gem pipeline</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row mb-3">
<div class="col-12 mb-3">
<div class="card h-100 d-flex flex-column" data-module="call-history" data-has-content="{{ 'true' if call_history else 'false' }}">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0" style="color: var(--accent);">📞 Opkaldshistorik</h6>
<a href="/telefoni" class="btn btn-sm btn-outline-primary">
<i class="bi bi-telephone"></i>
</a>
</div>
<div class="card-body p-0">
{% if call_history and call_history|length > 0 %}
<div class="table-responsive">
<table class="table table-sm table-hover mb-0 align-middle">
<thead>
<tr>
<th class="ps-3">Dato</th>
<th>Retning</th>
<th>Nummer</th>
<th>Bruger</th>
<th class="text-end pe-3">Varighed</th>
</tr>
</thead>
<tbody>
{% for call in call_history %}
<tr>
<td class="ps-3">{{ call.started_at.strftime('%d/%m/%Y %H:%M') if call.started_at else '-' }}</td>
<td>{{ 'Udgående' if call.direction == 'outbound' else 'Indgående' }}</td>
<td>
{% if call.ekstern_nummer %}
<div class="d-flex gap-2 align-items-center flex-wrap">
<span>{{ call.ekstern_nummer }}</span>
<button type="button" class="btn btn-sm btn-outline-success" onclick="ringOutFromCase('{{ call.ekstern_nummer }}')">
Ring op
</button>
</div>
{% else %}
-
{% endif %}
</td>
<td>{{ call.full_name or call.username or '-' }}</td>
<td class="text-end pe-3">
{% if call.duration_sec is not none %}
{{ (call.duration_sec // 60)|int }}:{{ '%02d'|format((call.duration_sec % 60)|int) }}
{% elif call.ended_at %}
-
{% else %}
I gang
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="p-3 text-muted text-center">Ingen opkald linket til denne sag</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
<!-- TREDELT-2: Hero, Info -->
<div class="col-xl-8 order-1 order-xl-2" id="inner-center-col">
@ -3531,7 +3376,6 @@
</div>
</div></div><!-- slut inner cols -->
</div>
<div class="col-xl-3 col-lg-4" id="case-right-column">
<div class="right-modules-grid">
<div class="card d-flex flex-column h-100 right-module-card" data-module="hardware" data-has-content="unknown">
@ -3548,26 +3392,151 @@
</div>
</div>
<div class="card h-100 d-flex flex-column right-module-card" data-module="wiki" data-has-content="unknown">
<div class="card h-100 d-flex flex-column right-module-card" data-module="pipeline" data-has-content="{{ 'true' if case.pipeline_stage_id or case.pipeline_amount or case.pipeline_probability else 'false' }}">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0" style="color: var(--accent); font-size: 0.85rem;">Kunde-wiki</h6>
<h6 class="mb-0" style="color: var(--accent);">📈 Salgspipeline</h6>
<button id="pipelineEditToggle" class="btn btn-sm btn-outline-primary" onclick="togglePipelineEdit()">
<i class="bi bi-pencil"></i>
</button>
</div>
<div class="card-body flex-grow-1 p-0" style="max-height: 220px; overflow: auto;">
<div class="p-2 border-bottom">
<input type="text" class="form-control form-control-sm" id="wikiSearchInput" placeholder="Soeg i Wiki (tom = guide)" style="font-size: 0.8rem;">
<div class="card-body">
<div id="pipelineViewMode">
<div class="row g-3">
<div class="col-12">
<div class="summary-item h-100">
<div class="summary-label">Stage</div>
<div class="summary-value">
{% set ns = namespace(selected_stage=None) %}
{% for stage in pipeline_stages or [] %}
{% if case.pipeline_stage_id == stage.id %}
{% set ns.selected_stage = stage %}
{% endif %}
{% endfor %}
{% if ns.selected_stage %}
<span class="badge" style="background: {{ ns.selected_stage.color or '#0f4c75' }};">{{ ns.selected_stage.name }}</span>
{% else %}
<span class="text-muted">Ikke sat</span>
{% endif %}
</div>
</div>
</div>
<div class="col-6">
<div class="summary-item h-100">
<div class="summary-label">Sandsynlighed</div>
<div class="summary-value">{{ case.pipeline_probability if case.pipeline_probability is not none else 0 }}%</div>
</div>
</div>
<div class="col-6">
<div class="summary-item h-100">
<div class="summary-label">Beløb</div>
<div class="summary-value">
{% if case.pipeline_amount is not none %}
{{ "{:,.2f}".format(case.pipeline_amount|float).replace(',', 'X').replace('.', ',').replace('X', '.') }} kr.
{% else %}
<span class="text-muted">Ikke sat</span>
{% endif %}
</div>
</div>
</div>
</div>
<div class="mt-3">
<div class="summary-item">
<div class="summary-label">Beskrivelse</div>
<div class="summary-value" style="white-space: pre-wrap;">{{ case.pipeline_description or 'Ingen beskrivelse' }}</div>
</div>
</div>
</div>
<div class="list-group list-group-flush" id="wiki-list">
<div class="p-3 text-center text-muted">Henter wiki...</div>
<div id="pipelineEditMode" class="d-none">
<div class="row g-3">
<div class="col-12">
<label class="form-label small text-muted">Stage</label>
<select id="pipelineStageSelect" class="form-select form-select-sm">
<option value="">Ikke sat</option>
{% for stage in pipeline_stages or [] %}
<option value="{{ stage.id }}" {% if case.pipeline_stage_id == stage.id %}selected{% endif %}>{{ stage.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-6">
<label class="form-label small text-muted">Sandsynlighed (%)</label>
<input id="pipelineProbabilityInput" type="number" min="0" max="100" class="form-control form-control-sm" value="{{ case.pipeline_probability if case.pipeline_probability is not none else '' }}">
</div>
<div class="col-6">
<label class="form-label small text-muted">Beløb (kr.)</label>
<input id="pipelineAmountInput" type="number" min="0" step="0.01" class="form-control form-control-sm" value="{{ case.pipeline_amount if case.pipeline_amount is not none else '' }}">
</div>
<div class="col-12">
<label class="form-label small text-muted">Beskrivelse</label>
<textarea id="pipelineDescriptionInput" rows="3" class="form-control form-control-sm" placeholder="Skriv kort note for denne pipeline-entry...">{{ case.pipeline_description or '' }}</textarea>
</div>
</div>
<div class="d-flex justify-content-end gap-2 mt-3">
<button class="btn btn-sm btn-outline-secondary" onclick="togglePipelineEdit(false)">Annuller</button>
<button class="btn btn-sm btn-primary" onclick="savePipeline()">Gem pipeline</button>
</div>
</div>
</div>
</div>
<div class="card h-100 d-flex flex-column right-module-card" data-module="call-history" data-has-content="{{ 'true' if call_history else 'false' }}">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0" style="color: var(--accent);">📞 Opkaldshistorik</h6>
<a href="/telefoni" class="btn btn-sm btn-outline-primary">
<i class="bi bi-telephone"></i>
</a>
</div>
<div class="card-body p-0">
{% if call_history and call_history|length > 0 %}
<div class="table-responsive">
<table class="table table-sm table-hover mb-0 align-middle">
<thead>
<tr>
<th class="ps-3">Dato</th>
<th>Retning</th>
<th>Nummer</th>
<th>Bruger</th>
<th class="text-end pe-3">Varighed</th>
</tr>
</thead>
<tbody>
{% for call in call_history %}
<tr>
<td class="ps-3">{{ call.started_at.strftime('%d/%m/%Y %H:%M') if call.started_at else '-' }}</td>
<td>{{ 'Udgående' if call.direction == 'outbound' else 'Indgående' }}</td>
<td>
{% if call.ekstern_nummer %}
<div class="d-flex gap-2 align-items-center flex-wrap">
<span>{{ call.ekstern_nummer }}</span>
<button type="button" class="btn btn-sm btn-outline-success" onclick="ringOutFromCase('{{ call.ekstern_nummer }}')">
Ring op
</button>
</div>
{% else %}
-
{% endif %}
</td>
<td>{{ call.full_name or call.username or '-' }}</td>
<td class="text-end pe-3">
{% if call.duration_sec is not none %}
{{ (call.duration_sec // 60)|int }}:{{ '%02d'|format((call.duration_sec % 60)|int) }}
{% elif call.ended_at %}
-
{% else %}
I gang
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="p-3 text-muted text-center">Ingen opkald linket til denne sag</div>
{% endif %}
</div>
</div>
<div class="card h-100 d-flex flex-column right-module-card" data-module="todo-steps" data-has-content="unknown">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0" style="color: var(--accent);">✅ Todo-opgaver</h6>
@ -3590,6 +3559,26 @@
</div>
</div>
<div class="card h-100 d-flex flex-column right-module-card" data-module="wiki" data-has-content="unknown">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="mb-0" style="color: var(--accent); font-size: 0.85rem;">Kunde-wiki</h6>
</div>
<div class="card-body flex-grow-1 p-0" style="max-height: 220px; overflow: auto;">
<div class="p-2 border-bottom">
<input type="text" class="form-control form-control-sm" id="wikiSearchInput" placeholder="Soeg i Wiki (tom = guide)" style="font-size: 0.8rem;">
</div>
<div class="list-group list-group-flush" id="wiki-list">
<div class="p-3 text-center text-muted">Henter wiki...</div>
</div>
</div>
</div>
</div>
</div>
</div>