import { DeesElement, customElement, html, css, cssManager, property, type TemplateResult, } from '@design.estate/dees-element'; import type { ISgDashboardStats, ISgPackage, ISgOrganization } from '../interfaces.js'; import './sg-protocol-badge.js'; declare global { interface HTMLElementTagNameMap { 'sg-dashboard-view': SgDashboardView; } } @customElement('sg-dashboard-view') export class SgDashboardView extends DeesElement { public static demo = () => html`
`; public static demoGroups = ['Dashboard']; @property({ type: Object }) public accessor stats: ISgDashboardStats = { organizationCount: 0, packageCount: 0, totalDownloads: 0, tokenCount: 0, }; @property({ type: Array }) public accessor recentPackages: ISgPackage[] = []; @property({ type: Array }) public accessor organizations: ISgOrganization[] = []; public static styles = [ cssManager.defaultStyles, css` :host { display: block; color: ${cssManager.bdTheme('#111', '#fff')}; } .dashboard { display: flex; flex-direction: column; gap: 32px; } .page-title { font-size: 24px; font-weight: 700; letter-spacing: -0.02em; color: ${cssManager.bdTheme('#111', '#fff')}; } /* Stats grid */ .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1px; background: ${cssManager.bdTheme('#e5e5e5', '#333')}; border: 1px solid ${cssManager.bdTheme('#e5e5e5', '#333')}; } .stat-card { background: ${cssManager.bdTheme('#fff', '#111')}; padding: 20px; display: flex; flex-direction: column; gap: 4px; } .stat-label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; color: ${cssManager.bdTheme('#888', '#777')}; } .stat-value { font-size: 28px; font-weight: 700; font-family: 'JetBrains Mono', monospace; color: ${cssManager.bdTheme('#111', '#fff')}; } /* Section */ .section { display: flex; flex-direction: column; gap: 12px; } .section-header { display: flex; justify-content: space-between; align-items: center; } .section-title { font-size: 16px; font-weight: 600; color: ${cssManager.bdTheme('#111', '#fff')}; } .section-action { font-size: 13px; color: ${cssManager.bdTheme('#666', '#999')}; cursor: pointer; background: transparent; border: 1px solid ${cssManager.bdTheme('#ddd', '#333')}; padding: 6px 12px; transition: all 150ms ease; } .section-action:hover { border-color: ${cssManager.bdTheme('#999', '#666')}; color: ${cssManager.bdTheme('#111', '#fff')}; } /* Package list */ .package-list { display: flex; flex-direction: column; border: 1px solid ${cssManager.bdTheme('#e5e5e5', '#333')}; } .package-row { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; background: ${cssManager.bdTheme('#fff', '#111')}; border-bottom: 1px solid ${cssManager.bdTheme('#e5e5e5', '#333')}; cursor: pointer; transition: background 100ms ease; } .package-row:last-child { border-bottom: none; } .package-row:hover { background: ${cssManager.bdTheme('#fafafa', '#1a1a1a')}; } .package-info { display: flex; flex-direction: column; gap: 4px; min-width: 0; } .package-name { font-size: 14px; font-weight: 600; color: ${cssManager.bdTheme('#111', '#fff')}; font-family: 'JetBrains Mono', monospace; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .package-meta { display: flex; align-items: center; gap: 8px; font-size: 12px; color: ${cssManager.bdTheme('#888', '#777')}; } .package-right { display: flex; align-items: center; gap: 12px; flex-shrink: 0; } .package-version { font-size: 12px; font-family: 'JetBrains Mono', monospace; color: ${cssManager.bdTheme('#666', '#999')}; background: ${cssManager.bdTheme('#f5f5f5', '#1a1a1a')}; padding: 2px 8px; border: 1px solid ${cssManager.bdTheme('#e5e5e5', '#333')}; } .package-downloads { font-size: 12px; color: ${cssManager.bdTheme('#888', '#777')}; font-family: 'JetBrains Mono', monospace; } /* Orgs grid */ .orgs-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1px; background: ${cssManager.bdTheme('#e5e5e5', '#333')}; border: 1px solid ${cssManager.bdTheme('#e5e5e5', '#333')}; } .org-card { background: ${cssManager.bdTheme('#fff', '#111')}; padding: 16px; cursor: pointer; transition: background 100ms ease; } .org-card:hover { background: ${cssManager.bdTheme('#fafafa', '#1a1a1a')}; } .org-name { font-size: 15px; font-weight: 600; color: ${cssManager.bdTheme('#111', '#fff')}; margin-bottom: 4px; } .org-handle { font-size: 12px; font-family: 'JetBrains Mono', monospace; color: ${cssManager.bdTheme('#888', '#777')}; margin-bottom: 8px; } .org-meta { display: flex; gap: 12px; font-size: 12px; color: ${cssManager.bdTheme('#888', '#777')}; } /* Quick actions */ .quick-actions { display: flex; gap: 8px; flex-wrap: wrap; } .quick-action-btn { padding: 8px 16px; background: transparent; border: 1px solid ${cssManager.bdTheme('#ddd', '#333')}; font-size: 13px; font-weight: 500; color: ${cssManager.bdTheme('#333', '#ddd')}; cursor: pointer; transition: all 150ms ease; } .quick-action-btn:hover { background: ${cssManager.bdTheme('#111', '#fff')}; color: ${cssManager.bdTheme('#fff', '#111')}; border-color: ${cssManager.bdTheme('#111', '#fff')}; } .empty-state { padding: 32px; text-align: center; color: ${cssManager.bdTheme('#888', '#777')}; font-size: 14px; border: 1px solid ${cssManager.bdTheme('#e5e5e5', '#333')}; background: ${cssManager.bdTheme('#fff', '#111')}; } `, ]; public render(): TemplateResult { return html`
Dashboard
Organizations
${this.stats.organizationCount}
Packages
${this.stats.packageCount}
Total Downloads
${this.formatNumber(this.stats.totalDownloads)}
API Tokens
${this.stats.tokenCount}
Recent Packages
${this.recentPackages.length > 0 ? html`
${this.recentPackages.map( (pkg) => html`
this.navigate('package', pkg.id)}>
${pkg.name}
${pkg.description || ''}
${pkg.latestVersion ? html`${pkg.latestVersion}` : ''} ${this.formatNumber(pkg.downloadCount)} pulls
` )}
` : html`
No packages published yet
`}
Organizations
${this.organizations.length > 0 ? html`
${this.organizations.map( (org) => html`
this.navigate('org', org.id)}>
${org.displayName}
@${org.name}
${org.memberCount} members ${org.isPublic ? 'Public' : 'Private'}
` )}
` : html`
No organizations yet
`}
Quick Actions
`; } private formatNumber(n: number): string { if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`; if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`; return n.toString(); } private navigate(type: 'org' | 'package' | 'tokens' | 'packages', id?: string) { this.dispatchEvent( new CustomEvent('navigate', { detail: { type, id }, bubbles: true, composed: true, }) ); } }