Files
dss/admin-ui/js/components/admin/ds-admin-settings.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

260 lines
8.1 KiB
JavaScript

/**
* ds-admin-settings.js
* Admin settings panel for DSS configuration
* Allows configuration of hostname, port, and local/remote setup
*/
import { useAdminStore } from '../../stores/admin-store.js';
export default class AdminSettings extends HTMLElement {
constructor() {
super();
this.adminStore = useAdminStore();
this.state = this.adminStore.getState();
}
connectedCallback() {
this.render();
this.setupEventListeners();
this.unsubscribe = this.adminStore.subscribe(() => {
this.state = this.adminStore.getState();
this.updateUI();
});
}
disconnectedCallback() {
if (this.unsubscribe) this.unsubscribe();
}
render() {
this.innerHTML = `
<div style="padding: 24px; max-width: 600px;">
<h2 style="margin-bottom: 24px; font-size: 20px;">DSS Settings</h2>
<!-- Hostname Setting -->
<div style="margin-bottom: 24px;">
<label style="display: block; margin-bottom: 8px; font-weight: 500;">
Hostname
</label>
<input
id="hostname-input"
type="text"
value="${this.state.hostname}"
style="
width: 100%;
padding: 8px 12px;
border: 1px solid var(--vscode-input-border);
background: var(--vscode-input-background);
color: var(--vscode-foreground);
border-radius: 4px;
font-family: monospace;
"
placeholder="localhost or IP address"
/>
<div style="font-size: 11px; color: var(--vscode-text-dim); margin-top: 4px;">
Default: localhost
</div>
</div>
<!-- Port Setting -->
<div style="margin-bottom: 24px;">
<label style="display: block; margin-bottom: 8px; font-weight: 500;">
Storybook Port
</label>
<input
id="port-input"
type="number"
value="${this.state.port}"
style="
width: 100%;
padding: 8px 12px;
border: 1px solid var(--vscode-input-border);
background: var(--vscode-input-background);
color: var(--vscode-foreground);
border-radius: 4px;
font-family: monospace;
"
placeholder="6006"
min="1"
max="65535"
/>
<div style="font-size: 11px; color: var(--vscode-text-dim); margin-top: 4px;">
Default: 6006 (Storybook standard port)
</div>
</div>
<!-- DSS Setup Type -->
<div style="margin-bottom: 24px;">
<label style="display: block; margin-bottom: 12px; font-weight: 500;">
DSS Setup Type
</label>
<div style="display: flex; gap: 16px;">
<label style="display: flex; align-items: center; cursor: pointer;">
<input
type="radio"
name="setup-type"
value="local"
${this.state.isRemote ? '' : 'checked'}
style="margin-right: 8px;"
/>
<span>Local</span>
</label>
<label style="display: flex; align-items: center; cursor: pointer;">
<input
type="radio"
name="setup-type"
value="remote"
${this.state.isRemote ? 'checked' : ''}
style="margin-right: 8px;"
/>
<span>Remote (Headless)</span>
</label>
</div>
<div style="font-size: 11px; color: var(--vscode-text-dim); margin-top: 8px;">
<strong>Local:</strong> Uses browser devtools and local services<br/>
<strong>Remote:</strong> Uses headless tools and MCP providers
</div>
</div>
<!-- Current Configuration Display -->
<div style="
background: var(--vscode-sidebar);
border: 1px solid var(--vscode-border);
border-radius: 4px;
padding: 12px;
margin-bottom: 24px;
">
<div style="font-size: 11px; color: var(--vscode-text-dim); margin-bottom: 8px;">CURRENT STORYBOOK URL:</div>
<div style="
font-family: monospace;
font-size: 12px;
word-break: break-all;
color: var(--vscode-foreground);
" id="storybook-url-display">
${this.getStorybookUrlDisplay()}
</div>
</div>
<!-- Action Buttons -->
<div style="display: flex; gap: 8px;">
<button
id="save-btn"
style="
padding: 8px 16px;
background: var(--vscode-button-background);
color: var(--vscode-button-foreground);
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
"
>
Save Settings
</button>
<button
id="reset-btn"
style="
padding: 8px 16px;
background: var(--vscode-button-secondaryBackground);
color: var(--vscode-button-secondaryForeground);
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 500;
"
>
Reset to Defaults
</button>
</div>
</div>
`;
}
setupEventListeners() {
const hostnameInput = this.querySelector('#hostname-input');
const portInput = this.querySelector('#port-input');
const setupTypeRadios = this.querySelectorAll('input[name="setup-type"]');
const saveBtn = this.querySelector('#save-btn');
const resetBtn = this.querySelector('#reset-btn');
// Update on input (but don't save immediately)
hostnameInput.addEventListener('change', () => {
this.adminStore.setHostname(hostnameInput.value);
});
portInput.addEventListener('change', () => {
const port = parseInt(portInput.value);
if (port > 0 && port <= 65535) {
this.adminStore.setPort(port);
}
});
setupTypeRadios.forEach(radio => {
radio.addEventListener('change', (e) => {
this.adminStore.setRemote(e.target.value === 'remote');
});
});
saveBtn.addEventListener('click', () => {
this.showNotification('Settings saved successfully!');
console.log('[AdminSettings] Settings saved:', this.adminStore.getState());
});
resetBtn.addEventListener('click', () => {
if (confirm('Reset all settings to defaults?')) {
this.adminStore.reset();
this.render();
this.setupEventListeners();
this.showNotification('Settings reset to defaults');
}
});
}
updateUI() {
const hostnameInput = this.querySelector('#hostname-input');
const portInput = this.querySelector('#port-input');
const setupTypeRadios = this.querySelectorAll('input[name="setup-type"]');
const urlDisplay = this.querySelector('#storybook-url-display');
if (hostnameInput) hostnameInput.value = this.state.hostname;
if (portInput) portInput.value = this.state.port;
setupTypeRadios.forEach(radio => {
radio.checked = (radio.value === 'remote') === this.state.isRemote;
});
if (urlDisplay) {
urlDisplay.textContent = this.getStorybookUrlDisplay();
}
}
getStorybookUrlDisplay() {
return this.adminStore.getStorybookUrl('default');
}
showNotification(message) {
const notification = document.createElement('div');
notification.textContent = message;
notification.style.cssText = `
position: fixed;
bottom: 20px;
right: 20px;
background: var(--vscode-notifications-background);
color: var(--vscode-foreground);
padding: 12px 16px;
border-radius: 4px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
z-index: 1000;
animation: slideIn 0.3s ease;
`;
document.body.appendChild(notification);
setTimeout(() => {
notification.style.animation = 'slideOut 0.3s ease';
setTimeout(() => notification.remove(), 300);
}, 3000);
}
}
customElements.define('ds-admin-settings', AdminSettings);