// ========================================== // Armature — Dashboard Package (v0.31.1) // ========================================== // SDK Exercise Surface. Exercises every sw.* primitive with ZERO // component CSS overrides. All styling comes from the SDK itself. // // Primitives exercised: // sw.userMenu, sw.menu, sw.toolbar, sw.tabs, sw.dropdown, // sw.chat, sw.notes, sw.modal, sw.confirm, sw.toast, // sw.on/emit, sw.api, sw.user/isAdmin, sw.theme, sw.pipe // // Dependencies (loaded by base.html): // API, UI, App, sw, esc (ui-primitives) // ========================================== (function () { 'use strict'; const SURFACE_ID = 'dashboard'; if (window.__SURFACE__ !== SURFACE_ID) return; const base = window.__BASE__ || ''; // ── Init ───────────────────────────────────── async function _init() { const mount = document.getElementById('extension-mount'); if (!mount) return; const surface = document.createElement('div'); surface.className = 'surface-dashboard'; surface.id = 'dashboardSurface'; mount.appendChild(surface); // ── Topbar ── const topbar = _buildTopbar(); surface.appendChild(topbar); // sw.userMenu — flyout: 'down' from topbar sw.userMenu(topbar, { flyout: 'down' }); // ── Body ── const body = document.createElement('div'); body.className = 'dashboard-body'; surface.appendChild(body); // ── Sidebar (tabs: activity + notes) ── const sidebar = document.createElement('div'); sidebar.className = 'dashboard-sidebar'; body.appendChild(sidebar); // sw.tabs — two tabs const tabs = sw.tabs(sidebar, { tabs: [ { id: 'activity', label: 'Activity' }, { id: 'notes', label: 'Notes' }, ], activeTab: 'activity', onActivate: function (id) { if (id === 'notes' && !_notesLoaded) { _notesLoaded = true; if (_notePanel) { _notePanel.loadNotesList(); _notePanel.loadNoteFolders(); } } }, }); // sw.chat — standalone in activity tab const activityPanel = tabs.getPanel('activity'); if (activityPanel) { sw.chat(activityPanel, { id: 'dash', standalone: true }); } // sw.notes — lazy-loaded in notes tab let _notePanel = null; let _notesLoaded = false; const notesPanel = tabs.getPanel('notes'); if (notesPanel) { _notePanel = sw.notes(notesPanel, { projectId: null }); } // ── Main content area ── const main = document.createElement('div'); main.className = 'dashboard-main'; body.appendChild(main); _buildMainContent(main); // ── Theme reactivity ── sw.theme.on('change', function (resolved) { const cards = main.querySelectorAll('.dashboard-card'); cards.forEach(function (c) { c.style.borderColor = ''; // reset to CSS default for new theme }); }); // ── Cross-component events ── sw.on('dashboard.filter.changed', function (payload) { _loadChannels(main.querySelector('.dashboard-cards'), payload.value); }); console.log('[DashboardPkg] Mounted'); } // ── Topbar ────────────────────────────────── function _buildTopbar() { const el = document.createElement('div'); el.className = 'dashboard-topbar'; el.innerHTML = '' + '' + 'Back' + '' + '
' + '' + ''; // sw.toolbar — action buttons const toolbarItems = [ { id: 'refresh', icon: '', title: 'Refresh', onClick: function () { const cards = document.querySelector('.dashboard-cards'); if (cards) _loadChannels(cards, _currentFilter); sw.toast('Refreshed', 'success'); }, }, ]; // sw.isAdmin — conditionally show admin action if (sw.isAdmin) { toolbarItems.push({ id: 'admin', icon: '', title: 'Admin Panel', onClick: function () { window.location.href = base + '/admin'; }, }); } sw.toolbar(el, { items: toolbarItems }); return el; } // ── Main Content ──────────────────────────── let _currentFilter = ''; function _buildMainContent(main) { // sw.user — greeting const user = sw.user; const greeting = document.createElement('div'); greeting.className = 'dashboard-greeting'; greeting.textContent = 'Welcome back' + (user ? ', ' + (user.display_name || user.username) : ''); main.appendChild(greeting); const subtitle = document.createElement('div'); subtitle.className = 'dashboard-subtitle'; subtitle.textContent = 'Your recent conversations' + (sw.isAdmin ? ' \u00b7 Admin' : ''); main.appendChild(subtitle); // ── Filter bar ── const filterBar = document.createElement('div'); filterBar.className = 'dashboard-filter-bar'; main.appendChild(filterBar); const filterLabel = document.createElement('span'); filterLabel.className = 'dashboard-filter-label'; filterLabel.textContent = 'Filter'; filterBar.appendChild(filterLabel); // sw.dropdown — channel type filter sw.dropdown(filterBar, { items: [ { value: '', label: 'All channels' }, { value: 'channel', label: 'Team channels' }, { value: 'direct', label: 'Direct chats' }, { value: 'workflow', label: 'Workflows' }, ], value: '', onChange: function (value) { _currentFilter = value; // sw.emit — cross-component event sw.emit('dashboard.filter.changed', { value: value }); }, }); // ── Cards ── const cards = document.createElement('div'); cards.className = 'dashboard-cards'; main.appendChild(cards); _loadChannels(cards, ''); // ── Admin section (sw.isAdmin) ── if (sw.isAdmin) { const adminSection = document.createElement('div'); adminSection.className = 'dashboard-admin-section'; main.appendChild(adminSection); const sectionTitle = document.createElement('div'); sectionTitle.className = 'dashboard-section-title'; sectionTitle.textContent = 'Administration'; adminSection.appendChild(sectionTitle); const adminCards = document.createElement('div'); adminCards.className = 'dashboard-cards'; adminSection.appendChild(adminCards); _loadAdminCards(adminCards); } } // ── Channel Cards ─────────────────────────── async function _loadChannels(container, typeFilter) { if (!container) return; container.innerHTML = '