stores/order: split into slices

This commit is contained in:
vchikalkin 2025-05-23 13:14:26 +03:00
parent d8f853180b
commit 4160ed4540
13 changed files with 172 additions and 90 deletions

View File

@ -0,0 +1,11 @@
import { type StateCreator } from 'zustand';
export type ClientSlice = {
clientId: null | string;
setClientId: (id: null | string) => void;
};
export const createClientSlice: StateCreator<ClientSlice> = (set) => ({
clientId: null,
setClientId: (id) => set({ clientId: id }),
});

View File

@ -0,0 +1,15 @@
import { type StateCreator } from 'zustand';
export type DateTimeSlice = {
date: Date;
setDate: (date: Date) => void;
setTime: (time: null | string) => void;
time: null | string;
};
export const createDateTimeSlice: StateCreator<DateTimeSlice> = (set) => ({
date: new Date(),
setDate: (date) => set({ date }),
setTime: (time) => set({ time }),
time: null,
});

View File

@ -0,0 +1,6 @@
export * from './client-slice';
export * from './datetime-slice';
export * from './master-slice';
export * from './service-slice';
export * from './slot-slice';
export * from './steps-slice';

View File

@ -0,0 +1,11 @@
import { type StateCreator } from 'zustand';
export type MasterSlice = {
masterId: null | string;
setMasterId: (id: null | string) => void;
};
export const createMasterSlice: StateCreator<MasterSlice> = (set) => ({
masterId: null,
setMasterId: (id) => set({ masterId: id }),
});

View File

@ -0,0 +1,11 @@
import { type StateCreator } from 'zustand';
export type ServiceSlice = {
serviceId: null | string;
setServiceId: (id: null | string) => void;
};
export const createServiceSlice: StateCreator<ServiceSlice> = (set) => ({
serviceId: null,
setServiceId: (id) => set({ serviceId: id }),
});

View File

@ -0,0 +1,11 @@
import { type StateCreator } from 'zustand';
export type SlotSlice = {
setSlotId: (slot: null | string) => void;
slotId: null | string;
};
export const createSlotSlice: StateCreator<SlotSlice> = (set) => ({
setSlotId: (slot) => set({ slotId: slot }),
slotId: null,
});

View File

@ -0,0 +1,41 @@
/* eslint-disable canonical/id-match */
/* eslint-disable @typescript-eslint/naming-convention */
import { type StateCreator } from 'zustand';
export type Steps =
| 'client-select'
| 'datetime-select'
| 'error'
| 'loading'
| 'master-select'
| 'service-select'
| 'success';
export type StepsSlice = {
_setStepSequence: (steps: Steps[]) => void;
_stepSequence: Steps[];
nextStep: () => void;
prevStep: () => void;
setStep: (step: Steps) => void;
step: Steps;
};
export const createStepsSlice: StateCreator<StepsSlice> = (set, get) => ({
_setStepSequence: (steps) => set({ _stepSequence: steps }),
_stepSequence: [],
nextStep: () => {
const { _stepSequence, step } = get();
const index = _stepSequence.indexOf(step);
const next = _stepSequence[index + 1];
if (next) set({ step: next });
},
prevStep: () => {
const { _stepSequence, step } = get();
const index = _stepSequence.indexOf(step);
const previous = _stepSequence[index - 1];
if (previous) set({ step: previous });
},
setStep: (step) => set({ step }),
step: 'loading',
});

View File

@ -1,17 +1,7 @@
'use client';
import { createOrderStore } from './store';
import { createContext, type PropsWithChildren, useRef } from 'react';
import { createZustandStore } from '@/utils/zustand/context';
export type OrderStoreApi = ReturnType<typeof createOrderStore>;
const { Provider, useZustandStore } = createZustandStore(createOrderStore);
export const OrderStoreContext = createContext<OrderStoreApi | undefined>(undefined);
export function OrderStoreProvider({ children }: Readonly<PropsWithChildren>) {
const storeRef = useRef<null | OrderStoreApi>(null);
if (storeRef.current === null) {
storeRef.current = createOrderStore();
}
return <OrderStoreContext value={storeRef.current}>{children}</OrderStoreContext>;
}
export { Provider as OrderStoreProvider, useZustandStore as useOrderStore };

View File

