Files
TalkingQ_Dashboard/scripts/session.js
“yuanmengy” a225272d1e data&device ready
2026-06-05 17:11:56 +08:00

579 lines
26 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* TalkingQ Platform - Session Detail Page
*/
const SESSION_TOPIC_DRILLDOWN_KEY = 'talkingq_session_topic_drilldown';
const SESSION_VIEW_STATE_KEY = 'talkingq_session_view_state';
const SESSION_DEFAULT_DATE = '2026-05-25';
const SESSION_TOPIC_OPTIONS = ['讲故事', '唱儿歌', '问答学习', '天气查询', '闹钟设置', '游戏互动', '英语学习', '百科问答', '情感陪伴', '其他'];
function getMockSessionData() {
const rows = [
['sess-a251', 'company_a', '智慧星科技', 'DEV-A001', '小Q故事机器人', '2026-05-25 14:22', '讲故事', 12, '8m 32s', 'good'],
['sess-a252', 'company_a', '智慧星科技', 'DEV-A001', '小Q故事机器人', '2026-05-25 10:12', '百科问答', 8, '5m 28s', 'good'],
['sess-a253', 'company_a', '智慧星科技', 'DEV-A005', '英语学习助手', '2026-05-25 13:48', '英语学习', 7, '5m 10s', 'good'],
['sess-a254', 'company_a', '智慧星科技', 'DEV-A008', '小Q音乐伙伴', '2026-05-25 13:08', '唱儿歌', 10, '6m 48s', 'good'],
['sess-a255', 'company_a', '智慧星科技', 'DEV-A011', '生活助手', '2026-05-25 12:36', '天气查询', 6, '4m 12s', 'neutral'],
['sess-a256', 'company_a', '智慧星科技', 'DEV-A018', '设备小助手', '2026-05-25 11:52', '闹钟设置', 4, '2m 20s', 'good'],
['sess-a257', 'company_a', '智慧星科技', 'DEV-A021', '小Q游戏伙伴', '2026-05-25 11:18', '游戏互动', 15, '11m 05s', 'good'],
['sess-a258', 'company_a', '智慧星科技', 'DEV-A026', '陪伴机器人', '2026-05-25 10:40', '情感陪伴', 8, '5m 44s', 'good'],
['sess-a241', 'company_a', '智慧星科技', 'DEV-A001', '小Q故事机器人', '2026-05-24 16:06', '讲故事', 9, '6m 16s', 'good'],
['sess-a242', 'company_a', '智慧星科技', 'DEV-A005', '英语学习助手', '2026-05-24 14:20', '问答学习', 6, '4m 38s', 'neutral'],
['sess-a231', 'company_a', '智慧星科技', 'DEV-A021', '小Q游戏伙伴', '2026-05-23 19:42', '游戏互动', 13, '9m 52s', 'good'],
['sess-b251', 'company_b', '未来玩具厂', 'DEV-B002', '学习机助手', '2026-05-25 14:05', '问答学习', 3, '1m 45s', 'bad'],
['sess-b252', 'company_b', '未来玩具厂', 'DEV-B004', '科学百科探索者', '2026-05-25 13:42', '百科问答', 9, '7m 03s', 'neutral'],
['sess-b253', 'company_b', '未来玩具厂', 'DEV-B008', '小Q故事机器人', '2026-05-25 13:18', '讲故事', 11, '8m 15s', 'good'],
['sess-b254', 'company_b', '未来玩具厂', 'DEV-B015', '小Q音乐伙伴', '2026-05-25 12:46', '唱儿歌', 8, '4m 50s', 'good'],
['sess-b255', 'company_b', '未来玩具厂', 'DEV-B019', '小Q游戏伙伴', '2026-05-25 11:56', '游戏互动', 14, '10m 22s', 'good'],
['sess-b256', 'company_b', '未来玩具厂', 'DEV-B024', '生活助手', '2026-05-25 11:20', '天气查询', 5, '3m 06s', 'neutral'],
['sess-b241', 'company_b', '未来玩具厂', 'DEV-B008', '小Q故事机器人', '2026-05-24 15:32', '讲故事', 10, '7m 30s', 'good'],
['sess-b242', 'company_b', '未来玩具厂', 'DEV-B004', '科学百科探索者', '2026-05-24 12:14', '百科问答', 7, '5m 48s', 'good'],
['sess-c251', 'company_c', '童趣电子', 'DEV-C001', '小Q故事机器人', '2026-05-25 13:55', '讲故事', 18, '15m 20s', 'good'],
['sess-c252', 'company_c', '童趣电子', 'DEV-C006', '小Q音乐伙伴', '2026-05-25 13:08', '唱儿歌', 9, '5m 35s', 'good'],
['sess-c253', 'company_c', '童趣电子', 'DEV-C012', '学习机助手', '2026-05-25 12:32', '问答学习', 10, '6m 18s', 'good'],
['sess-c254', 'company_c', '童趣电子', 'DEV-C018', '英语学习助手', '2026-05-25 11:54', '英语学习', 6, '4m 27s', 'neutral'],
['sess-c255', 'company_c', '童趣电子', 'DEV-C022', '科学百科探索者', '2026-05-25 11:16', '百科问答', 12, '9m 11s', 'good'],
['sess-c256', 'company_c', '童趣电子', 'DEV-C027', '陪伴机器人', '2026-05-25 10:45', '情感陪伴', 7, '4m 58s', 'good'],
['sess-c257', 'company_c', '童趣电子', 'DEV-C031', '小Q助手', '2026-05-25 10:20', '其他', 2, '1m 02s', 'neutral'],
['sess-c241', 'company_c', '童趣电子', 'DEV-C001', '小Q故事机器人', '2026-05-24 18:18', '讲故事', 16, '13m 44s', 'good'],
['sess-c242', 'company_c', '童趣电子', 'DEV-C012', '学习机助手', '2026-05-24 09:48', '问答学习', 9, '6m 01s', 'good'],
];
return rows.map(([id, companyId, companyName, device, agent, start, intent, turns, duration, quality]) => ({
id,
companyId,
companyName,
device,
agent,
start,
date: start.slice(0, 10),
intent,
turns,
duration,
quality,
}));
}
function readSessionTopicDrilldown() {
try {
const raw = sessionStorage.getItem(SESSION_TOPIC_DRILLDOWN_KEY);
if (!raw) return null;
const parsed = JSON.parse(raw);
if (!parsed?.topic) return null;
if (parsed.createdAt && Date.now() - parsed.createdAt > 30 * 60 * 1000) {
sessionStorage.removeItem(SESSION_TOPIC_DRILLDOWN_KEY);
return null;
}
return parsed;
} catch (error) {
sessionStorage.removeItem(SESSION_TOPIC_DRILLDOWN_KEY);
return null;
}
}
function readSessionViewState() {
try {
return JSON.parse(sessionStorage.getItem(SESSION_VIEW_STATE_KEY) || '{}') || {};
} catch (error) {
return {};
}
}
function updateSessionViewState(patch) {
const next = { ...readSessionViewState(), ...patch };
sessionStorage.setItem(SESSION_VIEW_STATE_KEY, JSON.stringify(next));
}
function setSessionTopicDrilldown(topic, context = {}) {
if (!topic) {
sessionStorage.removeItem(SESSION_TOPIC_DRILLDOWN_KEY);
return;
}
sessionStorage.setItem(SESSION_TOPIC_DRILLDOWN_KEY, JSON.stringify({
topic,
companyId: context.companyId || 'all',
companyName: context.companyName || '全部公司',
source: context.source || 'session-filter',
createdAt: Date.now(),
}));
}
function getSessionCurrentCompanyId() {
const currentRole = window.__getUserRole ? window.__getUserRole() : 'admin';
const currentCompanyId = window.__getCurrentCompanyId ? window.__getCurrentCompanyId() : 'all';
if (currentRole !== 'client') return currentCompanyId;
if (currentCompanyId !== 'all') return currentCompanyId;
try {
const user = JSON.parse(sessionStorage.getItem('talkingq_user') || 'null');
return user?.companyId || user?.allowedCompanies?.[0] || currentCompanyId;
} catch (error) {
return currentCompanyId;
}
}
function getSessionCompanyName(companyId, fallback = '当前公司') {
if (companyId === 'all') return '全部公司';
return window.__getCompanyNameById ? window.__getCompanyNameById(companyId) : fallback;
}
function sessionMatchesTopic(session, topic) {
if (!topic) return true;
const intent = String(session.intent || '');
if (intent === topic) return true;
if (intent.includes(topic) || topic.includes(intent)) return true;
const aliases = {
'讲故事': ['故事', '睡前故事', '绘本'],
'唱儿歌': ['儿歌', '音乐', '唱歌'],
'问答学习': ['问答', '学习', '作业'],
'天气查询': ['天气'],
'闹钟设置': ['闹钟', '提醒'],
'游戏互动': ['游戏', '互动'],
'英语学习': ['英语', '英文'],
'百科问答': ['百科', '科普'],
'情感陪伴': ['陪伴', '情感', '聊天'],
'其他': ['其他'],
};
return (aliases[topic] || []).some(keyword => intent.includes(keyword));
}
function sessionEscapeHtml(value) {
return String(value ?? '').replace(/[&<>"']/g, char => ({
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
}[char]));
}
function sessionEscapeAttr(value) {
return sessionEscapeHtml(value).replace(/"/g, '&quot;');
}
function formatSessionDateLabel(date) {
const [year, month, day] = String(date || '').split('-');
return year && month && day ? `${year}.${month}.${day}` : date;
}
function getSessionQualityText(quality) {
if (quality === 'good') return '优质';
if (quality === 'bad') return '待优化';
return '正常';
}
function getSessionQualityBadge(quality) {
if (quality === 'good') return 'badge-success';
if (quality === 'bad') return 'badge-danger';
return 'badge-default';
}
function getTopicOptionsForCompany(sessions) {
const seen = new Set(sessions.map(item => item.intent));
return SESSION_TOPIC_OPTIONS.filter(topic => seen.has(topic));
}
function getCompanyDevices(companySessions, daySessions) {
const byDevice = new Map();
companySessions.forEach(session => {
if (!byDevice.has(session.device)) {
byDevice.set(session.device, {
device: session.device,
companyId: session.companyId,
companyName: session.companyName,
agent: session.agent,
sessions: [],
});
}
});
daySessions.forEach(session => {
if (!byDevice.has(session.device)) {
byDevice.set(session.device, {
device: session.device,
companyId: session.companyId,
companyName: session.companyName,
agent: session.agent,
sessions: [],
});
}
byDevice.get(session.device).sessions.push(session);
});
return Array.from(byDevice.values()).map(item => {
const topics = Array.from(new Set(item.sessions.map(session => session.intent)));
const turns = item.sessions.reduce((sum, session) => sum + session.turns, 0);
const lastSession = item.sessions.slice().sort((a, b) => b.start.localeCompare(a.start))[0];
return {
...item,
topics,
turns,
count: item.sessions.length,
lastSession,
};
}).sort((a, b) => b.count - a.count || a.device.localeCompare(b.device));
}
function getConversationLines(topic) {
const samples = {
'讲故事': ['小Q给我讲一个关于恐龙的故事吧', '好的!从前,在一片茂密的丛林里,住着一只叫做小雷的霸王龙宝宝……', '霸王龙是最厉害的恐龙吗?', '霸王龙很厉害,但恐龙世界里还有很多不同种类的大家伙。我们可以一起认识它们。'],
'唱儿歌': ['小Q唱一首上学路上的儿歌。', '当然可以,我会用轻快一点的节奏来唱。', '再唱一首动物相关的。', '好的,这次我们唱一首关于小兔子的儿歌。'],
'问答学习': ['小Q为什么月亮会变圆又变弯', '这是因为月亮围着地球转,我们看到被太阳照亮的部分会变化。', '那满月是什么时候?', '当我们看到月亮被照亮的一面几乎完整朝向地球时,就是满月。'],
'天气查询': ['小Q今天要带雨伞吗', '今天下午可能有小雨,建议带一把轻便雨伞。', '那适合出去玩吗?', '可以短时间外出,尽量避开下午降雨时段。'],
'闹钟设置': ['小Q明天早上七点叫我起床。', '好的,已为你设置明天早上七点的起床提醒。', '再加一个七点十分。', '已添加七点十分的第二个提醒。'],
'游戏互动': ['小Q我们玩猜动物吧。', '好,我来描述一个动物,你来猜。它有长长的鼻子和大大的耳朵。', '是大象!', '答对了!下一题要不要难一点?'],
'英语学习': ['How do I say 小熊 in English?', '小熊 can be said as little bear or teddy bear, depending on context.', 'Can you make a sentence?', 'Sure. The little bear is sleeping under the tree.'],
'百科问答': ['小Q火山为什么会喷发', '地下的岩浆和气体压力变大时,会沿着裂缝冲出来,就形成了火山喷发。', '岩浆冷了会变成什么?', '岩浆冷却后会变成岩石,有些还会形成新的地貌。'],
'情感陪伴': ['小Q我今天有点不开心。', '我在呢。你愿意告诉我发生了什么吗?', '我和朋友吵架了。', '听起来你很难过。我们可以先深呼吸,再想想怎么和朋友好好说。'],
'其他': ['小Q你现在能做什么', '我可以聊天、讲故事、唱儿歌、回答问题,也可以帮你做一些简单提醒。', '那我们随便聊聊吧。', '好呀,你今天最开心的一件事是什么?'],
};
return samples[topic] || samples['其他'];
}
function renderSessionConversation(session, options = {}) {
if (!session) {
return '<div class="empty-state" style="padding:40px 12px"><p>暂无可查看的对话内容</p></div>';
}
const lines = getConversationLines(session.intent);
const hiddenTurns = Math.max(session.turns - 4, 0);
return `
<div class="session-message user">
<div class="session-message-avatar">用户</div>
<div class="session-message-text">${sessionEscapeHtml(lines[0])}</div>
</div>
<div class="session-message bot">
<div class="session-message-avatar">Q</div>
<div class="session-message-text">${sessionEscapeHtml(lines[1])}</div>
</div>
<div class="session-message user">
<div class="session-message-avatar">用户</div>
<div class="session-message-text">${sessionEscapeHtml(lines[2])}</div>
</div>
<div class="session-message bot">
<div class="session-message-avatar">Q</div>
<div class="session-message-text">${sessionEscapeHtml(lines[3])}</div>
</div>
${options.showMore !== false ? `<div class="session-more">还有 ${hiddenTurns} 轮对话</div>` : ''}
`;
}
function renderDeviceRows(devices, selectedDeviceId, showCompanyColumn) {
if (!devices.length) {
return `<tr><td colspan="${showCompanyColumn ? 7 : 6}" style="text-align:center;padding:40px;color:var(--text-muted)">暂无设备数据</td></tr>`;
}
return devices.map(device => {
const active = device.device === selectedDeviceId;
return `
<tr class="${active ? 'session-device-row active' : 'session-device-row'}" data-device-id="${sessionEscapeAttr(device.device)}">
<td>
<div style="font-weight:600">${sessionEscapeHtml(device.device)}</div>
<div style="font-size:12px;color:var(--text-muted)">${sessionEscapeHtml(device.agent)}</div>
</td>
${showCompanyColumn ? `<td><span class="badge badge-default">${sessionEscapeHtml(getSessionCompanyName(device.companyId, device.companyName))}</span></td>` : ''}
<td><span class="badge badge-info">${device.count} 条</span></td>
<td style="font-weight:600">${device.turns} 轮</td>
<td>${device.topics.length ? device.topics.slice(0, 3).map(topic => `<span class="badge badge-default" style="margin-right:4px">${sessionEscapeHtml(topic)}</span>`).join('') : '<span style="color:var(--text-muted)">当天无对话</span>'}</td>
<td style="font-size:12px;color:var(--text-muted)">${device.lastSession ? sessionEscapeHtml(device.lastSession.start.slice(11)) : '-'}</td>
<td><button class="btn btn-outline btn-sm view-device-btn" data-device-id="${sessionEscapeAttr(device.device)}">查看</button></td>
</tr>
`;
}).join('');
}
function renderDeviceSessionList(device, selectedDate) {
if (!device) {
return `
<div class="session-device-panel">
<div class="session-panel-header">
<div>
<h3>设备对话历史</h3>
<p>${formatSessionDateLabel(selectedDate)} · 从左侧选择设备查看对话内容</p>
</div>
</div>
<div class="empty-state" style="padding:56px 16px"><p>请选择设备查看当天对话历史</p></div>
</div>
`;
}
if (!device.sessions.length) {
return `
<div class="session-device-panel">
<div class="session-panel-header">
<div>
<h3>${sessionEscapeHtml(device.device)}</h3>
<p>${formatSessionDateLabel(selectedDate)} 当天暂无对话</p>
</div>
<button class="btn btn-outline btn-sm" data-session-back-device>
<i data-lucide="arrow-left"></i> 返回设备列表
</button>
</div>
<div class="empty-state" style="padding:42px 16px"><p>该设备在所选日期没有产生对话</p></div>
</div>
`;
}
return `
<div class="session-device-panel">
<div class="session-panel-header">
<div>
<h3>${sessionEscapeHtml(device.device)}</h3>
<p>${formatSessionDateLabel(selectedDate)} · ${device.sessions.length} 条对话 · ${device.turns} 轮互动</p>
</div>
<div class="session-panel-actions">
<span class="badge badge-success">${sessionEscapeHtml(device.agent)}</span>
<button class="btn btn-outline btn-sm" data-session-back-device>
<i data-lucide="arrow-left"></i> 返回设备列表
</button>
</div>
</div>
<div class="session-history-list">
${device.sessions.map(session => `
<div class="session-history-card">
<div class="session-history-head">
<div>
<strong>${sessionEscapeHtml(session.intent)}</strong>
<span>${sessionEscapeHtml(session.start.slice(11))} · ${session.turns} 轮 · ${sessionEscapeHtml(session.duration)}</span>
</div>
<span class="badge ${getSessionQualityBadge(session.quality)}">${getSessionQualityText(session.quality)}</span>
</div>
<div class="session-transcript">
${renderSessionConversation(session)}
</div>
</div>
`).join('')}
</div>
</div>
`;
}
function renderTopicAggregateView(sessions, selectedTopic, selectedDate, companyName) {
const totalTurns = sessions.reduce((sum, session) => sum + session.turns, 0);
const agents = new Set(sessions.map(session => session.agent));
return `
<div class="session-topic-layout">
<div class="session-topic-summary">
<div>
<span>话题</span>
<strong>${sessionEscapeHtml(selectedTopic)}</strong>
</div>
<div>
<span>日期</span>
<strong>${formatSessionDateLabel(selectedDate)}</strong>
</div>
<div>
<span>会话数</span>
<strong>${sessions.length}</strong>
</div>
<div>
<span>总轮次</span>
<strong>${totalTurns}</strong>
</div>
</div>
<div class="chart-card full">
<div class="card-header">
<div>
<div class="card-title">「${sessionEscapeHtml(selectedTopic)}」对话内容整合</div>
<div class="card-subtitle">${sessionEscapeHtml(companyName)} 全部设备聚合 · ${agents.size} 个智能体参与 · 不按设备区分</div>
</div>
</div>
${sessions.length ? `
<div class="session-aggregate-list">
${sessions.map(session => `
<div class="session-aggregate-card">
<div class="session-history-head">
<div>
<strong>${sessionEscapeHtml(session.agent)}</strong>
<span>${sessionEscapeHtml(session.start.slice(11))} · ${session.turns} 轮 · ${sessionEscapeHtml(session.duration)}</span>
</div>
<span class="badge ${getSessionQualityBadge(session.quality)}">${getSessionQualityText(session.quality)}</span>
</div>
<div class="session-transcript">
${renderSessionConversation(session)}
</div>
</div>
`).join('')}
</div>
` : `<div class="empty-state" style="padding:56px 16px"><p>所选日期暂无「${sessionEscapeHtml(selectedTopic)}」相关对话</p></div>`}
</div>
</div>
`;
}
registerPage('session', function(container) {
const currentRole = window.__getUserRole ? window.__getUserRole() : 'admin';
const isClient = currentRole === 'client';
const currentCompanyId = getSessionCurrentCompanyId();
const currentCompanyName = getSessionCompanyName(currentCompanyId, '当前公司');
const viewState = readSessionViewState();
const topicDrilldown = readSessionTopicDrilldown();
const selectedTopic = topicDrilldown?.topic || '';
const fromDashboardDrilldown = topicDrilldown?.source === 'dashboard-topic-ranking';
const selectedDate = viewState.date || topicDrilldown?.date || SESSION_DEFAULT_DATE;
const allSessions = getMockSessionData();
const companySessions = currentCompanyId === 'all'
? allSessions
: allSessions.filter(session => session.companyId === currentCompanyId);
const daySessions = companySessions.filter(session => session.date === selectedDate);
const topicSessions = selectedTopic
? daySessions.filter(session => sessionMatchesTopic(session, selectedTopic))
: [];
const devices = getCompanyDevices(companySessions, daySessions);
const selectedDeviceId = selectedTopic
? ''
: (viewState.deviceId && devices.some(device => device.device === viewState.deviceId) ? viewState.deviceId : '');
const selectedDevice = devices.find(device => device.device === selectedDeviceId);
const showCompanyColumn = !isClient && currentCompanyId === 'all';
const topicOptions = getTopicOptionsForCompany(companySessions);
const deviceCount = devices.length;
const dayTurns = daySessions.reduce((sum, session) => sum + session.turns, 0);
const pageDesc = selectedTopic
? `${currentCompanyName} · 按热门话题聚合查看对话内容`
: `${currentCompanyName} · 按设备查看所选日期的对话历史`;
container.innerHTML = `
<div class="page-header">
<div>
<div class="page-title">会话详情</div>
<div class="page-desc">${sessionEscapeHtml(pageDesc)}</div>
</div>
</div>
${selectedTopic ? `
<div class="session-view-backbar">
<button class="btn btn-outline btn-sm" id="sessionStepBackBtn">
<i data-lucide="arrow-left"></i> ${fromDashboardDrilldown ? '返回数据仪表盘' : '返回全部话题'}
</button>
</div>
<div class="session-drilldown-banner">
<div class="session-drilldown-icon"><i data-lucide="bar-chart-3"></i></div>
<div class="session-drilldown-copy">
<strong>热门话题聚合:${sessionEscapeHtml(selectedTopic)}</strong>
<span>${sessionEscapeHtml(currentCompanyName)} · ${formatSessionDateLabel(selectedDate)} · 当前结果 ${topicSessions.length} 条 · 不区分设备</span>
</div>
</div>
` : ''}
<div class="session-filter-panel">
<div class="session-filter-main">
<div class="form-group">
<label class="form-label">日期</label>
<input type="date" class="form-control" id="sessionDateFilter" value="${sessionEscapeAttr(selectedDate)}">
</div>
<div class="form-group">
<label class="form-label">热门话题</label>
<select class="form-control" id="sessionTopicFilter">
<option value="">全部话题</option>
${topicOptions.map(topic => `<option value="${sessionEscapeAttr(topic)}" ${topic === selectedTopic ? 'selected' : ''}>${sessionEscapeHtml(topic)}</option>`).join('')}
</select>
</div>
</div>
<div class="session-filter-stats">
<div>
<span>设备</span>
<strong>${deviceCount}</strong>
</div>
<div>
<span>当日会话</span>
<strong>${selectedTopic ? topicSessions.length : daySessions.length}</strong>
</div>
<div>
<span>互动轮次</span>
<strong>${selectedTopic ? topicSessions.reduce((sum, session) => sum + session.turns, 0) : dayTurns}</strong>
</div>
</div>
</div>
${selectedTopic ? renderTopicAggregateView(topicSessions, selectedTopic, selectedDate, currentCompanyName) : `
<div class="session-device-layout">
<div class="chart-card">
<div class="card-header">
<div>
<div class="card-title">全部设备</div>
<div class="card-subtitle">${formatSessionDateLabel(selectedDate)} · 点击设备查看当天对话历史</div>
</div>
</div>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>设备</th>
${showCompanyColumn ? '<th>所属公司</th>' : ''}
<th>当日会话</th>
<th>轮次</th>
<th>涉及话题</th>
<th>最后对话</th>
<th>操作</th>
</tr>
</thead>
<tbody>${renderDeviceRows(devices, selectedDeviceId, showCompanyColumn)}</tbody>
</table>
</div>
</div>
<div class="chart-card">
${renderDeviceSessionList(selectedDevice, selectedDate)}
</div>
</div>
`}
`;
lucide.createIcons({ elements: [container] });
const backToAllTopics = () => {
sessionStorage.removeItem(SESSION_TOPIC_DRILLDOWN_KEY);
navigateTo('session');
};
container.querySelector('#sessionStepBackBtn')?.addEventListener('click', () => {
sessionStorage.removeItem(SESSION_TOPIC_DRILLDOWN_KEY);
if (fromDashboardDrilldown) {
navigateTo('dashboard');
return;
}
navigateTo('session');
});
container.querySelector('#clearSessionTopicFilter')?.addEventListener('click', backToAllTopics);
container.querySelectorAll('[data-session-back-topic]').forEach(button => {
button.addEventListener('click', backToAllTopics);
});
container.querySelectorAll('[data-session-back-device]').forEach(button => {
button.addEventListener('click', () => {
updateSessionViewState({ deviceId: '' });
navigateTo('session');
});
});
container.querySelector('#sessionDateFilter')?.addEventListener('change', event => {
updateSessionViewState({ date: event.target.value || SESSION_DEFAULT_DATE, deviceId: '' });
navigateTo('session');
});
container.querySelector('#sessionTopicFilter')?.addEventListener('change', event => {
const topic = event.target.value;
setSessionTopicDrilldown(topic, {
companyId: currentCompanyId,
companyName: currentCompanyName,
source: 'session-filter',
date: selectedDate,
});
navigateTo('session');
});
const selectDevice = deviceId => {
if (!deviceId) return;
updateSessionViewState({ deviceId });
navigateTo('session');
};
container.querySelectorAll('.session-device-row').forEach(row => {
row.addEventListener('click', () => {
selectDevice(row.dataset.deviceId);
});
});
container.querySelectorAll('.view-device-btn').forEach(button => {
button.addEventListener('click', event => {
event.stopPropagation();
selectDevice(button.dataset.deviceId);
});
});
});