Full-stack Next.js 16 app with three scraping pipelines: - AirScale CSV → Anymailfinder Bulk Decision Maker search - LinkedIn Sales Navigator → Vayne → Anymailfinder email enrichment - Apify Google SERP → domain extraction → Anymailfinder bulk enrichment Includes Docker multi-stage build + docker-compose for Coolify deployment. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
32 lines
889 B
TypeScript
32 lines
889 B
TypeScript
import { create } from "zustand";
|
|
|
|
export interface ActiveJob {
|
|
id: string;
|
|
type: string;
|
|
status: string;
|
|
progress: number;
|
|
total: number;
|
|
}
|
|
|
|
interface AppStore {
|
|
activeJobs: ActiveJob[];
|
|
sidebarCollapsed: boolean;
|
|
addJob: (job: ActiveJob) => void;
|
|
updateJob: (id: string, updates: Partial<ActiveJob>) => void;
|
|
removeJob: (id: string) => void;
|
|
setSidebarCollapsed: (v: boolean) => void;
|
|
}
|
|
|
|
export const useAppStore = create<AppStore>((set) => ({
|
|
activeJobs: [],
|
|
sidebarCollapsed: false,
|
|
addJob: (job) => set((s) => ({ activeJobs: [...s.activeJobs, job] })),
|
|
updateJob: (id, updates) =>
|
|
set((s) => ({
|
|
activeJobs: s.activeJobs.map((j) => (j.id === id ? { ...j, ...updates } : j)),
|
|
})),
|
|
removeJob: (id) =>
|
|
set((s) => ({ activeJobs: s.activeJobs.filter((j) => j.id !== id) })),
|
|
setSidebarCollapsed: (v) => set({ sidebarCollapsed: v }),
|
|
}));
|