Files
dss/admin-ui/js/workdesks/base-workdesk.js
Digital Production Factory 276ed71f31 Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm

Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)

Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability

Migration completed: $(date)
🤖 Clean migration with full functionality preserved
2025-12-09 18:45:48 -03:00

211 lines
5.9 KiB
JavaScript

/**
* base-workdesk.js
* Abstract base class for all team workdesks
* Refactored: A11y improvements, extracted styles, event delegation
*/
import '../components/tools/ds-metrics-panel.js';
export default class BaseWorkdesk {
constructor(shell) {
if (new.target === BaseWorkdesk) {
throw new Error('Cannot instantiate abstract class BaseWorkdesk');
}
this.shell = shell;
this.teamId = '';
this.teamName = '';
this.tools = [];
}
/**
* Render the workdesk - must be implemented by subclasses
*/
render() {
this.renderSidebar();
this.renderStage();
this.renderPanel();
}
/**
* Render sidebar content
* Improved: Uses semantic <button> elements, injected styles, and keyboard support
*/
renderSidebar() {
const sidebar = this.shell.sidebarContent;
if (!sidebar) return;
// We define styles here to keep BaseWorkdesk self-contained without external CSS dependencies
const css = `
.workdesk-sidebar-container {
padding: 16px;
}
.workdesk-header {
color: var(--vscode-descriptionForeground);
margin-bottom: 12px;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
font-weight: 600;
}
.tools-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.tool-btn {
appearance: none;
background: transparent;
border: 1px solid transparent;
border-radius: 4px;
padding: 8px;
width: 100%;
text-align: left;
cursor: pointer;
color: var(--vscode-foreground);
transition: background-color 0.1s, border-color 0.1s;
}
.tool-btn:hover {
background-color: var(--vscode-list-hoverBackground);
}
.tool-btn:focus-visible {
outline: 2px solid var(--vscode-focusBorder);
outline-offset: -2px;
background-color: var(--vscode-list-activeSelectionBackground);
color: var(--vscode-list-activeSelectionForeground);
}
.tool-name {
font-size: 13px;
margin-bottom: 2px;
font-weight: 500;
}
.tool-desc {
font-size: 11px;
color: var(--vscode-descriptionForeground);
}
/* A11y focus support for description inside button */
.tool-btn:focus-visible .tool-desc {
color: var(--vscode-list-activeSelectionForeground);
opacity: 0.9;
}
`;
sidebar.innerHTML = `
<style>${css}</style>
<div class="workdesk-sidebar-container">
<div class="workdesk-header" id="tools-heading">
${this.teamName} Tools
</div>
<div class="tools-list" role="group" aria-labelledby="tools-heading">
${this.tools.map(tool => `
<button class="tool-btn" data-tool="${tool.id}" type="button">
<div class="tool-name">${tool.name}</div>
<div class="tool-desc">${tool.description}</div>
</button>
`).join('')}
</div>
</div>
`;
// Event delegation for better performance and lifecycle management
const toolsList = sidebar.querySelector('.tools-list');
if (toolsList) {
toolsList.addEventListener('click', (e) => {
const btn = e.target.closest('.tool-btn');
if (btn) {
this.onToolClick(btn.dataset.tool);
}
});
}
}
/**
* Render stage content - must be implemented by subclasses
*/
renderStage() {
throw new Error('renderStage() must be implemented by subclass');
}
/**
* Render panel content - DEPRECATED
* Panel is now configured by ds-shell.js via panel.configure() during team switch
* Subclasses should not override this method
*/
renderPanel() {
// Panel configuration is now handled by ds-shell.js
// This method is kept for backwards compatibility but does nothing
console.log(`[${this.teamId}] Panel configured by shell via panel-config.js`);
}
/**
* Handle tool click - can be overridden by subclasses
*/
onToolClick(toolId) {
console.log(`Tool clicked: ${toolId}`);
const tool = this.tools.find(t => t.id === toolId);
if (tool) {
this.loadTool(tool);
}
}
/**
* Load a tool into the stage - can be overridden by subclasses
* Improved: Loading spinner using CSS instead of Emoji
*/
loadTool(tool) {
const stage = this.shell.stageContent;
if (!stage) return;
// Use a clean CSS spinner with codicon if available
stage.innerHTML = `
<div style="display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; padding: 48px;">
<div class="codicon codicon-loading codicon-modifier-spin" style="font-size: 24px; margin-bottom: 12px;"></div>
<div style="font-size: 12px; color: var(--vscode-descriptionForeground);">Loading ${tool.name}...</div>
</div>
`;
}
/**
* Clean up workdesk - can be overridden by subclasses
*/
destroy() {
if (this.shell.sidebarContent) {
this.shell.sidebarContent.innerHTML = '';
}
if (this.shell.stageContent) {
this.shell.stageContent.innerHTML = '';
}
}
/**
* Utility: Create a card element
* NOTE: Deprecated - use ds-metric-card web component instead
*/
createCard(title, content) {
return `
<div style="
background-color: var(--vscode-sidebar);
border: 1px solid var(--vscode-border);
border-radius: 4px;
padding: 16px;
">
<h3 style="margin-bottom: 12px; font-size: 14px;">${title}</h3>
<div style="font-size: 12px; color: var(--vscode-text-dim);">
${content}
</div>
</div>
`;
}
/**
* Utility: Create a button
*/
createButton(label, onClick) {
const button = document.createElement('button');
button.className = 'button';
button.textContent = label;
button.addEventListener('click', onClick);
return button;
}
}