120 lines
2.9 KiB
TypeScript
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
|
|
}),
|
|
}
|
|
)
|
|
);
|