Implements computeImpactDeltas pure function with 8 TDD tests covering replace/add/none modes and null weight/price handling. Adds useImpactDeltas hook, categoryId to ThreadWithCandidates, and selectedSetupId state to uiStore. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
123 lines
3.8 KiB
TypeScript
123 lines
3.8 KiB
TypeScript
import { create } from "zustand";
|
|
|
|
interface UIState {
|
|
// Item panel state
|
|
panelMode: "closed" | "add" | "edit";
|
|
editingItemId: number | null;
|
|
confirmDeleteItemId: number | null;
|
|
|
|
openAddPanel: () => void;
|
|
openEditPanel: (itemId: number) => void;
|
|
closePanel: () => void;
|
|
openConfirmDelete: (itemId: number) => void;
|
|
closeConfirmDelete: () => void;
|
|
|
|
// Candidate panel state
|
|
candidatePanelMode: "closed" | "add" | "edit";
|
|
editingCandidateId: number | null;
|
|
confirmDeleteCandidateId: number | null;
|
|
|
|
openCandidateAddPanel: () => void;
|
|
openCandidateEditPanel: (id: number) => void;
|
|
closeCandidatePanel: () => void;
|
|
openConfirmDeleteCandidate: (id: number) => void;
|
|
closeConfirmDeleteCandidate: () => void;
|
|
|
|
// Resolution dialog state
|
|
resolveThreadId: number | null;
|
|
resolveCandidateId: number | null;
|
|
|
|
openResolveDialog: (threadId: number, candidateId: number) => void;
|
|
closeResolveDialog: () => void;
|
|
|
|
// Setup-related UI state
|
|
itemPickerOpen: boolean;
|
|
openItemPicker: () => void;
|
|
closeItemPicker: () => void;
|
|
|
|
confirmDeleteSetupId: number | null;
|
|
openConfirmDeleteSetup: (id: number) => void;
|
|
closeConfirmDeleteSetup: () => void;
|
|
|
|
// Create thread modal
|
|
createThreadModalOpen: boolean;
|
|
openCreateThreadModal: () => void;
|
|
closeCreateThreadModal: () => void;
|
|
|
|
// External link dialog
|
|
externalLinkUrl: string | null;
|
|
openExternalLink: (url: string) => void;
|
|
closeExternalLink: () => void;
|
|
|
|
// Candidate view mode
|
|
candidateViewMode: "list" | "grid" | "compare";
|
|
setCandidateViewMode: (mode: "list" | "grid" | "compare") => void;
|
|
|
|
// Setup impact preview
|
|
selectedSetupId: number | null;
|
|
setSelectedSetupId: (id: number | null) => void;
|
|
}
|
|
|
|
export const useUIStore = create<UIState>((set) => ({
|
|
// Item panel
|
|
panelMode: "closed",
|
|
editingItemId: null,
|
|
confirmDeleteItemId: null,
|
|
|
|
openAddPanel: () => set({ panelMode: "add", editingItemId: null }),
|
|
openEditPanel: (itemId) => set({ panelMode: "edit", editingItemId: itemId }),
|
|
closePanel: () => set({ panelMode: "closed", editingItemId: null }),
|
|
openConfirmDelete: (itemId) => set({ confirmDeleteItemId: itemId }),
|
|
closeConfirmDelete: () => set({ confirmDeleteItemId: null }),
|
|
|
|
// Candidate panel
|
|
candidatePanelMode: "closed",
|
|
editingCandidateId: null,
|
|
confirmDeleteCandidateId: null,
|
|
|
|
openCandidateAddPanel: () =>
|
|
set({ candidatePanelMode: "add", editingCandidateId: null }),
|
|
openCandidateEditPanel: (id) =>
|
|
set({ candidatePanelMode: "edit", editingCandidateId: id }),
|
|
closeCandidatePanel: () =>
|
|
set({ candidatePanelMode: "closed", editingCandidateId: null }),
|
|
openConfirmDeleteCandidate: (id) => set({ confirmDeleteCandidateId: id }),
|
|
closeConfirmDeleteCandidate: () => set({ confirmDeleteCandidateId: null }),
|
|
|
|
// Resolution dialog
|
|
resolveThreadId: null,
|
|
resolveCandidateId: null,
|
|
|
|
openResolveDialog: (threadId, candidateId) =>
|
|
set({ resolveThreadId: threadId, resolveCandidateId: candidateId }),
|
|
closeResolveDialog: () =>
|
|
set({ resolveThreadId: null, resolveCandidateId: null }),
|
|
|
|
// Setup-related UI state
|
|
itemPickerOpen: false,
|
|
openItemPicker: () => set({ itemPickerOpen: true }),
|
|
closeItemPicker: () => set({ itemPickerOpen: false }),
|
|
|
|
confirmDeleteSetupId: null,
|
|
openConfirmDeleteSetup: (id) => set({ confirmDeleteSetupId: id }),
|
|
closeConfirmDeleteSetup: () => set({ confirmDeleteSetupId: null }),
|
|
|
|
// Create thread modal
|
|
createThreadModalOpen: false,
|
|
openCreateThreadModal: () => set({ createThreadModalOpen: true }),
|
|
closeCreateThreadModal: () => set({ createThreadModalOpen: false }),
|
|
|
|
// External link dialog
|
|
externalLinkUrl: null,
|
|
openExternalLink: (url) => set({ externalLinkUrl: url }),
|
|
closeExternalLink: () => set({ externalLinkUrl: null }),
|
|
|
|
// Candidate view mode
|
|
candidateViewMode: "list",
|
|
setCandidateViewMode: (mode) => set({ candidateViewMode: mode }),
|
|
|
|
// Setup impact preview
|
|
selectedSetupId: null,
|
|
setSelectedSetupId: (id) => set({ selectedSetupId: id }),
|
|
}));
|