Files
dss/admin-ui/src/components/layout/Panel.tsx
Bruno Sarlo 71c6dc805a
Some checks failed
DSS Project Analysis / dss-context-update (push) Has been cancelled
feat: Rebuild admin-ui with Preact + Signals
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>
2025-12-10 20:29:21 -03:00

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>
);
}