WIP: Save current work before CHORUS rebrand

- Agent roles integration progress
- Various backend and frontend updates
- Storybook cache cleanup

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
anthonyrawlins
2025-08-01 02:20:56 +10:00
parent 1e81daaf18
commit b6bff318d9
740 changed files with 90022 additions and 279523 deletions

View File

@@ -4,6 +4,7 @@
*/
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
import { apiConfig } from '../config/api';
interface User {
id: string; // UUID as string
@@ -45,7 +46,13 @@ interface AuthProviderProps {
children: ReactNode;
}
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL + '/api' || '/api';
const API_BASE_URL = apiConfig.baseURL + '/api';
// Debug logging
if (apiConfig.debug) {
console.log('Auth API_BASE_URL:', API_BASE_URL);
console.log('apiConfig.baseURL:', apiConfig.baseURL);
}
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const [user, setUser] = useState<User | null>(null);
@@ -127,6 +134,7 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
localStorage.setItem('hive_tokens', JSON.stringify(newTokens));
localStorage.setItem('hive_user', JSON.stringify(data.user));
localStorage.setItem('token', newTokens.access_token);
return true;
} else {
@@ -168,6 +176,7 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
// Store in localStorage
localStorage.setItem('hive_tokens', JSON.stringify(newTokens));
localStorage.setItem('hive_user', JSON.stringify(data.user));
localStorage.setItem('token', newTokens.access_token);
} catch (error: any) {
throw new Error(error.message || 'Login failed');
@@ -203,6 +212,7 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
// Store in localStorage
localStorage.setItem('hive_tokens', JSON.stringify(newTokens));
localStorage.setItem('hive_user', JSON.stringify(data.user));
localStorage.setItem('token', newTokens.access_token);
} catch (error) {
console.error('Registration failed:', error);
throw error;
@@ -248,6 +258,7 @@ export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
setTokens(null);
localStorage.removeItem('hive_tokens');
localStorage.removeItem('hive_user');
localStorage.removeItem('token');
};
const contextValue: AuthContextType = {

View File

@@ -1,5 +1,6 @@
import React, { createContext, useContext, useEffect, useState } from 'react';
import React, { createContext, useContext, useEffect, useState, useRef } from 'react';
import { useSocketIO, SocketIOMessage } from '../hooks/useSocketIO';
import { useAuth } from './AuthContext';
interface SocketIOContextType {
isConnected: boolean;
@@ -45,6 +46,8 @@ export const SocketIOProvider: React.FC<SocketIOProviderProps> = ({
</SocketIOContext.Provider>
);
}
const { isAuthenticated } = useAuth();
const [subscriptions, setSubscriptions] = useState<Map<string, Set<(data: any) => void>>>(new Map());
const {
@@ -91,6 +94,21 @@ export const SocketIOProvider: React.FC<SocketIOProviderProps> = ({
// console.error('Socket.IO error:', error);
}
});
// Reconnect when authentication status changes (but only once)
const hasReconnectedRef = useRef(false);
useEffect(() => {
if (isAuthenticated && !hasReconnectedRef.current) {
console.log('User authenticated, reconnecting Socket.IO...');
hasReconnectedRef.current = true;
setTimeout(() => {
reconnect();
}, 1000); // Delay to prevent immediate reconnection loop
}
if (!isAuthenticated) {
hasReconnectedRef.current = false;
}
}, [isAuthenticated, reconnect]);
const subscribe = (messageType: string, handler: (data: any) => void) => {
setSubscriptions(prev => {

View File

@@ -0,0 +1,65 @@
import React, { createContext, useContext, useEffect, useState } from 'react';
interface ThemeContextType {
isDarkMode: boolean;
toggleDarkMode: () => void;
setDarkMode: (enabled: boolean) => void;
}
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export const useTheme = () => {
const context = useContext(ThemeContext);
if (context === undefined) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
interface ThemeProviderProps {
children: React.ReactNode;
}
export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
const [isDarkMode, setIsDarkMode] = useState(() => {
// Check localStorage for saved preference
const saved = localStorage.getItem('darkMode');
if (saved !== null) {
return JSON.parse(saved);
}
// Default to system preference
return window.matchMedia('(prefers-color-scheme: dark)').matches;
});
useEffect(() => {
// Apply dark mode class to document
if (isDarkMode) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
// Save preference to localStorage
localStorage.setItem('darkMode', JSON.stringify(isDarkMode));
}, [isDarkMode]);
const toggleDarkMode = () => {
setIsDarkMode(!isDarkMode);
};
const setDarkMode = (enabled: boolean) => {
setIsDarkMode(enabled);
};
const value = {
isDarkMode,
toggleDarkMode,
setDarkMode,
};
return (
<ThemeContext.Provider value={value}>
{children}
</ThemeContext.Provider>
);
};