2026-03-21 21:48:56 -05:00
|
|
|
import { create } from 'zustand';
|
|
|
|
|
import type { Rack, Module } from '../types';
|
|
|
|
|
import { apiClient } from '../api/client';
|
|
|
|
|
|
|
|
|
|
interface RackState {
|
|
|
|
|
racks: Rack[];
|
|
|
|
|
loading: boolean;
|
|
|
|
|
selectedModuleId: string | null;
|
|
|
|
|
// Fetch
|
|
|
|
|
fetchRacks: () => Promise<void>;
|
|
|
|
|
// Rack CRUD
|
|
|
|
|
addRack: (name: string, totalU?: number, location?: string) => Promise<Rack>;
|
|
|
|
|
updateRack: (id: string, data: Partial<{ name: string; totalU: number; location: string; displayOrder: number }>) => Promise<void>;
|
|
|
|
|
deleteRack: (id: string) => Promise<void>;
|
|
|
|
|
// Module CRUD (optimistic update helpers)
|
|
|
|
|
addModule: (rackId: string, data: Parameters<typeof apiClient.racks.addModule>[1]) => Promise<Module>;
|
2026-03-21 22:05:42 -05:00
|
|
|
moveModule: (moduleId: string, targetRackId: string, targetUPosition: number) => Promise<void>;
|
2026-03-21 21:48:56 -05:00
|
|
|
updateModuleLocal: (moduleId: string, data: Partial<Module>) => void;
|
|
|
|
|
removeModuleLocal: (moduleId: string) => void;
|
|
|
|
|
// Selection
|
|
|
|
|
setSelectedModule: (id: string | null) => void;
|
2026-03-22 14:55:33 -05:00
|
|
|
// Cabling
|
|
|
|
|
cablingFromPortId: string | null;
|
|
|
|
|
setCablingFromPortId: (id: string | null) => void;
|
|
|
|
|
createConnection: (fromPortId: string, toPortId: string) => Promise<void>;
|
2026-03-22 21:35:10 -05:00
|
|
|
updateConnection: (id: string, data: Partial<{ color: string; label: string; edgeType: string }>) => Promise<void>;
|
2026-03-22 14:55:33 -05:00
|
|
|
deleteConnection: (id: string) => Promise<void>;
|
2026-03-22 15:04:53 -05:00
|
|
|
// Port Config Global Modal
|
|
|
|
|
activeConfigPortId: string | null;
|
|
|
|
|
setActiveConfigPortId: (id: string | null) => void;
|
2026-03-22 21:35:10 -05:00
|
|
|
// Connection Config Global Modal
|
|
|
|
|
activeConfigConnectionId: string | null;
|
|
|
|
|
setActiveConfigConnectionId: (id: string | null) => void;
|
2026-03-21 21:48:56 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const useRackStore = create<RackState>((set, get) => ({
|
|
|
|
|
racks: [],
|
|
|
|
|
loading: false,
|
|
|
|
|
selectedModuleId: null,
|
|
|
|
|
|
|
|
|
|
fetchRacks: async () => {
|
|
|
|
|
set({ loading: true });
|
|
|
|
|
try {
|
|
|
|
|
const racks = await apiClient.racks.list();
|
|
|
|
|
set({ racks, loading: false });
|
|
|
|
|
} catch {
|
|
|
|
|
set({ loading: false });
|
|
|
|
|
throw new Error('Failed to load racks');
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
addRack: async (name, totalU = 42, location) => {
|
|
|
|
|
const rack = await apiClient.racks.create({ name, totalU, location });
|
|
|
|
|
set((s) => ({ racks: [...s.racks, rack].sort((a, b) => a.displayOrder - b.displayOrder) }));
|
|
|
|
|
return rack;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
updateRack: async (id, data) => {
|
|
|
|
|
const updated = await apiClient.racks.update(id, data);
|
|
|
|
|
set((s) => ({
|
|
|
|
|
racks: s.racks
|
|
|
|
|
.map((r) => (r.id === id ? updated : r))
|
|
|
|
|
.sort((a, b) => a.displayOrder - b.displayOrder),
|
|
|
|
|
}));
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
deleteRack: async (id) => {
|
|
|
|
|
await apiClient.racks.delete(id);
|
|
|
|
|
set((s) => ({ racks: s.racks.filter((r) => r.id !== id) }));
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
addModule: async (rackId, data) => {
|
|
|
|
|
const module = await apiClient.racks.addModule(rackId, data);
|
|
|
|
|
set((s) => ({
|
|
|
|
|
racks: s.racks.map((r) =>
|
|
|
|
|
r.id === rackId
|
|
|
|
|
? { ...r, modules: [...r.modules, module].sort((a, b) => a.uPosition - b.uPosition) }
|
|
|
|
|
: r
|
|
|
|
|
),
|
|
|
|
|
}));
|
|
|
|
|
return module;
|
|
|
|
|
},
|
|
|
|
|
|
2026-03-21 22:05:42 -05:00
|
|
|
moveModule: async (moduleId, targetRackId, targetUPosition) => {
|
|
|
|
|
const updated = await apiClient.modules.move(moduleId, targetRackId, targetUPosition);
|
|
|
|
|
set((s) => {
|
|
|
|
|
// Remove from source rack, insert into target rack
|
|
|
|
|
const racks = s.racks.map((r) => ({
|
|
|
|
|
...r,
|
|
|
|
|
modules: r.modules.filter((m) => m.id !== moduleId),
|
|
|
|
|
}));
|
|
|
|
|
return {
|
|
|
|
|
racks: racks.map((r) =>
|
|
|
|
|
r.id === targetRackId
|
|
|
|
|
? { ...r, modules: [...r.modules, updated].sort((a, b) => a.uPosition - b.uPosition) }
|
|
|
|
|
: r
|
|
|
|
|
),
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
2026-03-21 21:48:56 -05:00
|
|
|
updateModuleLocal: (moduleId, data) => {
|
|
|
|
|
set((s) => ({
|
|
|
|
|
racks: s.racks.map((r) => ({
|
|
|
|
|
...r,
|
|
|
|
|
modules: r.modules.map((m) => (m.id === moduleId ? { ...m, ...data } : m)),
|
|
|
|
|
})),
|
|
|
|
|
}));
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
removeModuleLocal: (moduleId) => {
|
|
|
|
|
set((s) => ({
|
|
|
|
|
racks: s.racks.map((r) => ({
|
|
|
|
|
...r,
|
|
|
|
|
modules: r.modules.filter((m) => m.id !== moduleId),
|
|
|
|
|
})),
|
|
|
|
|
}));
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
setSelectedModule: (id) => set({ selectedModuleId: id }),
|
2026-03-22 14:55:33 -05:00
|
|
|
|
|
|
|
|
// Cabling
|
|
|
|
|
cablingFromPortId: null,
|
|
|
|
|
setCablingFromPortId: (id) => set({ cablingFromPortId: id }),
|
|
|
|
|
|
|
|
|
|
createConnection: async (fromPortId, toPortId) => {
|
|
|
|
|
await apiClient.connections.create({ fromPortId, toPortId });
|
|
|
|
|
// Refresh racks to get updated nested connections
|
|
|
|
|
const racks = await apiClient.racks.list();
|
|
|
|
|
set({ racks });
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
deleteConnection: async (id) => {
|
|
|
|
|
await apiClient.connections.delete(id);
|
|
|
|
|
const racks = await apiClient.racks.list();
|
|
|
|
|
set({ racks });
|
|
|
|
|
},
|
2026-03-22 15:04:53 -05:00
|
|
|
|
2026-03-22 21:35:10 -05:00
|
|
|
updateConnection: async (id, data) => {
|
|
|
|
|
await apiClient.connections.update(id, data);
|
|
|
|
|
const racks = await apiClient.racks.list();
|
|
|
|
|
set({ racks });
|
|
|
|
|
},
|
|
|
|
|
|
2026-03-22 15:04:53 -05:00
|
|
|
activeConfigPortId: null,
|
|
|
|
|
setActiveConfigPortId: (id) => set({ activeConfigPortId: id }),
|
2026-03-22 21:35:10 -05:00
|
|
|
|
|
|
|
|
activeConfigConnectionId: null,
|
|
|
|
|
setActiveConfigConnectionId: (id) => set({ activeConfigConnectionId: id }),
|
2026-03-21 21:48:56 -05:00
|
|
|
}));
|