feat: initial SPA frontend for network app

This commit is contained in:
2026-01-28 19:51:45 +00:00
commit 1afd5d5bac
35 changed files with 7071 additions and 0 deletions

87
src/stores/clients.ts Normal file
View File

@@ -0,0 +1,87 @@
import { create } from 'zustand';
import type { Client, ClientCreate } from '@/types';
import { api } from '@/lib/api';
interface ClientsState {
clients: Client[];
selectedClient: Client | null;
isLoading: boolean;
error: string | null;
searchQuery: string;
selectedTag: string | null;
setSearchQuery: (query: string) => void;
setSelectedTag: (tag: string | null) => void;
fetchClients: () => Promise<void>;
fetchClient: (id: string) => Promise<void>;
createClient: (data: ClientCreate) => Promise<Client>;
updateClient: (id: string, data: Partial<ClientCreate>) => Promise<void>;
deleteClient: (id: string) => Promise<void>;
markContacted: (id: string) => Promise<void>;
}
export const useClientsStore = create<ClientsState>()((set, get) => ({
clients: [],
selectedClient: null,
isLoading: false,
error: null,
searchQuery: '',
selectedTag: null,
setSearchQuery: (query) => set({ searchQuery: query }),
setSelectedTag: (tag) => set({ selectedTag: tag }),
fetchClients: async () => {
set({ isLoading: true, error: null });
try {
const { searchQuery, selectedTag } = get();
const clients = await api.getClients({
search: searchQuery || undefined,
tag: selectedTag || undefined,
});
set({ clients, isLoading: false });
} catch (err: any) {
set({ error: err.message, isLoading: false });
}
},
fetchClient: async (id) => {
set({ isLoading: true, error: null });
try {
const client = await api.getClient(id);
set({ selectedClient: client, isLoading: false });
} catch (err: any) {
set({ error: err.message, isLoading: false });
}
},
createClient: async (data) => {
const client = await api.createClient(data);
set((state) => ({ clients: [...state.clients, client] }));
return client;
},
updateClient: async (id, data) => {
const updated = await api.updateClient(id, data);
set((state) => ({
clients: state.clients.map((c) => (c.id === id ? updated : c)),
selectedClient: state.selectedClient?.id === id ? updated : state.selectedClient,
}));
},
deleteClient: async (id) => {
await api.deleteClient(id);
set((state) => ({
clients: state.clients.filter((c) => c.id !== id),
selectedClient: state.selectedClient?.id === id ? null : state.selectedClient,
}));
},
markContacted: async (id) => {
const updated = await api.markContacted(id);
set((state) => ({
clients: state.clients.map((c) => (c.id === id ? updated : c)),
selectedClient: state.selectedClient?.id === id ? updated : state.selectedClient,
}));
},
}));