Improve admin visitor context layout
This commit is contained in:
+77
-5
@@ -270,10 +270,17 @@ function renderConversations() {
|
||||
function renderActive() {
|
||||
const { conversation, messages } = state.active;
|
||||
$("#conversationHeader").innerHTML = `
|
||||
<div>
|
||||
<h2>${escapeHtml(conversation.visitor.name || conversation.visitor.email || "Navstevnik")}</h2>
|
||||
<p>${visitorCountry(conversation)} ${escapeHtml(conversation.currentUrl || "")}</p>
|
||||
<p>${escapeHtml(conversation.device.userAgent || "")}</p>
|
||||
<div class="visitor-head">
|
||||
<div class="visitor-title-row">
|
||||
<h2>${escapeHtml(conversation.visitor.name || conversation.visitor.email || "Navstevnik")}</h2>
|
||||
<span class="country-pill">${visitorCountry(conversation)}</span>
|
||||
</div>
|
||||
<div class="visitor-quick">
|
||||
<span title="Doba na webu">${formatDuration(conversation.sessionSeconds)}</span>
|
||||
<span title="Navstivene stranky">${Number(conversation.pageCount || 0)} str.</span>
|
||||
<span title="Pocet chatu">${Number(conversation.visitorConversationCount || 1)}x chat</span>
|
||||
</div>
|
||||
${visitorDetails(conversation)}
|
||||
</div>
|
||||
`;
|
||||
$("#replyForm").classList.remove("hidden");
|
||||
@@ -306,10 +313,37 @@ function statusButtons(item) {
|
||||
}
|
||||
|
||||
function visitorCountry(item) {
|
||||
const countryCode = item.countryCode || countryFromLanguage(item.language);
|
||||
const countryCode = item.countryCode || countryFromLanguage(item.language) || countryFromTimezone(item.timezone);
|
||||
return countryCode ? `${flag(countryCode)} ${escapeHtml(countryCode)}` : "zeme ?";
|
||||
}
|
||||
|
||||
function visitorDetails(conversation) {
|
||||
const history = Array.isArray(conversation.browsingHistory) ? conversation.browsingHistory : [];
|
||||
const historyItems = history.length ? history.slice().reverse().map((item) => `
|
||||
<li>
|
||||
<a href="${escapeHtml(item.url || "#")}" target="_blank" rel="noreferrer">${escapeHtml(pageLabel(item))}</a>
|
||||
<small>${escapeHtml(formatVisitedAt(item.at))}</small>
|
||||
</li>
|
||||
`).join("") : `<li><span>Zatim bez historie.</span></li>`;
|
||||
return `
|
||||
<details class="visitor-details">
|
||||
<summary title="Detail navstevnika">
|
||||
<span aria-hidden="true">i</span>
|
||||
<strong>Detail navstevnika</strong>
|
||||
</summary>
|
||||
<div class="visitor-detail-grid">
|
||||
<span>Aktualne</span>
|
||||
<a href="${escapeHtml(conversation.currentUrl || "#")}" target="_blank" rel="noreferrer">${escapeHtml(shortUrl(conversation.currentUrl) || "-")}</a>
|
||||
<span>Prisiel z</span>
|
||||
${conversation.referrer ? `<a href="${escapeHtml(conversation.referrer)}" target="_blank" rel="noreferrer">${escapeHtml(shortUrl(conversation.referrer))}</a>` : `<em>primy vstup / nezname</em>`}
|
||||
<span>Zarizeni</span>
|
||||
<em>${escapeHtml(deviceLabel(conversation.device, conversation.language, conversation.timezone))}</em>
|
||||
</div>
|
||||
<ol class="history-list">${historyItems}</ol>
|
||||
</details>
|
||||
`;
|
||||
}
|
||||
|
||||
function formatDuration(seconds) {
|
||||
const value = Number(seconds || 0);
|
||||
if (value < 60) return `${Math.max(0, value)}s`;
|
||||
@@ -415,6 +449,44 @@ function countryFromLanguage(language) {
|
||||
return match ? match[1].toUpperCase() : null;
|
||||
}
|
||||
|
||||
function countryFromTimezone(timezone) {
|
||||
const zones = {
|
||||
"Europe/Prague": "CZ",
|
||||
"Europe/Bratislava": "SK",
|
||||
"Europe/Warsaw": "PL",
|
||||
"Europe/Berlin": "DE",
|
||||
"Europe/Vienna": "AT",
|
||||
"Europe/Budapest": "HU"
|
||||
};
|
||||
return zones[String(timezone || "")] || null;
|
||||
}
|
||||
|
||||
function shortUrl(value) {
|
||||
if (!value) return "";
|
||||
try {
|
||||
const url = new URL(value);
|
||||
const path = `${url.pathname}${url.search}`.replace(/\/$/, "");
|
||||
return `${url.hostname}${path || "/"}`;
|
||||
} catch {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
|
||||
function pageLabel(item) {
|
||||
return item.title || shortUrl(item.url) || item.url || "stranka";
|
||||
}
|
||||
|
||||
function formatVisitedAt(value) {
|
||||
if (!value) return "";
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return "";
|
||||
return date.toLocaleTimeString("cs-CZ", { hour: "2-digit", minute: "2-digit" });
|
||||
}
|
||||
|
||||
function deviceLabel(device = {}, language, timezone) {
|
||||
return [device.platform, device.viewport, language, timezone].filter(Boolean).join(" | ");
|
||||
}
|
||||
|
||||
function openPhotoPreview(url, name) {
|
||||
let modal = $("#photoModal");
|
||||
if (!modal) {
|
||||
|
||||
Reference in New Issue
Block a user