ui(ucp): cartes journal appels mobile style iPhone (v2.5.7)
This commit is contained in:
1 parent
26cac163d6
commit
135a6267db
3 files changed
+73
-28
No files matched your search
+64
-26
@@ -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
@@ -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]">
|
||||
|
||||
Reference in new issue
Block a user