Fix SAG detail right column nesting
This commit is contained in:
parent
60d692c085
commit
73803f894b
14
RELEASE_NOTES_v2.2.62.md
Normal file
14
RELEASE_NOTES_v2.2.62.md
Normal 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`
|
||||
@ -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,24 +3392,149 @@
|
||||
</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 class="list-group list-group-flush" id="wiki-list">
|
||||
<div class="p-3 text-center text-muted">Henter wiki...</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 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">
|
||||
@ -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>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user