release: v2.2.57 email+sag tab stability
This commit is contained in:
parent
eb5e14e2a1
commit
9a3ada380f
18
RELEASE_NOTES_v2.2.57.md
Normal file
18
RELEASE_NOTES_v2.2.57.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Release Notes v2.2.57
|
||||
|
||||
Dato: 2026-03-18
|
||||
|
||||
## Fokus
|
||||
Stabilisering af UI i Email- og SAG-modulerne.
|
||||
|
||||
## Aendringer
|
||||
- Email-visning: yderligere hardening af HTML-tabeller i mail-body, inklusive normalisering af inline styles for at undgaa layout break.
|
||||
- Email-visning: forbedret overflow-haandtering for bredt indhold (tabeller, celler og media).
|
||||
- SAG detaljeside: forbedret tab-loading, saa data hentes ved faneskift for Varekob & Salg, Abonnement og Paamindelser.
|
||||
- SAG detaljeside: robust fallback for reminder user-id via `/api/v1/auth/me`.
|
||||
- SAG detaljeside: rettet API-kald for reminders og kalender til stabil case-id reference.
|
||||
|
||||
## Berorte filer
|
||||
- app/emails/frontend/emails.html
|
||||
- app/modules/sag/templates/detail.html
|
||||
- RELEASE_NOTES_v2.2.57.md
|
||||
@ -323,7 +323,38 @@
|
||||
.email-html-body table {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
width: 100%;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
table-layout: auto !important;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.email-html-body tbody,
|
||||
.email-html-body thead,
|
||||
.email-html-body tfoot,
|
||||
.email-html-body tr,
|
||||
.email-html-body td,
|
||||
.email-html-body th {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.email-html-body td,
|
||||
.email-html-body th {
|
||||
white-space: normal !important;
|
||||
word-break: break-word !important;
|
||||
overflow-wrap: anywhere !important;
|
||||
}
|
||||
|
||||
.email-html-body img,
|
||||
.email-html-body video,
|
||||
.email-html-body iframe {
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
.email-html-body [style*="position:fixed"],
|
||||
.email-html-body [style*="position: fixed"] {
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
.email-body iframe {
|
||||
@ -1886,9 +1917,39 @@ function renderEmailDetail(email) {
|
||||
// If HTML, inject it as innerHTML after rendering
|
||||
if (email.body_html) {
|
||||
const htmlDiv = pane.querySelector('.email-html-body');
|
||||
if (htmlDiv) htmlDiv.innerHTML = email.body_html;
|
||||
if (htmlDiv) {
|
||||
htmlDiv.innerHTML = email.body_html;
|
||||
normalizeEmailHtmlLayout(htmlDiv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeEmailHtmlLayout(container) {
|
||||
if (!container) return;
|
||||
|
||||
const tables = container.querySelectorAll('table');
|
||||
tables.forEach((table) => {
|
||||
table.style.maxWidth = '100%';
|
||||
table.style.width = '100%';
|
||||
table.style.tableLayout = 'auto';
|
||||
table.removeAttribute('width');
|
||||
});
|
||||
|
||||
const cells = container.querySelectorAll('td, th');
|
||||
cells.forEach((cell) => {
|
||||
cell.style.whiteSpace = 'normal';
|
||||
cell.style.wordBreak = 'break-word';
|
||||
cell.style.overflowWrap = 'anywhere';
|
||||
});
|
||||
|
||||
const images = container.querySelectorAll('img, iframe, video');
|
||||
images.forEach((el) => {
|
||||
el.style.maxWidth = '100%';
|
||||
if (el.tagName === 'IMG' || el.tagName === 'VIDEO') {
|
||||
el.style.height = 'auto';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderEmailAnalysis(email) {
|
||||
const aiAnalysisTab = document.getElementById('aiAnalysisTab');
|
||||
|
||||
@ -2286,6 +2286,27 @@
|
||||
todoForm.addEventListener('submit', createTodoStep);
|
||||
}
|
||||
|
||||
const caseTabs = document.getElementById('caseTabs');
|
||||
if (caseTabs) {
|
||||
caseTabs.addEventListener('shown.bs.tab', async (event) => {
|
||||
const targetSelector = event?.target?.getAttribute('data-bs-target') || '';
|
||||
const tabId = targetSelector.startsWith('#') ? targetSelector.slice(1) : targetSelector;
|
||||
|
||||
try {
|
||||
if (tabId === 'sales' && typeof loadVarekobSalg === 'function') {
|
||||
await loadVarekobSalg();
|
||||
} else if (tabId === 'subscription' && typeof loadSubscriptionForCase === 'function') {
|
||||
await loadSubscriptionForCase();
|
||||
} else if (tabId === 'reminders') {
|
||||
if (typeof loadReminders === 'function') await loadReminders();
|
||||
if (typeof loadCaseCalendar === 'function') await loadCaseCalendar();
|
||||
}
|
||||
} catch (tabLoadError) {
|
||||
console.error('Tab data reload failed:', tabLoadError);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Focus on title when create modal opens
|
||||
const createModalEl = document.getElementById('createRelatedCaseModal');
|
||||
if (createModalEl) {
|
||||
@ -4570,6 +4591,7 @@
|
||||
|
||||
<script>
|
||||
let reminderUserId = null;
|
||||
const remindersCaseId = {{ case.id }};
|
||||
|
||||
function getReminderUserId() {
|
||||
const token = localStorage.getItem('access_token') || sessionStorage.getItem('access_token');
|
||||
@ -4586,6 +4608,20 @@
|
||||
return null;
|
||||
}
|
||||
|
||||
async function ensureReminderUserId() {
|
||||
const localId = getReminderUserId();
|
||||
if (localId) return localId;
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/v1/auth/me', { credentials: 'include' });
|
||||
if (!res.ok) return null;
|
||||
const me = await res.json();
|
||||
return me?.id || me?.user_id || null;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function formatReminderDate(value) {
|
||||
if (!value) return '-';
|
||||
const date = new Date(value);
|
||||
@ -4637,7 +4673,7 @@
|
||||
async function loadReminders() {
|
||||
const list = document.getElementById('remindersList');
|
||||
if (!list) return;
|
||||
reminderUserId = getReminderUserId();
|
||||
reminderUserId = await ensureReminderUserId();
|
||||
|
||||
if (!reminderUserId) {
|
||||
list.innerHTML = '<div class="p-4 text-center text-muted">Kunne ikke finde bruger-id.</div>';
|
||||
@ -4648,7 +4684,7 @@
|
||||
list.innerHTML = '<div class="p-4 text-center text-muted"><span class="spinner-border spinner-border-sm"></span> Henter reminders...</div>';
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/v1/sag/${caseIds}/reminders?user_id=${reminderUserId}`);
|
||||
const res = await fetch(`/api/v1/sag/${remindersCaseId}/reminders?user_id=${reminderUserId}`);
|
||||
if (!res.ok) throw new Error('Kunne ikke hente reminders');
|
||||
const reminders = await res.json();
|
||||
renderReminders(reminders);
|
||||
@ -4722,7 +4758,7 @@
|
||||
}
|
||||
|
||||
async function saveReminder() {
|
||||
reminderUserId = getReminderUserId();
|
||||
reminderUserId = await ensureReminderUserId();
|
||||
if (!reminderUserId) {
|
||||
alert('Mangler bruger-id. Log ind igen.');
|
||||
return;
|
||||
@ -4785,7 +4821,7 @@
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/v1/sag/${caseIds}/reminders?user_id=${reminderUserId}`, {
|
||||
const res = await fetch(`/api/v1/sag/${remindersCaseId}/reminders?user_id=${reminderUserId}`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload)
|
||||
@ -4846,7 +4882,7 @@
|
||||
childrenList.innerHTML = '<div class="text-muted small">Indlæser børnesager...</div>';
|
||||
|
||||
try {
|
||||
const res = await fetch(`/api/v1/sag/${caseIds}/calendar-events?include_children=true`);
|
||||
const res = await fetch(`/api/v1/sag/${remindersCaseId}/calendar-events?include_children=true`);
|
||||
if (!res.ok) throw new Error('Kunne ikke hente kalenderaftaler');
|
||||
const data = await res.json();
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user