hotfix: server-render initial telephony calls
This commit is contained in:
parent
93da2866dc
commit
b1a4342a9a
15
RELEASE_NOTES_v2.2.85.md
Normal file
15
RELEASE_NOTES_v2.2.85.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# Release Notes v2.2.85
|
||||||
|
|
||||||
|
Dato: 2026-05-04
|
||||||
|
|
||||||
|
## Hotfix
|
||||||
|
|
||||||
|
- Telefoni-siden (`/telefoni`) rendrer nu seneste opkald server-side ved page load (SSR fallback).
|
||||||
|
- Dette sikrer, at brugeren ser opkald med det samme, selv hvis browserens JS/rendering/filter-state fejler eller er cachet.
|
||||||
|
- Klient-side `loadCalls()` koerer stadig bagefter og opdaterer tabellen som foer.
|
||||||
|
|
||||||
|
## Berorte filer
|
||||||
|
|
||||||
|
- `app/modules/telefoni/frontend/views.py`
|
||||||
|
- `app/modules/telefoni/templates/log.html`
|
||||||
|
- `VERSION`
|
||||||
@ -3,6 +3,7 @@ import logging
|
|||||||
from fastapi import APIRouter, Request
|
from fastapi import APIRouter, Request
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
|
from app.core.database import execute_query
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@ -11,4 +12,43 @@ templates = Jinja2Templates(directory="app")
|
|||||||
|
|
||||||
@router.get("/telefoni", response_class=HTMLResponse)
|
@router.get("/telefoni", response_class=HTMLResponse)
|
||||||
async def telefoni_log_page(request: Request):
|
async def telefoni_log_page(request: Request):
|
||||||
return templates.TemplateResponse("modules/telefoni/templates/log.html", {"request": request})
|
initial_calls = []
|
||||||
|
try:
|
||||||
|
initial_calls = execute_query(
|
||||||
|
"""
|
||||||
|
SELECT
|
||||||
|
t.id,
|
||||||
|
t.direction,
|
||||||
|
COALESCE(
|
||||||
|
NULLIF(TRIM(t.ekstern_nummer), ''),
|
||||||
|
NULLIF(TRIM(t.raw_payload->>'caller'), ''),
|
||||||
|
NULLIF(TRIM(t.raw_payload->>'callee'), '')
|
||||||
|
) AS display_number,
|
||||||
|
t.started_at,
|
||||||
|
t.duration_sec,
|
||||||
|
t.ended_at,
|
||||||
|
u.full_name,
|
||||||
|
u.username,
|
||||||
|
t.kontakt_id,
|
||||||
|
CONCAT(COALESCE(c.first_name, ''), ' ', COALESCE(c.last_name, '')) AS contact_name,
|
||||||
|
t.sag_id,
|
||||||
|
s.titel AS sag_titel
|
||||||
|
FROM telefoni_opkald t
|
||||||
|
LEFT JOIN users u ON u.user_id = t.bruger_id
|
||||||
|
LEFT JOIN contacts c ON c.id = t.kontakt_id
|
||||||
|
LEFT JOIN sag_sager s ON s.id = t.sag_id
|
||||||
|
ORDER BY t.started_at DESC
|
||||||
|
LIMIT 50
|
||||||
|
""",
|
||||||
|
(),
|
||||||
|
) or []
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("⚠️ Could not load initial telefoni calls for SSR fallback: %s", e)
|
||||||
|
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
"modules/telefoni/templates/log.html",
|
||||||
|
{
|
||||||
|
"request": request,
|
||||||
|
"initial_calls": initial_calls,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|||||||
@ -59,7 +59,41 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="telefoniRows">
|
<tbody id="telefoniRows">
|
||||||
<tr><td colspan="7" class="text-muted small">Indlæser...</td></tr>
|
{% if initial_calls and initial_calls|length > 0 %}
|
||||||
|
{% for r in initial_calls %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ r.started_at or '-' }}</td>
|
||||||
|
<td>{{ r.full_name or r.username or '-' }}</td>
|
||||||
|
<td>{{ 'Udgående' if r.direction == 'outbound' else 'Indgående' }}</td>
|
||||||
|
<td>{{ r.display_number or '-' }}</td>
|
||||||
|
<td>
|
||||||
|
{% if r.kontakt_id %}
|
||||||
|
<a href="/contacts/{{ r.kontakt_id }}">{{ r.contact_name or ('Kontakt #' ~ r.kontakt_id) }}</a>
|
||||||
|
{% else %}
|
||||||
|
-
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if r.sag_id %}
|
||||||
|
<a href="/sag/{{ r.sag_id }}/v3">{{ r.sag_titel or ('Sag #' ~ r.sag_id) }}</a>
|
||||||
|
{% else %}
|
||||||
|
-
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="text-end">
|
||||||
|
{% if r.duration_sec is not none %}
|
||||||
|
{{ r.duration_sec }}s
|
||||||
|
{% elif r.ended_at %}
|
||||||
|
-
|
||||||
|
{% else %}
|
||||||
|
I gang
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
<tr><td colspan="7" class="text-muted small">Indlæser...</td></tr>
|
||||||
|
{% endif %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@ -178,6 +212,7 @@ function escapeHtml(str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let telefoniCurrentUserId = null;
|
let telefoniCurrentUserId = null;
|
||||||
|
let telefoniAutoResetTried = false;
|
||||||
const telefoniCallMap = new Map();
|
const telefoniCallMap = new Map();
|
||||||
const linkSagState = {
|
const linkSagState = {
|
||||||
callId: null,
|
callId: null,
|
||||||
@ -846,10 +881,22 @@ async function loadCalls() {
|
|||||||
telefoniCallMap.clear();
|
telefoniCallMap.clear();
|
||||||
(rows || []).forEach(r => telefoniCallMap.set(Number(r.id), r));
|
(rows || []).forEach(r => telefoniCallMap.set(Number(r.id), r));
|
||||||
if (!rows || rows.length === 0) {
|
if (!rows || rows.length === 0) {
|
||||||
|
const hadFilters = Boolean(userId || from || to || withoutCase);
|
||||||
|
if (hadFilters && !telefoniAutoResetTried) {
|
||||||
|
telefoniAutoResetTried = true;
|
||||||
|
document.getElementById('filterUser').value = '';
|
||||||
|
document.getElementById('filterFrom').value = '';
|
||||||
|
document.getElementById('filterTo').value = '';
|
||||||
|
document.getElementById('filterWithoutCase').checked = false;
|
||||||
|
await loadCalls();
|
||||||
|
return;
|
||||||
|
}
|
||||||
tbody.innerHTML = '<tr><td colspan="7" class="text-muted small">Ingen opkald fundet</td></tr>';
|
tbody.innerHTML = '<tr><td colspan="7" class="text-muted small">Ingen opkald fundet</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
telefoniAutoResetTried = false;
|
||||||
|
|
||||||
tbody.innerHTML = rows.map(r => {
|
tbody.innerHTML = rows.map(r => {
|
||||||
const started = r.started_at ? new Date(r.started_at) : null;
|
const started = r.started_at ? new Date(r.started_at) : null;
|
||||||
const dateTxt = started ? started.toLocaleString('da-DK') : '-';
|
const dateTxt = started ? started.toLocaleString('da-DK') : '-';
|
||||||
@ -974,6 +1021,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
if (fromFilter) fromFilter.value = '';
|
if (fromFilter) fromFilter.value = '';
|
||||||
if (toFilter) toFilter.value = '';
|
if (toFilter) toFilter.value = '';
|
||||||
if (withoutCaseFilter) withoutCaseFilter.checked = false;
|
if (withoutCaseFilter) withoutCaseFilter.checked = false;
|
||||||
|
telefoniAutoResetTried = false;
|
||||||
|
|
||||||
await loadUsers();
|
await loadUsers();
|
||||||
document.getElementById('btnRefresh').addEventListener('click', loadCalls);
|
document.getElementById('btnRefresh').addEventListener('click', loadCalls);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user