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,413 @@
/**
* user-store.js
* User state management store
* Manages current logged-in user, preferences, and integrations
* MVP3: Integrates with backend API while maintaining localStorage persistence
*/
import apiClient from '../services/api-client.js';
export class UserStore {
constructor() {
// Current user state
this.currentUser = this.loadUserFromStorage();
this.isAuthenticated = !!this.currentUser;
this.isLoading = false;
// User preferences
this.preferences = this.loadPreferencesFromStorage() || this.getDefaultPreferences();
// User integrations (API keys, tokens)
this.integrations = this.loadIntegrationsFromStorage() || {};
// Event listeners
this.listeners = new Set();
// Try to verify current user with API on initialization
if (this.isAuthenticated) {
this.verifyUserWithAPI();
}
}
/**
* Get default user preferences
* @returns {Object} Default preferences
*/
getDefaultPreferences() {
return {
theme: 'dark',
language: 'en',
lastTeam: 'ui',
chatCollapsedState: true,
notifications: {
enabled: true,
email: true,
desktop: true
},
layout: {
showChat: true,
showPanel: true,
sidebarWidth: 250
},
editor: {
fontSize: 13,
fontFamily: 'Monaco, Menlo, Ubuntu Mono, monospace',
lineHeight: 1.6
}
};
}
/**
* Load user from localStorage
* @returns {Object|null} User object or null
*/
loadUserFromStorage() {
try {
const stored = localStorage.getItem('current_user');
return stored ? JSON.parse(stored) : null;
} catch (error) {
console.warn('[UserStore] Failed to parse stored user:', error);
return null;
}
}
/**
* Load preferences from localStorage
* @returns {Object|null} Preferences or null
*/
loadPreferencesFromStorage() {
try {
const stored = localStorage.getItem('user_preferences');
return stored ? JSON.parse(stored) : null;
} catch (error) {
console.warn('[UserStore] Failed to parse stored preferences:', error);
return null;
}
}
/**
* Load integrations from localStorage
* @returns {Object} Integrations object
*/
loadIntegrationsFromStorage() {
try {
const stored = localStorage.getItem('user_integrations');
return stored ? JSON.parse(stored) : {};
} catch (error) {
console.warn('[UserStore] Failed to parse stored integrations:', error);
return {};
}
}
/**
* Persist user to localStorage
*/
persistUser() {
if (this.currentUser) {
localStorage.setItem('current_user', JSON.stringify(this.currentUser));
} else {
localStorage.removeItem('current_user');
}
}
/**
* Persist preferences to localStorage
*/
persistPreferences() {
localStorage.setItem('user_preferences', JSON.stringify(this.preferences));
}
/**
* Persist integrations to localStorage
*/
persistIntegrations() {
localStorage.setItem('user_integrations', JSON.stringify(this.integrations));
}
/**
* Verify current user with API
*/
async verifyUserWithAPI() {
try {
const user = await apiClient.getMe();
this.currentUser = user;
this.isAuthenticated = true;
this.persistUser();
this.notifyListeners();
} catch (error) {
console.warn('[UserStore] Failed to verify user with API:', error);
// Fall back to localStorage user
}
}
/**
* Register new user
* @param {string} email - User email
* @param {string} password - User password
* @param {string} name - User name
* @returns {Object} Created user
*/
async register(email, password, name) {
this.isLoading = true;
this.notifyListeners();
try {
const user = await apiClient.register(email, password, name);
this.currentUser = user;
this.isAuthenticated = true;
this.persistUser();
this.notifyListeners();
return user;
} catch (error) {
this.isLoading = false;
this.notifyListeners();
throw error;
}
}
/**
* Login user
* @param {string} email - User email
* @param {string} password - User password
* @returns {Object} Logged-in user
*/
async login(email, password) {
this.isLoading = true;
this.notifyListeners();
try {
const user = await apiClient.login(email, password);
this.currentUser = user;
this.isAuthenticated = true;
this.persistUser();
this.isLoading = false;
this.notifyListeners();
return user;
} catch (error) {
this.isLoading = false;
this.isAuthenticated = false;
this.notifyListeners();
throw error;
}
}
/**
* Logout user
*/
async logout() {
try {
await apiClient.logout();
} catch (error) {
console.warn('[UserStore] API logout failed:', error);
}
this.currentUser = null;
this.isAuthenticated = false;
this.integrations = {};
localStorage.removeItem('current_user');
localStorage.removeItem('user_preferences');
localStorage.removeItem('user_integrations');
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
this.notifyListeners();
}
/**
* Get current user
* @returns {Object|null} Current user or null
*/
getCurrentUser() {
return this.currentUser;
}
/**
* Check if user is authenticated
* @returns {boolean} Authentication status
*/
isLoggedIn() {
return this.isAuthenticated;
}
/**
* Update user profile
* @param {Object} updates - {name, email, avatar, bio}
* @returns {Object} Updated user
*/
async updateProfile(updates) {
try {
// Try API first if authenticated
if (this.isAuthenticated && apiClient.accessToken) {
const updated = await apiClient.updateUser(this.currentUser.id, updates);
this.currentUser = { ...this.currentUser, ...updated };
this.persistUser();
this.notifyListeners();
return updated;
}
} catch (error) {
console.warn('[UserStore] API profile update failed:', error);
}
// Fallback to local update
this.currentUser = { ...this.currentUser, ...updates };
this.persistUser();
this.notifyListeners();
return this.currentUser;
}
/**
* Update user preferences
* @param {Object} updates - Partial preference updates
* @returns {Object} Updated preferences
*/
updatePreferences(updates) {
this.preferences = { ...this.preferences, ...updates };
// Deep merge nested objects
if (updates.notifications) {
this.preferences.notifications = { ...this.preferences.notifications, ...updates.notifications };
}
if (updates.layout) {
this.preferences.layout = { ...this.preferences.layout, ...updates.layout };
}
if (updates.editor) {
this.preferences.editor = { ...this.preferences.editor, ...updates.editor };
}
this.persistPreferences();
this.notifyListeners();
return this.preferences;
}
/**
* Get user preferences
* @returns {Object} Current preferences
*/
getPreferences() {
return { ...this.preferences };
}
/**
* Add or update integration
* @param {string} service - Service name (figma, jira, github, slack, storybook)
* @param {string} apiKey - API key or token for the service
* @param {Object} metadata - Additional metadata (e.g., {projectKey: 'ABC'})
* @returns {Object} Updated integrations
*/
setIntegration(service, apiKey, metadata = {}) {
this.integrations[service] = {
enabled: !!apiKey,
apiKey,
...metadata,
lastUpdated: new Date().toISOString()
};
this.persistIntegrations();
this.notifyListeners();
return this.integrations;
}
/**
* Get specific integration
* @param {string} service - Service name
* @returns {Object|null} Integration config or null
*/
getIntegration(service) {
return this.integrations[service] || null;
}
/**
* Get all integrations
* @returns {Object} All integrations
*/
getIntegrations() {
return { ...this.integrations };
}
/**
* Remove integration
* @param {string} service - Service name
* @returns {Object} Updated integrations
*/
removeIntegration(service) {
delete this.integrations[service];
this.persistIntegrations();
this.notifyListeners();
return this.integrations;
}
/**
* Get user avatar (with fallback)
* @returns {string} Avatar URL or initials
*/
getAvatar() {
if (!this.currentUser) return null;
if (this.currentUser.avatar) return this.currentUser.avatar;
// Generate initials-based avatar
const initials = (this.currentUser.name || this.currentUser.email)
.split(' ')
.map(n => n[0])
.join('')
.toUpperCase();
return `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect fill='%23007acc' width='32' height='32'/%3E%3Ctext x='50%25' y='50%25' dominant-baseline='middle' text-anchor='middle' fill='white' font-size='14' font-weight='bold' font-family='sans-serif' transform='translate(0, 1)'%3E${initials}%3C/text%3E%3C/svg%3E`;
}
/**
* Get user display name
* @returns {string} Name or email
*/
getDisplayName() {
if (!this.currentUser) return 'Anonymous';
return this.currentUser.name || this.currentUser.email;
}
/**
* Subscribe to user state changes
* @param {Function} callback - Called with {currentUser, preferences, integrations, isAuthenticated}
* @returns {Function} Unsubscribe function
*/
subscribe(callback) {
this.listeners.add(callback);
return () => this.listeners.delete(callback);
}
/**
* Notify all listeners of state changes
*/
notifyListeners() {
this.listeners.forEach(listener => listener({
currentUser: this.currentUser,
preferences: this.getPreferences(),
integrations: this.getIntegrations(),
isAuthenticated: this.isAuthenticated
}));
}
/**
* Reset to default state
*/
reset() {
this.currentUser = null;
this.isAuthenticated = false;
this.preferences = this.getDefaultPreferences();
this.integrations = {};
localStorage.removeItem('current_user');
localStorage.removeItem('user_preferences');
localStorage.removeItem('user_integrations');
this.notifyListeners();
}
}
// Singleton instance
let userStoreInstance = null;
export function useUserStore() {
if (!userStoreInstance) {
userStoreInstance = new UserStore();
}
return userStoreInstance;
}