@ -1,22 +1,10 @@
/* eslint-disable canonical/id-match */
'use client';
import { OrderStoreContext } from './context';
import { type OrderStore, type Steps } from './types';
import { useOrderStore } from './context';
import { type Steps } from './types';
import { useCustomerQuery } from '@/hooks/api/customers';
import { Enum_Customer_Role } from '@repo/graphql/types';
import { useContext, useEffect } from 'react';
import { useStore } from 'zustand';
export const useOrderStore = <T,>(selector: (store: OrderStore) => T): T => {
const orderStoreContext = useContext(OrderStoreContext);
if (!orderStoreContext) {
throw new Error(`useOrderStore must be used within OrderStoreProvider`);
}
return useStore(orderStoreContext, selector);
};
import { useEffect } from 'react';
const STEPS: Steps[] = [
'master-select',

View File

@ -1,2 +1 @@
export * from './context';
export { useOrderStore } from './hooks';

View File

@ -1,39 +1,21 @@
/* eslint-disable @typescript-eslint/naming-convention */
/* eslint-disable canonical/id-match */
import {
createClientSlice,
createDateTimeSlice,
createMasterSlice,
createServiceSlice,
createSlotSlice,
createStepsSlice,
} from '../lib/slices';
import { type OrderStore } from './types';
import { createStore } from 'zustand';
export function createOrderStore() {
return createStore<OrderStore>((set, get) => ({
_setStepSequence: (steps) => set({ _stepSequence: steps }),
_stepSequence: [],
clientId: null,
date: new Date(),
masterId: null,
nextStep: () => {
const { _stepSequence, step } = get();
const index = _stepSequence.indexOf(step);
const next = _stepSequence[index + 1];
if (next) set({ step: next });
},
prevStep: () => {
const { _stepSequence, step } = get();
const index = _stepSequence.indexOf(step);
const previous = _stepSequence[index - 1];
if (previous) set({ step: previous });
},
serviceId: null,
setClientId: (id) => set({ clientId: id }),
setDate: (date) => set({ date }),
setMasterId: (id) => set({ masterId: id }),
setServiceId: (id) => set({ serviceId: id }),
setSlotId: (id) => set({ slotId: id }),
setStep: (step) => set({ step }),
setTime: (time) => set({ time }),
slotId: null,
step: 'loading',
time: null,
return createStore<OrderStore>((...args) => ({
...createClientSlice(...args),
...createDateTimeSlice(...args),
...createMasterSlice(...args),
...createServiceSlice(...args),
...createSlotSlice(...args),
...createStepsSlice(...args),
}));
}

View File

@ -1,29 +1,17 @@
export type OrderStore = {
_setStepSequence: (steps: Steps[]) => void;
_stepSequence: Steps[];
clientId: null | string;
date: Date;
masterId: null | string;
nextStep: () => void;
prevStep: () => void;
serviceId: null | string;
setClientId: (id: null | string) => void;
setDate: (date: Date) => void;
setMasterId: (id: null | string) => void;
setServiceId: (id: null | string) => void;
setSlotId: (slot: null | string) => void;
setStep: (step: Steps) => void;
setTime: (time: null | string) => void;
slotId: null | string;
step: Steps;
time: null | string;
};
import {
type ClientSlice,
type DateTimeSlice,
type MasterSlice,
type ServiceSlice,
type SlotSlice,
type StepsSlice,
} from '../lib/slices';
export type Steps =
| 'client-select'
| 'datetime-select'
| 'error'
| 'loading'
| 'master-select'
| 'service-select'
| 'success';
export type OrderStore = ClientSlice &
DateTimeSlice &
MasterSlice &
ServiceSlice &
SlotSlice &
StepsSlice;
export { type Steps } from '../lib/slices';

View File

@ -0,0 +1,29 @@
import { createContext, type PropsWithChildren, useContext, useRef } from 'react';
import { type StoreApi, useStore } from 'zustand';
export function createZustandStore<S>(createStore: () => StoreApi<S>) {
type ZustandStoreApi = StoreApi<S>;
const StoreContext = createContext<undefined | ZustandStoreApi>(undefined);
function Provider({ children }: Readonly<PropsWithChildren>) {
const storeRef = useRef<null | ZustandStoreApi>(null);
if (storeRef.current === null) {
storeRef.current = createStore();
}
return <StoreContext value={storeRef.current}>{children}</StoreContext>;
}
const useZustandStore = <T,>(selector: (store: S) => T): T => {
const orderStoreContext = useContext(StoreContext);
if (!orderStoreContext) {
throw new Error(`useZustandStore must be used within OrderStoreProvider`);
}
return useStore(orderStoreContext, selector);
};
return { Provider, useZustandStore };
}