ui(ucp): cartes journal appels mobile style iPhone (v2.5.7)

This commit is contained in:
sorcier committed 2026-11-11 23:21:23 +01:00
1 parent 26cac163d6
commit 135a6267db
3 files changed
+73 -28

No files matched your search

+64 -26
View File
@@ -607,7 +607,8 @@ function debounceLoadCdr() {
async function loadPersonalCdr() {
const tbody = document.getElementById('ucp-cdr-tbody');
if (!tbody) return;
const mobileBox = document.getElementById('ucp-cdr-mobile-list');
if (!tbody && !mobileBox) return;
try {
const res = await ucpApiCall('get_personal_cdr', 'GET', {
@@ -625,46 +626,83 @@ async function loadPersonalCdr() {
document.getElementById('ucp-cdr-missed').textContent = String(stats.missed);
if (!calls.length) {
tbody.innerHTML = `<tr><td colspan="6" class="py-8 text-center text-slate-400">Aucun appel correspondant.</td></tr>`;
if (tbody) tbody.innerHTML = `<tr><td colspan="6" class="py-8 text-center text-slate-400">Aucun appel correspondant.</td></tr>`;
if (mobileBox) mobileBox.innerHTML = `<div class="ucp-card rounded-2xl p-6 text-center text-xs text-slate-400">Aucun appel correspondant.</div>`;
return;
}
tbody.innerHTML = calls.map(c => {
const dirMap = {
inbound: '<span class="px-2 py-0.5 rounded-full bg-sky-50 text-sky-700 border border-sky-200 text-[10px] font-bold">↘ Entrant</span>',
outbound: '<span class="px-2 py-0.5 rounded-full bg-indigo-50 text-indigo-700 border border-indigo-200 text-[10px] font-bold">↗ Sortant</span>',
internal: '<span class="px-2 py-0.5 rounded-full bg-slate-100 text-slate-700 border border-slate-200 text-[10px] font-bold">⇄ Interne</span>',
};
const dirMap = {
inbound: '<span class="px-2 py-0.5 rounded-full bg-sky-50 text-sky-700 border border-sky-200 text-[10px] font-bold">↘ Entrant</span>',
outbound: '<span class="px-2 py-0.5 rounded-full bg-indigo-50 text-indigo-700 border border-indigo-200 text-[10px] font-bold">↗ Sortant</span>',
internal: '<span class="px-2 py-0.5 rounded-full bg-slate-100 text-slate-700 border border-slate-200 text-[10px] font-bold">⇄ Interne</span>',
};
const formatParty = (name, num) => {
if (name && num && name !== num) {
return `<div class="font-semibold text-slate-900">${escapeHtml(name)}</div><div class="text-[11px] font-mono text-slate-500">${escapeHtml(num)}</div>`;
}
return `<span class="font-semibold font-mono text-slate-900">${escapeHtml(num || name || '—')}</span>`;
};
const formatInlineParty = (name, num) => {
if (name && num && name !== num) {
return `<strong class="text-slate-900">${escapeHtml(name)}</strong> <span class="font-mono text-[11px] text-slate-500">(${escapeHtml(num)})</span>`;
}
return `<strong class="font-mono text-slate-900">${escapeHtml(num || name || '—')}</strong>`;
};
const rowsHtml = [];
const cardsHtml = [];
calls.forEach(c => {
const ok = c.disposition === 'ANSWERED';
const stBadge = ok
? `<span class="px-2 py-0.5 rounded-full bg-emerald-50 text-emerald-700 border border-emerald-200 text-[10px] font-bold">${escapeHtml(c.status_label || 'Répondu')}</span>`
: `<span class="px-2 py-0.5 rounded-full bg-rose-50 text-rose-700 border border-rose-200 text-[10px] font-bold">${escapeHtml(c.status_label || 'Manqué')}</span>`;
const srcNum = String(c.caller_number ?? c.src ?? '');
const srcName = String(c.caller_name ?? '');
const dstNum = String(c.callee_number ?? c.dst ?? '');
const dstName = String(c.callee_name ?? '');
const dirBadge = dirMap[c.direction] || escapeHtml(c.direction_label || c.direction);
const srcNum = String(c.caller_number ?? c.src ?? '');
const srcName = String(c.caller_name ?? '');
const dstNum = String(c.callee_number ?? c.dst ?? '');
const dstName = String(c.callee_name ?? '');
const durText = escapeHtml(c.billsec_formatted || formatDuration(c.billsec || c.duration || 0));
const dtText = escapeHtml(c.datetime_formatted || c.datetime || '');
const formatParty = (name, num) => {
if (name && num && name !== num) {
return `<div class="font-semibold text-slate-900">${escapeHtml(name)}</div><div class="text-[11px] font-mono text-slate-500">${escapeHtml(num)}</div>`;
}
return `<span class="font-semibold font-mono text-slate-900">${escapeHtml(num || name || '—')}</span>`;
};
return `
rowsHtml.push(`
<tr class="hover:bg-slate-50/80 transition">
<td class="py-3 px-4 text-slate-600 font-mono whitespace-nowrap">${escapeHtml(c.datetime_formatted || c.datetime || '')}</td>
<td class="py-3 px-4">${dirMap[c.direction] || escapeHtml(c.direction_label || c.direction)}</td>
<td class="py-3 px-4 text-slate-600 font-mono whitespace-nowrap">${dtText}</td>
<td class="py-3 px-4">${dirBadge}</td>
<td class="py-3 px-4">${formatParty(srcName, srcNum)}</td>
<td class="py-3 px-4">${formatParty(dstName, dstNum)}</td>
<td class="py-3 px-4 font-mono text-slate-600">${escapeHtml(c.billsec_formatted || formatDuration(c.billsec || c.duration || 0))}</td>
<td class="py-3 px-4 font-mono text-slate-600">${durText}</td>
<td class="py-3 px-4">${stBadge}</td>
</tr>
`;
}).join('');
`);
cardsHtml.push(`
<div class="ucp-card rounded-2xl p-3.5 space-y-2 min-w-0">
<div class="flex items-center justify-between gap-2 flex-wrap">
<div class="flex items-center gap-1.5">
${dirBadge}
${stBadge}
</div>
<span class="font-mono text-xs font-semibold text-slate-700">⏱ ${durText}</span>
</div>
<div class="text-xs leading-snug break-words flex flex-wrap items-center gap-1.5">
<span>${formatInlineParty(srcName, srcNum)}</span>
<span class="text-slate-400 font-bold">→</span>
<span>${formatInlineParty(dstName, dstNum)}</span>
</div>
<div class="text-[11px] font-mono text-slate-400">${dtText}</div>
</div>
`);
});
if (tbody) tbody.innerHTML = rowsHtml.join('');
if (mobileBox) mobileBox.innerHTML = cardsHtml.join('');
} catch (err) {
tbody.innerHTML = `<tr><td colspan="6" class="py-6 text-center text-rose-600">${escapeHtml(err.message)}</td></tr>`;
if (tbody) tbody.innerHTML = `<tr><td colspan="6" class="py-6 text-center text-rose-600">${escapeHtml(err.message)}</td></tr>`;
if (mobileBox) mobileBox.innerHTML = `<div class="ucp-card rounded-2xl p-5 text-center text-xs text-rose-600">${escapeHtml(err.message)}</div>`;
}
}
+7 -1
View File
@@ -350,7 +350,13 @@ $csrfToken = UcpAuth::getCsrfToken();
class="px-3 py-2 rounded-xl bg-slate-50 border border-slate-300 text-xs text-slate-900 focus:bg-white focus:outline-none focus:border-indigo-500">
</div>
<div class="ucp-card rounded-2xl overflow-hidden">
<!-- Vue mobile style journal d'appels iPhone (< 768px) -->
<div id="ucp-cdr-mobile-list" class="md:hidden space-y-2.5">
<div class="ucp-card rounded-2xl p-6 text-center text-xs text-slate-400">Chargement…</div>
</div>
<!-- Vue tableau complète sur ordinateur (>= 768px) -->
<div class="hidden md:block ucp-card rounded-2xl overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full text-left text-xs">
<thead class="bg-slate-50 border-b border-slate-200 text-slate-500 uppercase text-[11px]">