Add campaign layer, GPU model caching, file browser cleanup
- Campaigns: new table, CRUD API, React context + provider
- Sessions: scoped to campaigns, paths under campaigns/{id}/
- File browser: scoped per campaign, removed copy/paste/autoPlay
- Sidebar: campaign selector dropdown at top
- Transcribe: GPU model cached/released via job counter
- Jobs: status text updates dynamically in real-time
- Auto-redirect: blocked when summarize job is active
This commit is contained in:
59
frontend/src/contexts/CampaignContext.tsx
Normal file
59
frontend/src/contexts/CampaignContext.tsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react';
|
||||
import { campaignApi, type Campaign } from '../api';
|
||||
|
||||
interface CampaignContextType {
|
||||
currentCampaign: Campaign | null;
|
||||
setCampaign: (c: Campaign | null) => void;
|
||||
campaigns: Campaign[];
|
||||
refreshCampaigns: () => void;
|
||||
}
|
||||
|
||||
const CampaignContext = createContext<CampaignContextType | null>(null);
|
||||
|
||||
export function CampaignProvider({ children }: { children: ReactNode }) {
|
||||
const [campaigns, setCampaigns] = useState<Campaign[]>([]);
|
||||
const [currentCampaign, setCurrentCampaign] = useState<Campaign | null>(null);
|
||||
|
||||
const refreshCampaigns = useCallback(async () => {
|
||||
try {
|
||||
const list = await campaignApi.list();
|
||||
setCampaigns(list);
|
||||
} catch {}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
refreshCampaigns();
|
||||
}, [refreshCampaigns]);
|
||||
|
||||
useEffect(() => {
|
||||
if (campaigns.length === 0) return;
|
||||
if (!currentCampaign) {
|
||||
const stored = localStorage.getItem('campaignId');
|
||||
const match = stored ? campaigns.find(c => c.id === stored) : null;
|
||||
setCurrentCampaign(match || campaigns[0]);
|
||||
} else {
|
||||
const stillExists = campaigns.find(c => c.id === currentCampaign.id);
|
||||
if (!stillExists) {
|
||||
setCurrentCampaign(campaigns[0] || null);
|
||||
}
|
||||
}
|
||||
}, [campaigns]);
|
||||
|
||||
const setCampaign = (c: Campaign | null) => {
|
||||
if (c) localStorage.setItem('campaignId', c.id);
|
||||
else localStorage.removeItem('campaignId');
|
||||
setCurrentCampaign(c);
|
||||
};
|
||||
|
||||
return (
|
||||
<CampaignContext.Provider value={{ currentCampaign, setCampaign, campaigns, refreshCampaigns }}>
|
||||
{children}
|
||||
</CampaignContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useCampaign() {
|
||||
const ctx = useContext(CampaignContext);
|
||||
if (!ctx) throw new Error('useCampaign must be used within CampaignProvider');
|
||||
return ctx;
|
||||
}
|
||||
Reference in New Issue
Block a user