mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-28 09:33:46 +00:00
Со времён редизайна со скоуп-селектором граф строится только для выбранных кампаний, партнёров или пользователей, и посмотреть сеть целиком стало нечем. Бэкенд для этого уже есть — GET /cabinet/admin/ referral-network/ отдаёт полный граф и остался неиспользуемым после того, как фронт перешёл на /scoped. Возвращаем к нему доступ: в дропдауне селектора появляется закреплённый пункт «Вся сеть», который грузит граф целиком. Режим взаимоисключающий с выбором чипов — полный граф и так содержит все сущности.
97 lines
2.7 KiB
TypeScript
97 lines
2.7 KiB
TypeScript
import { create } from 'zustand';
|
|
import type { SelectedNode, NetworkFilters, ScopeSelection } from '@/types/referralNetwork';
|
|
|
|
export const MAX_SCOPE_ITEMS = 50;
|
|
|
|
const DEFAULT_FILTERS: NetworkFilters = {
|
|
campaigns: [],
|
|
partnersOnly: false,
|
|
minReferrals: 0,
|
|
};
|
|
|
|
function resetGraphState() {
|
|
return {
|
|
selectedNode: null as SelectedNode,
|
|
hoveredNodeId: null as string | null,
|
|
highlightedNodes: new Set<string>(),
|
|
filters: { ...DEFAULT_FILTERS },
|
|
};
|
|
}
|
|
|
|
interface ReferralNetworkState {
|
|
selectedNode: SelectedNode;
|
|
hoveredNodeId: string | null;
|
|
highlightedNodes: Set<string>;
|
|
filters: NetworkFilters;
|
|
scope: ScopeSelection[];
|
|
isFullNetwork: boolean;
|
|
|
|
setSelectedNode: (node: SelectedNode) => void;
|
|
setHoveredNode: (id: string | null) => void;
|
|
setHighlightedNodes: (nodes: Set<string>) => void;
|
|
updateFilters: (filters: Partial<NetworkFilters>) => void;
|
|
resetFilters: () => void;
|
|
setFullNetwork: (enabled: boolean) => void;
|
|
addScope: (item: ScopeSelection) => void;
|
|
removeScope: (type: ScopeSelection['type'], id: number) => void;
|
|
clearScope: () => void;
|
|
}
|
|
|
|
export const useReferralNetworkStore = create<ReferralNetworkState>()((set) => ({
|
|
selectedNode: null,
|
|
hoveredNodeId: null,
|
|
highlightedNodes: new Set<string>(),
|
|
filters: { ...DEFAULT_FILTERS },
|
|
scope: [],
|
|
isFullNetwork: false,
|
|
|
|
setSelectedNode: (node) => set({ selectedNode: node }),
|
|
setHoveredNode: (id) => set({ hoveredNodeId: id }),
|
|
setHighlightedNodes: (nodes) => set({ highlightedNodes: nodes }),
|
|
|
|
updateFilters: (partial) =>
|
|
set((state) => ({
|
|
filters: { ...state.filters, ...partial },
|
|
})),
|
|
|
|
resetFilters: () => set({ filters: { ...DEFAULT_FILTERS } }),
|
|
|
|
// Full network and a scope selection are mutually exclusive: the whole graph
|
|
// already contains every campaign, partner and user, so narrowing it by chips
|
|
// would be meaningless. Each mode therefore drops the other.
|
|
setFullNetwork: (enabled) =>
|
|
set({
|
|
isFullNetwork: enabled,
|
|
scope: [],
|
|
...resetGraphState(),
|
|
}),
|
|
|
|
addScope: (item) =>
|
|
set((state) => {
|
|
const exists = state.scope.some((s) => s.type === item.type && s.id === item.id);
|
|
if (exists) return state;
|
|
if (state.scope.length >= MAX_SCOPE_ITEMS) return state;
|
|
return {
|
|
scope: [...state.scope, item],
|
|
isFullNetwork: false,
|
|
...resetGraphState(),
|
|
};
|
|
}),
|
|
|
|
removeScope: (type, id) =>
|
|
set((state) => {
|
|
const next = state.scope.filter((s) => !(s.type === type && s.id === id));
|
|
if (next.length === 0) {
|
|
return { scope: next, ...resetGraphState() };
|
|
}
|
|
return { scope: next };
|
|
}),
|
|
|
|
clearScope: () =>
|
|
set({
|
|
scope: [],
|
|
isFullNetwork: false,
|
|
...resetGraphState(),
|
|
}),
|
|
}));
|