Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
Complete rebuild of the admin-ui using Preact + Signals for a lightweight, reactive framework. Features include: - Team-centric workdesks (UI, UX, QA, Admin) - Comprehensive API client with 150+ DSS backend endpoints - Dark mode with system preference detection - Keyboard shortcuts and command palette - AI chat sidebar with Claude integration - Toast notifications system - Export/import functionality for project backup - TypeScript throughout with full type coverage Bundle size: ~66KB main (21KB gzipped), ~5KB framework overhead 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
197 lines
6.3 KiB
TypeScript
197 lines
6.3 KiB
TypeScript
import { JSX } from 'preact';
|
|
import { activePanel, setActivePanel, panelOpen, togglePanel, PanelId } from '../../state/app';
|
|
import { teamPanels } from '../../state/team';
|
|
import { Button } from '../base/Button';
|
|
import './Panel.css';
|
|
|
|
// Panel icons
|
|
const getPanelIcon = (panelId: string): JSX.Element => {
|
|
const icons: Record<string, JSX.Element> = {
|
|
metrics: (
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
<path d="M18 20V10" /><path d="M12 20V4" /><path d="M6 20v-6" />
|
|
</svg>
|
|
),
|
|
tokens: (
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
<circle cx="12" cy="12" r="10" /><path d="M12 6v6l4 2" />
|
|
</svg>
|
|
),
|
|
figma: (
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
<path d="M5 5.5A3.5 3.5 0 0 1 8.5 2H12v7H8.5A3.5 3.5 0 0 1 5 5.5z" />
|
|
<path d="M12 2h3.5a3.5 3.5 0 1 1 0 7H12V2z" />
|
|
</svg>
|
|
),
|
|
activity: (
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12" />
|
|
</svg>
|
|
),
|
|
chat: (
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
|
</svg>
|
|
),
|
|
console: (
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
<polyline points="4 17 10 11 4 5" /><line x1="12" y1="19" x2="20" y2="19" />
|
|
</svg>
|
|
),
|
|
network: (
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
<rect x="2" y="2" width="20" height="8" rx="2" /><rect x="2" y="14" width="20" height="8" rx="2" />
|
|
<line x1="6" y1="6" x2="6.01" y2="6" /><line x1="6" y1="18" x2="6.01" y2="18" />
|
|
</svg>
|
|
),
|
|
tests: (
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
|
|
<polyline points="22 4 12 14.01 9 11.01" />
|
|
</svg>
|
|
),
|
|
system: (
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
<rect x="2" y="3" width="20" height="14" rx="2" /><line x1="8" y1="21" x2="16" y2="21" />
|
|
<line x1="12" y1="17" x2="12" y2="21" />
|
|
</svg>
|
|
)
|
|
};
|
|
return icons[panelId] || icons.activity;
|
|
};
|
|
|
|
// Panel labels
|
|
const panelLabels: Record<string, string> = {
|
|
metrics: 'Metrics',
|
|
tokens: 'Tokens',
|
|
figma: 'Figma',
|
|
activity: 'Activity',
|
|
chat: 'Chat',
|
|
diff: 'Visual Diff',
|
|
accessibility: 'Accessibility',
|
|
screenshots: 'Screenshots',
|
|
console: 'Console',
|
|
network: 'Network',
|
|
tests: 'Tests',
|
|
system: 'System'
|
|
};
|
|
|
|
export function Panel() {
|
|
const panels = teamPanels.value;
|
|
|
|
return (
|
|
<aside className="panel">
|
|
<div className="panel-header">
|
|
<div className="panel-tabs" role="tablist">
|
|
{panels.map(panelId => (
|
|
<button
|
|
key={panelId}
|
|
role="tab"
|
|
className={`panel-tab ${activePanel.value === panelId ? 'active' : ''}`}
|
|
aria-selected={activePanel.value === panelId}
|
|
onClick={() => setActivePanel(panelId)}
|
|
>
|
|
{getPanelIcon(panelId as string)}
|
|
<span>{panelLabels[panelId as string] || panelId}</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={togglePanel}
|
|
aria-label={panelOpen.value ? 'Collapse panel' : 'Expand panel'}
|
|
icon={
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
{panelOpen.value ? (
|
|
<polyline points="6 15 12 9 18 15" />
|
|
) : (
|
|
<polyline points="6 9 12 15 18 9" />
|
|
)}
|
|
</svg>
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
<div className="panel-content" role="tabpanel">
|
|
<PanelContent panelId={activePanel.value} />
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|
|
|
|
function PanelContent({ panelId }: { panelId: PanelId }) {
|
|
// Placeholder content for each panel
|
|
switch (panelId) {
|
|
case 'metrics':
|
|
return <MetricsPanel />;
|
|
case 'activity':
|
|
return <ActivityPanel />;
|
|
case 'console':
|
|
return <ConsolePanel />;
|
|
default:
|
|
return (
|
|
<div className="panel-placeholder">
|
|
<p>{panelLabels[panelId as string] || 'Panel'} content</p>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
function MetricsPanel() {
|
|
return (
|
|
<div className="panel-metrics">
|
|
<div className="metric-card">
|
|
<span className="metric-label">Components</span>
|
|
<span className="metric-value">24</span>
|
|
</div>
|
|
<div className="metric-card">
|
|
<span className="metric-label">Tokens</span>
|
|
<span className="metric-value">156</span>
|
|
</div>
|
|
<div className="metric-card">
|
|
<span className="metric-label">Health Score</span>
|
|
<span className="metric-value metric-success">92%</span>
|
|
</div>
|
|
<div className="metric-card">
|
|
<span className="metric-label">Drift Issues</span>
|
|
<span className="metric-value metric-warning">3</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ActivityPanel() {
|
|
const activities = [
|
|
{ id: 1, action: 'Token sync completed', time: '2 min ago', status: 'success' },
|
|
{ id: 2, action: 'Component generated', time: '5 min ago', status: 'success' },
|
|
{ id: 3, action: 'Figma extraction', time: '10 min ago', status: 'info' }
|
|
];
|
|
|
|
return (
|
|
<div className="panel-activity">
|
|
{activities.map(activity => (
|
|
<div key={activity.id} className={`activity-item activity-${activity.status}`}>
|
|
<span className="activity-action">{activity.action}</span>
|
|
<span className="activity-time">{activity.time}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function ConsolePanel() {
|
|
return (
|
|
<div className="panel-console">
|
|
<pre className="console-output">
|
|
<code>
|
|
{`[INFO] DSS Admin UI loaded
|
|
[INFO] Connected to API server
|
|
[INFO] Project context loaded`}
|
|
</code>
|
|
</pre>
|
|
</div>
|
|
);
|
|
}
|