From 9beee9902ee3b04d8c72a969ff8deb9cd1db1b5e Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Thu, 26 Jun 2025 18:28:44 +0300 Subject: [PATCH] order: add repeat button --- apps/web/components/orders/order-buttons.tsx | 9 +++ .../components/orders/order-form/index.tsx | 6 +- apps/web/hooks/url/index.ts | 1 + apps/web/hooks/url/parameters.ts | 35 ++++++++++ apps/web/stores/order/hooks.tsx | 67 ++++++++++++++----- 5 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 apps/web/hooks/url/index.ts create mode 100644 apps/web/hooks/url/parameters.ts diff --git a/apps/web/components/orders/order-buttons.tsx b/apps/web/components/orders/order-buttons.tsx index 9885e0c..61688cc 100644 --- a/apps/web/components/orders/order-buttons.tsx +++ b/apps/web/components/orders/order-buttons.tsx @@ -1,12 +1,16 @@ /* eslint-disable canonical/id-match */ 'use client'; + import FloatingActionPanel from '../shared/action-panel'; import { type OrderComponentProps } from './types'; import { useIsMaster } from '@/hooks/api/customers'; import { useOrderMutation, useOrderQuery } from '@/hooks/api/orders'; +import { usePushWithData } from '@/hooks/url'; import { Enum_Order_State } from '@repo/graphql/types'; export function OrderButtons({ documentId }: Readonly) { + const push = usePushWithData(); + const isMaster = useIsMaster(); const { data: { order } = {} } = useOrderQuery({ documentId }); @@ -34,6 +38,10 @@ export function OrderButtons({ documentId }: Readonly) { } } + function handleOnRepeat() { + push('/orders/add', order); + } + return ( ) { ? undefined : () => handleApprove() } + onRepeat={isCancelled || isCompleted ? handleOnRepeat : undefined} /> ); } diff --git a/apps/web/components/orders/order-form/index.tsx b/apps/web/components/orders/order-form/index.tsx index 943a28a..6bc7bbe 100644 --- a/apps/web/components/orders/order-form/index.tsx +++ b/apps/web/components/orders/order-form/index.tsx @@ -7,8 +7,10 @@ import { NextButton } from './next-button'; import { ErrorPage, SuccessPage } from './result'; import { ServiceSelect } from './service-select'; import { SubmitButton } from './submit-button'; +import { useGetUrlData } from '@/hooks/url'; import { OrderStoreProvider, useInitOrderStore, useOrderStore } from '@/stores/order'; import { withContext } from '@/utils/context'; +import { type OrderFieldsFragment } from '@repo/graphql/types'; import { LoadingSpinner } from '@repo/ui/components/ui/spinner'; import { type JSX } from 'react'; @@ -35,7 +37,9 @@ function getButtonComponent(step: string) { } export const OrderForm = withContext(OrderStoreProvider)(function () { - useInitOrderStore(); + const data = useGetUrlData(); + + useInitOrderStore(data); const step = useOrderStore((store) => store.step); if (step === 'loading') return ; diff --git a/apps/web/hooks/url/index.ts b/apps/web/hooks/url/index.ts new file mode 100644 index 0000000..f7be6e6 --- /dev/null +++ b/apps/web/hooks/url/index.ts @@ -0,0 +1 @@ +export * from './parameters'; diff --git a/apps/web/hooks/url/parameters.ts b/apps/web/hooks/url/parameters.ts new file mode 100644 index 0000000..e046d53 --- /dev/null +++ b/apps/web/hooks/url/parameters.ts @@ -0,0 +1,35 @@ +/* eslint-disable canonical/id-match */ +/* eslint-disable @typescript-eslint/naming-convention */ +'use client'; + +import { useRouter, useSearchParams } from 'next/navigation'; + +export function useGetUrlData(): null | T { + const searchParameters = useSearchParams(); + const data = searchParameters.get('_data_'); + + if (!data) return null; + + try { + const decoded = decodeURIComponent(atob(data)); + return JSON.parse(decoded) as T; + } catch (error) { + throw new Error('Failed to parse data parameters', { cause: error }); + } +} + +export function usePushWithData() { + const router = useRouter(); + const searchParameters = useSearchParams(); + + return (url: string, parameters: T) => { + try { + const base64 = btoa(encodeURIComponent(JSON.stringify(parameters))); + const parameters_ = new URLSearchParams(searchParameters); + parameters_.set('_data_', base64); + router.push(`${url}?${parameters_.toString()}`); + } catch (error) { + throw new Error('Failed to serialize data parameters', { cause: error }); + } + }; +} diff --git a/apps/web/stores/order/hooks.tsx b/apps/web/stores/order/hooks.tsx index 3e40a6d..07c054f 100644 --- a/apps/web/stores/order/hooks.tsx +++ b/apps/web/stores/order/hooks.tsx @@ -1,8 +1,10 @@ +/* eslint-disable sonarjs/cognitive-complexity */ 'use client'; import { useOrderStore } from './context'; import { type Steps } from './types'; import { useCustomerQuery, useIsMaster } from '@/hooks/api/customers'; -import { useEffect } from 'react'; +import { type OrderFieldsFragment } from '@repo/graphql/types'; +import { useCallback, useEffect, useRef } from 'react'; const STEPS: Steps[] = [ 'master-select', @@ -14,28 +16,63 @@ const STEPS: Steps[] = [ export const MASTER_STEPS: Steps[] = STEPS.filter((step) => step !== 'master-select'); export const CLIENT_STEPS: Steps[] = STEPS.filter((step) => step !== 'client-select'); -export function useInitOrderStore() { +export function useInitOrderStore(initData: null | OrderFieldsFragment) { + const initialized = useRef(false); + const { data: { customer } = {} } = useCustomerQuery(); + const isMaster = useIsMaster(); const setMasterId = useOrderStore((store) => store.setMasterId); const setClientId = useOrderStore((store) => store.setClientId); + const setServiceId = useOrderStore((store) => store.setServiceId); const setStep = useOrderStore((store) => store.setStep); const setStepsSequence = useOrderStore((store) => store._setStepSequence); - const isMaster = useIsMaster(); + const step = useOrderStore((store) => store.step); + + const setFirstStep = useCallback(() => { + const steps = isMaster ? MASTER_STEPS : CLIENT_STEPS; + setStepsSequence(steps); + setStep(steps[0] as Steps); + }, [isMaster, setStep, setStepsSequence]); useEffect(() => { - if (isMaster && customer) { - setMasterId(customer?.documentId); + if (initialized.current || !customer || step !== 'loading') return; + + if (initData) { + const masterId = initData.slot?.master?.documentId; + const clientId = initData.client?.documentId; + const serviceId = initData.services[0]?.documentId; + + if (masterId) setMasterId(masterId); + if (clientId) setClientId(clientId); + if (serviceId) setServiceId(serviceId); + + if (masterId && clientId && serviceId) { + setStep('datetime-select'); + } else { + setFirstStep(); + } + } else { + if (isMaster) { + setMasterId(customer.documentId); + } else { + setClientId(customer.documentId); + } + + setFirstStep(); } - if (!isMaster && customer) { - setClientId(customer?.documentId); - } - - const steps = isMaster ? MASTER_STEPS : CLIENT_STEPS; - const initialStep = steps[0] as Steps; - - setStepsSequence(steps); - setStep(initialStep); - }, [customer, isMaster, setClientId, setMasterId, setStep, setStepsSequence]); + initialized.current = true; + }, [ + customer, + initData, + isMaster, + setClientId, + setFirstStep, + setMasterId, + setServiceId, + setStep, + setStepsSequence, + step, + ]); }