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
This commit is contained in:
Digital Production Factory
2025-12-09 18:45:48 -03:00
commit 276ed71f31
884 changed files with 373737 additions and 0 deletions

View File

@@ -0,0 +1,434 @@
/**
* ds-user-settings.js
* User settings page component
* Manages user profile, preferences, integrations, and account settings
* MVP3: Full integration with backend API and user-store
*/
import { useUserStore } from '../../stores/user-store.js';
export default class DSUserSettings extends HTMLElement {
constructor() {
super();
this.userStore = useUserStore();
this.activeTab = 'profile';
this.isLoading = false;
this.formChanges = {};
}
connectedCallback() {
this.render();
this.setupEventListeners();
this.subscribeToUserStore();
}
subscribeToUserStore() {
this.unsubscribe = this.userStore.subscribe(() => {
this.updateUI();
});
}
render() {
const user = this.userStore.getCurrentUser();
const displayName = this.userStore.getDisplayName();
const avatar = this.userStore.getAvatar();
this.innerHTML = `
<div style="display: flex; flex-direction: column; height: 100%; background: var(--vscode-bg);">
<!-- Header -->
<div style="padding: 24px; border-bottom: 1px solid var(--vscode-border);">
<div style="display: flex; align-items: center; gap: 16px; margin-bottom: 24px;">
<img src="${avatar}" alt="Avatar" style="width: 64px; height: 64px; border-radius: 8px; background: var(--vscode-sidebar);" />
<div>
<h1 style="margin: 0 0 4px 0; font-size: 24px;">${displayName}</h1>
<p style="margin: 0; color: var(--vscode-text-dim); font-size: 12px;">${user?.email || 'Not logged in'}</p>
</div>
</div>
</div>
<!-- Tabs -->
<div style="display: flex; border-bottom: 1px solid var(--vscode-border); padding: 0 24px; gap: 24px; flex-shrink: 0;">
<button class="settings-tab" data-tab="profile" style="padding: 12px 0; border: none; background: transparent; color: var(--vscode-text); cursor: pointer; border-bottom: 2px solid transparent; font-size: 13px; transition: all 0.2s;">
👤 Profile
</button>
<button class="settings-tab" data-tab="preferences" style="padding: 12px 0; border: none; background: transparent; color: var(--vscode-text-dim); cursor: pointer; border-bottom: 2px solid transparent; font-size: 13px; transition: all 0.2s;">
⚙️ Preferences
</button>
<button class="settings-tab" data-tab="integrations" style="padding: 12px 0; border: none; background: transparent; color: var(--vscode-text-dim); cursor: pointer; border-bottom: 2px solid transparent; font-size: 13px; transition: all 0.2s;">
🔗 Integrations
</button>
<button class="settings-tab" data-tab="about" style="padding: 12px 0; border: none; background: transparent; color: var(--vscode-text-dim); cursor: pointer; border-bottom: 2px solid transparent; font-size: 13px; transition: all 0.2s;">
About
</button>
</div>
<!-- Content -->
<div style="flex: 1; overflow-y: auto; padding: 24px;">
<!-- Profile Tab -->
<div id="profile-tab" class="settings-content">
<div style="max-width: 600px;">
<h2 style="margin: 0 0 16px 0; font-size: 18px;">Profile Settings</h2>
<div style="margin-bottom: 16px;">
<label style="display: block; font-size: 12px; font-weight: 500; margin-bottom: 4px;">Full Name</label>
<input id="profile-name" type="text" value="${user?.name || ''}" placeholder="Your full name" style="width: 100%; padding: 8px 12px; background: var(--vscode-input-background); border: 1px solid var(--vscode-border); color: var(--vscode-text); border-radius: 4px; font-size: 13px;" />
</div>
<div style="margin-bottom: 16px;">
<label style="display: block; font-size: 12px; font-weight: 500; margin-bottom: 4px;">Email</label>
<input id="profile-email" type="email" value="${user?.email || ''}" placeholder="your@email.com" style="width: 100%; padding: 8px 12px; background: var(--vscode-input-background); border: 1px solid var(--vscode-border); color: var(--vscode-text); border-radius: 4px; font-size: 13px;" />
</div>
<div style="margin-bottom: 16px;">
<label style="display: block; font-size: 12px; font-weight: 500; margin-bottom: 4px;">Role</label>
<div style="padding: 8px 12px; background: var(--vscode-sidebar); border: 1px solid var(--vscode-border); color: var(--vscode-text-dim); border-radius: 4px; font-size: 13px;">
${user?.role || 'User'} <span style="color: var(--vscode-text-dim); font-size: 11px;">(Read-only)</span>
</div>
</div>
<div style="margin-bottom: 24px;">
<label style="display: block; font-size: 12px; font-weight: 500; margin-bottom: 4px;">Bio</label>
<textarea id="profile-bio" placeholder="Tell us about yourself..." style="width: 100%; padding: 8px 12px; background: var(--vscode-input-background); border: 1px solid var(--vscode-border); color: var(--vscode-text); border-radius: 4px; font-size: 13px; min-height: 80px; resize: vertical;" >${user?.bio || ''}</textarea>
</div>
<div style="display: flex; gap: 8px;">
<button id="save-profile-btn" style="padding: 8px 16px; background: var(--vscode-button-background); color: var(--vscode-button-foreground); border: none; border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: 500;">
Save Changes
</button>
<button id="change-password-btn" style="padding: 8px 16px; background: var(--vscode-button-secondaryBackground); color: var(--vscode-button-secondaryForeground); border: none; border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: 500;">
Change Password
</button>
</div>
</div>
</div>
<!-- Preferences Tab -->
<div id="preferences-tab" class="settings-content" style="display: none;">
<div style="max-width: 600px;">
<h2 style="margin: 0 0 16px 0; font-size: 18px;">Preferences</h2>
<h3 style="margin: 0 0 12px 0; font-size: 14px; color: var(--vscode-text-dim);">Theme</h3>
<div style="margin-bottom: 24px;">
<label style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px; cursor: pointer;">
<input type="radio" name="theme" value="dark" checked />
<span style="font-size: 12px;">Dark</span>
</label>
<label style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
<input type="radio" name="theme" value="light" />
<span style="font-size: 12px;">Light</span>
</label>
</div>
<h3 style="margin: 0 0 12px 0; font-size: 14px; color: var(--vscode-text-dim);">Language</h3>
<div style="margin-bottom: 24px;">
<select id="pref-language" style="padding: 8px 12px; background: var(--vscode-input-background); border: 1px solid var(--vscode-border); color: var(--vscode-text); border-radius: 4px; font-size: 12px; cursor: pointer;">
<option value="en">English</option>
<option value="es">Español</option>
<option value="fr">Français</option>
<option value="de">Deutsch</option>
<option value="ja">日本語</option>
</select>
</div>
<h3 style="margin: 0 0 12px 0; font-size: 14px; color: var(--vscode-text-dim);">Notifications</h3>
<div style="margin-bottom: 24px;">
<label style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px; cursor: pointer;">
<input id="pref-notifications" type="checkbox" checked />
<span style="font-size: 12px;">Enable notifications</span>
</label>
<label style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px; cursor: pointer;">
<input id="pref-email-notifications" type="checkbox" checked />
<span style="font-size: 12px;">Email notifications</span>
</label>
<label style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
<input id="pref-desktop-notifications" type="checkbox" checked />
<span style="font-size: 12px;">Desktop notifications</span>
</label>
</div>
<button id="save-preferences-btn" style="padding: 8px 16px; background: var(--vscode-button-background); color: var(--vscode-button-foreground); border: none; border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: 500;">
Save Preferences
</button>
</div>
</div>
<!-- Integrations Tab -->
<div id="integrations-tab" class="settings-content" style="display: none;">
<div style="max-width: 600px;">
<h2 style="margin: 0 0 16px 0; font-size: 18px;">Integrations</h2>
<!-- Figma Integration -->
<div style="background: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px; margin-bottom: 12px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<div>
<h3 style="margin: 0 0 4px 0; font-size: 13px; font-weight: 500;">🎨 Figma</h3>
<p style="margin: 0; font-size: 11px; color: var(--vscode-text-dim);">Connect your Figma account for design token extraction</p>
</div>
<span id="figma-status" class="integration-status" style="font-size: 11px; padding: 4px 8px; background: #4CAF50; color: white; border-radius: 3px; display: none;">Connected</span>
</div>
<div style="display: flex; gap: 8px;">
<input id="figma-api-key" type="password" placeholder="Enter Figma API key" style="flex: 1; padding: 6px 10px; background: var(--vscode-input-background); border: 1px solid var(--vscode-border); color: var(--vscode-text); border-radius: 3px; font-size: 11px;" />
<button class="integration-save-btn" data-service="figma" style="padding: 6px 12px; background: var(--vscode-button-background); color: var(--vscode-button-foreground); border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">Save</button>
</div>
</div>
<!-- GitHub Integration -->
<div style="background: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px; margin-bottom: 12px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<div>
<h3 style="margin: 0 0 4px 0; font-size: 13px; font-weight: 500;">🐙 GitHub</h3>
<p style="margin: 0; font-size: 11px; color: var(--vscode-text-dim);">Connect GitHub for component library integration</p>
</div>
<span id="github-status" class="integration-status" style="font-size: 11px; padding: 4px 8px; background: #666; color: white; border-radius: 3px; display: none;">Connected</span>
</div>
<div style="display: flex; gap: 8px;">
<input id="github-api-key" type="password" placeholder="Enter GitHub personal access token" style="flex: 1; padding: 6px 10px; background: var(--vscode-input-background); border: 1px solid var(--vscode-border); color: var(--vscode-text); border-radius: 3px; font-size: 11px;" />
<button class="integration-save-btn" data-service="github" style="padding: 6px 12px; background: var(--vscode-button-background); color: var(--vscode-button-foreground); border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">Save</button>
</div>
</div>
<!-- Jira Integration -->
<div style="background: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px; margin-bottom: 12px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<div>
<h3 style="margin: 0 0 4px 0; font-size: 13px; font-weight: 500;">📋 Jira</h3>
<p style="margin: 0; font-size: 11px; color: var(--vscode-text-dim);">Connect Jira for issue tracking integration</p>
</div>
<span id="jira-status" class="integration-status" style="font-size: 11px; padding: 4px 8px; background: #666; color: white; border-radius: 3px; display: none;">Connected</span>
</div>
<div style="display: flex; gap: 8px; margin-bottom: 8px;">
<input id="jira-api-key" type="password" placeholder="Enter Jira API token" style="flex: 1; padding: 6px 10px; background: var(--vscode-input-background); border: 1px solid var(--vscode-border); color: var(--vscode-text); border-radius: 3px; font-size: 11px;" />
<button class="integration-save-btn" data-service="jira" style="padding: 6px 12px; background: var(--vscode-button-background); color: var(--vscode-button-foreground); border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">Save</button>
</div>
<input id="jira-project-key" type="text" placeholder="Jira project key (optional)" style="width: 100%; padding: 6px 10px; background: var(--vscode-input-background); border: 1px solid var(--vscode-border); color: var(--vscode-text); border-radius: 3px; font-size: 11px;" />
</div>
<!-- Slack Integration -->
<div style="background: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px; margin-bottom: 12px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<div>
<h3 style="margin: 0 0 4px 0; font-size: 13px; font-weight: 500;">💬 Slack</h3>
<p style="margin: 0; font-size: 11px; color: var(--vscode-text-dim);">Connect Slack for team notifications</p>
</div>
<span id="slack-status" class="integration-status" style="font-size: 11px; padding: 4px 8px; background: #666; color: white; border-radius: 3px; display: none;">Connected</span>
</div>
<div style="display: flex; gap: 8px;">
<input id="slack-webhook" type="password" placeholder="Enter Slack webhook URL" style="flex: 1; padding: 6px 10px; background: var(--vscode-input-background); border: 1px solid var(--vscode-border); color: var(--vscode-text); border-radius: 3px; font-size: 11px;" />
<button class="integration-save-btn" data-service="slack" style="padding: 6px 12px; background: var(--vscode-button-background); color: var(--vscode-button-foreground); border: none; border-radius: 3px; cursor: pointer; font-size: 11px;">Save</button>
</div>
</div>
</div>
</div>
<!-- About Tab -->
<div id="about-tab" class="settings-content" style="display: none;">
<div style="max-width: 600px;">
<h2 style="margin: 0 0 16px 0; font-size: 18px;">About</h2>
<div style="background: var(--vscode-sidebar); border: 1px solid var(--vscode-border); border-radius: 4px; padding: 16px; margin-bottom: 16px;">
<h3 style="margin: 0 0 12px 0; font-size: 14px;">Design System Swarm</h3>
<p style="margin: 0 0 8px 0; font-size: 12px; color: var(--vscode-text-dim);">Version: 3.0.0 (MVP3)</p>
<p style="margin: 0 0 8px 0; font-size: 12px;">Advanced design system management platform with AI assistance, design tokens, and multi-team collaboration.</p>
<p style="margin: 0; font-size: 11px; color: var(--vscode-text-dim);">© 2024 Design System Swarm. All rights reserved.</p>
</div>
<h3 style="margin: 16px 0 8px 0; font-size: 14px;">Features</h3>
<ul style="margin: 0; padding-left: 20px; font-size: 12px;">
<li>Design token management and synchronization</li>
<li>Figma integration with automated token extraction</li>
<li>Multi-team collaboration workspace</li>
<li>AI-powered design system analysis</li>
<li>Storybook integration and component documentation</li>
<li>GitHub and Jira integration</li>
</ul>
<div style="margin-top: 24px;">
<h3 style="margin: 0 0 8px 0; font-size: 14px;">Account Actions</h3>
<button id="logout-btn" style="padding: 8px 16px; background: #F44336; color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 12px; font-weight: 500;">
🚪 Logout
</button>
</div>
</div>
</div>
</div>
</div>
`;
}
setupEventListeners() {
// Tab switching
this.querySelectorAll('.settings-tab').forEach(btn => {
btn.addEventListener('click', (e) => {
this.switchTab(e.target.closest('button').dataset.tab);
});
});
// Profile tab
const saveProfileBtn = this.querySelector('#save-profile-btn');
if (saveProfileBtn) {
saveProfileBtn.addEventListener('click', () => this.saveProfile());
}
// Preferences tab
const savePreferencesBtn = this.querySelector('#save-preferences-btn');
if (savePreferencesBtn) {
savePreferencesBtn.addEventListener('click', () => this.savePreferences());
}
// Integration save buttons
this.querySelectorAll('.integration-save-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
const service = e.target.dataset.service;
const apiKeyInput = this.querySelector(`#${service}-api-key`) || this.querySelector(`#${service}-webhook`);
const apiKey = apiKeyInput?.value || '';
this.saveIntegration(service, apiKey);
});
});
// Logout button
const logoutBtn = this.querySelector('#logout-btn');
if (logoutBtn) {
logoutBtn.addEventListener('click', () => this.logout());
}
// Change password button
const changePasswordBtn = this.querySelector('#change-password-btn');
if (changePasswordBtn) {
changePasswordBtn.addEventListener('click', () => this.showChangePasswordDialog());
}
}
switchTab(tabName) {
this.activeTab = tabName;
// Hide all tabs
this.querySelectorAll('.settings-content').forEach(tab => {
tab.style.display = 'none';
});
// Show selected tab
const selectedTab = this.querySelector(`#${tabName}-tab`);
if (selectedTab) {
selectedTab.style.display = 'block';
}
// Update tab styling
this.querySelectorAll('.settings-tab').forEach(btn => {
const isActive = btn.dataset.tab === tabName;
btn.style.borderBottomColor = isActive ? 'var(--vscode-accent)' : 'transparent';
btn.style.color = isActive ? 'var(--vscode-text)' : 'var(--vscode-text-dim)';
});
}
async saveProfile() {
const name = this.querySelector('#profile-name')?.value || '';
const email = this.querySelector('#profile-email')?.value || '';
const bio = this.querySelector('#profile-bio')?.value || '';
try {
await this.userStore.updateProfile({ name, email, bio });
this.showNotification('Profile saved successfully', 'success');
} catch (error) {
this.showNotification('Failed to save profile', 'error');
console.error(error);
}
}
savePreferences() {
const theme = this.querySelector('input[name="theme"]:checked')?.value || 'dark';
const language = this.querySelector('#pref-language')?.value || 'en';
const notifications = this.querySelector('#pref-notifications')?.checked || false;
this.userStore.updatePreferences({
theme,
language,
notifications: {
enabled: notifications,
email: this.querySelector('#pref-email-notifications')?.checked || false,
desktop: this.querySelector('#pref-desktop-notifications')?.checked || false
}
});
this.showNotification('Preferences saved', 'success');
}
saveIntegration(service, apiKey) {
if (!apiKey) {
this.userStore.removeIntegration(service);
this.showNotification(`${service} integration removed`, 'success');
} else {
const metadata = {};
if (service === 'jira') {
metadata.projectKey = this.querySelector('#jira-project-key')?.value || '';
}
this.userStore.setIntegration(service, apiKey, metadata);
this.showNotification(`${service} integration saved`, 'success');
}
this.updateIntegrationStatus();
}
updateIntegrationStatus() {
const integrations = this.userStore.getIntegrations();
['figma', 'github', 'jira', 'slack'].forEach(service => {
const status = this.querySelector(`#${service}-status`);
if (status) {
if (integrations[service]?.enabled) {
status.style.display = 'inline-block';
status.style.background = '#4CAF50';
status.textContent = 'Connected';
} else {
status.style.display = 'none';
}
}
});
}
updateUI() {
// Update display when user state changes
const user = this.userStore.getCurrentUser();
const displayName = this.userStore.getDisplayName();
// Re-render component
this.render();
this.setupEventListeners();
}
showChangePasswordDialog() {
// Placeholder for password change dialog
// In a real implementation, this would show a modal dialog
alert('Change password functionality would be implemented here.\n\nIn production, this would show a modal with current password and new password fields.');
}
showNotification(message, type = 'info') {
const notificationEl = document.createElement('div');
notificationEl.style.cssText = `
position: fixed;
bottom: 24px;
right: 24px;
padding: 12px 16px;
background: ${type === 'success' ? '#4CAF50' : type === 'error' ? '#F44336' : '#0066CC'};
color: white;
border-radius: 4px;
font-size: 12px;
font-weight: 500;
z-index: 1000;
animation: slideInUp 0.3s ease-out;
`;
notificationEl.textContent = message;
document.body.appendChild(notificationEl);
setTimeout(() => {
notificationEl.style.animation = 'slideOutDown 0.3s ease-in';
setTimeout(() => notificationEl.remove(), 300);
}, 3000);
}
disconnectedCallback() {
if (this.unsubscribe) {
this.unsubscribe();
}
}
}
customElements.define('ds-user-settings', DSUserSettings);