forge/frontend/lib/store.ts

120 lines
2.9 KiB
TypeScript

import { create } from 'zustand';
import { persist } from 'zustand/middleware';
type UserRole = 'user' | 'admin' | 'super_admin';
interface User {
id: string;
email: string;
name: string;
role: UserRole;
avatar_url?: string;
}
export interface Job {
id: string;
module: string;
status: string;
progress: number;
created_at: string;
completed_at?: string;
output_asset_ids?: string[];
error_message?: string;
input_data?: Record<string, any>;
api_provider?: string;
api_model?: string;
}
interface Asset {
id: string;
original_filename: string;
file_type: string;
mime_type: string;
file_size_bytes: number;
created_at: string;
}
interface AppState {
user: User | null;
token: string | null;
activeJobs: Job[];
recentAssets: Asset[];
sidebarCollapsed: boolean;
// Actions
setUser: (user: User | null) => void;
setToken: (token: string | null) => void;
addJob: (job: Job) => void;
updateJob: (id: string, updates: Partial<Job>) => void;
removeJob: (id: string) => void;
clearCompletedJobs: () => void;
setRecentAssets: (assets: Asset[]) => void;
toggleSidebar: () => void;
logout: () => void;
}
export const useStore = create<AppState>()(
persist(
(set) => ({
user: null,
token: null,
activeJobs: [],
recentAssets: [],
sidebarCollapsed: false,
setUser: (user) => set({ user }),
setToken: (token) => {
// Note: Actual authentication is via httpOnly cookie
// This is just a marker for the UI state
set({ token });
},
addJob: (job) => set((state) => {
// Prevent duplicate jobs
if (state.activeJobs.some((j) => j.id === job.id)) {
return state;
}
return {
activeJobs: [job, ...state.activeJobs].slice(0, 50),
};
}),
updateJob: (id, updates) => set((state) => ({
activeJobs: state.activeJobs.map((job) =>
job.id === id ? { ...job, ...updates } : job
),
})),
removeJob: (id) => set((state) => ({
activeJobs: state.activeJobs.filter((job) => job.id !== id),
})),
clearCompletedJobs: () => set((state) => ({
activeJobs: state.activeJobs.filter(
(job) => job.status === 'queued' || job.status === 'processing'
),
})),
setRecentAssets: (assets) => set({ recentAssets: assets }),
toggleSidebar: () => set((state) => ({
sidebarCollapsed: !state.sidebarCollapsed,
})),
logout: () => {
// Clear all state (cookie is cleared by backend on /auth/logout)
set({ user: null, token: null, activeJobs: [], recentAssets: [] });
},
}),
{
name: 'forge-ai-storage',
partialize: (state) => ({
user: state.user,
token: state.token,
sidebarCollapsed: state.sidebarCollapsed,
activeJobs: state.activeJobs.slice(0, 20), // Persist last 20 jobs
}),
}
)
);