ui(ucp): cartes journal appels mobile style iPhone (v2.5.7)
This commit is contained in:
1 parent
26cac163d6
commit
135a6267db
3 files changed
+66
-21
No files matched your search
+57
-19
@@ -607,7 +607,8 @@ function debounceLoadCdr() {
|
|||||||
|
|
||||||
async function loadPersonalCdr() {
|
async function loadPersonalCdr() {
|
||||||
const tbody = document.getElementById('ucp-cdr-tbody');
|
const tbody = document.getElementById('ucp-cdr-tbody');
|
||||||
if (!tbody) return;
|
const mobileBox = document.getElementById('ucp-cdr-mobile-list');
|
||||||
|
if (!tbody && !mobileBox) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await ucpApiCall('get_personal_cdr', 'GET', {
|
const res = await ucpApiCall('get_personal_cdr', 'GET', {
|
||||||
@@ -625,25 +626,16 @@ async function loadPersonalCdr() {
|
|||||||
document.getElementById('ucp-cdr-missed').textContent = String(stats.missed);
|
document.getElementById('ucp-cdr-missed').textContent = String(stats.missed);
|
||||||
|
|
||||||
if (!calls.length) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbody.innerHTML = calls.map(c => {
|
|
||||||
const dirMap = {
|
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>',
|
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>',
|
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>',
|
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 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 formatParty = (name, num) => {
|
const formatParty = (name, num) => {
|
||||||
if (name && num && name !== num) {
|
if (name && num && name !== num) {
|
||||||
@@ -652,19 +644,65 @@ async function loadPersonalCdr() {
|
|||||||
return `<span class="font-semibold font-mono text-slate-900">${escapeHtml(num || name || '—')}</span>`;
|
return `<span class="font-semibold font-mono text-slate-900">${escapeHtml(num || name || '—')}</span>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
return `
|
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 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 || '');
|
||||||
|
|
||||||
|
rowsHtml.push(`
|
||||||
<tr class="hover:bg-slate-50/80 transition">
|
<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 text-slate-600 font-mono whitespace-nowrap">${dtText}</td>
|
||||||
<td class="py-3 px-4">${dirMap[c.direction] || escapeHtml(c.direction_label || c.direction)}</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(srcName, srcNum)}</td>
|
||||||
<td class="py-3 px-4">${formatParty(dstName, dstNum)}</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>
|
<td class="py-3 px-4">${stBadge}</td>
|
||||||
</tr>
|
</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) {
|
} 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">
|
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>
|
||||||
|
|
||||||
<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">
|
<div class="overflow-x-auto">
|
||||||
<table class="w-full text-left text-xs">
|
<table class="w-full text-left text-xs">
|
||||||
<thead class="bg-slate-50 border-b border-slate-200 text-slate-500 uppercase text-[11px]">
|
<thead class="bg-slate-50 border-b border-slate-200 text-slate-500 uppercase text-[11px]">
|
||||||
|
|||||||
+2
-1
@@ -1,9 +1,10 @@
|
|||||||
{
|
{
|
||||||
"version": "2.5.6",
|
"version": "2.5.7",
|
||||||
"codename": "Pulsar UCP",
|
"codename": "Pulsar UCP",
|
||||||
"release_date": "2026-11-11",
|
"release_date": "2026-11-11",
|
||||||
"min_php_version": "8.2",
|
"min_php_version": "8.2",
|
||||||
"changelog": [
|
"changelog": [
|
||||||
|
"UI v2.5.7 : journal d'appels UCP en cartes style iOS sur mobile (<768px) tout en conservant le tableau 6 colonnes sur ordinateur",
|
||||||
"Correctif v2.5.6 : ergonomie mobile iPhone (en-tête compact + dégradé latéral discret sur la barre d'onglets UCP sans scrollbar visible)",
|
"Correctif v2.5.6 : ergonomie mobile iPhone (en-tête compact + dégradé latéral discret sur la barre d'onglets UCP sans scrollbar visible)",
|
||||||
"Correctif v2.5.5 : suppression du zoom automatique iOS/iPhone sur les champs (<16px) et blocage du débordement horizontal mobile dans l'UCP",
|
"Correctif v2.5.5 : suppression du zoom automatique iOS/iPhone sur les champs (<16px) et blocage du débordement horizontal mobile dans l'UCP",
|
||||||
"Correctif v2.5.4 : correction du journal personnel UCP « Mes Appels » (correspondance caller_number/callee_number/canaux PJSIP et filtres Répondus/Manqués)",
|
"Correctif v2.5.4 : correction du journal personnel UCP « Mes Appels » (correspondance caller_number/callee_number/canaux PJSIP et filtres Répondus/Manqués)",
|
||||||
|
|||||||
Reference in new issue
Block a user