From fc94f373e9b82476359f1e4e0199cf4ce73e12ec Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Fri, 16 May 2025 12:55:50 +0300 Subject: [PATCH 01/12] refactor customer api --- apps/web/actions/api/customers.ts | 50 +++++++ apps/web/actions/api/lib/service.ts | 22 +++ apps/web/actions/contacts.ts | 26 ---- apps/web/actions/orders.ts | 14 +- apps/web/actions/profile.ts | 37 ----- apps/web/actions/services.ts | 8 +- apps/web/actions/session.ts | 12 ++ apps/web/actions/slots.ts | 13 +- apps/web/components/auth/update-profile.tsx | 10 +- .../web/components/contacts/contacts-list.tsx | 2 +- .../orders/components/contacts-grid/index.tsx | 4 +- apps/web/components/profile/data-card.tsx | 16 +- apps/web/components/profile/links-card.tsx | 4 +- apps/web/components/profile/person-card.tsx | 4 +- apps/web/hooks/{ => api}/contacts/index.ts | 0 apps/web/hooks/api/contacts/query.ts | 25 ++++ .../contacts/use-customer-contacts.ts | 20 +-- apps/web/hooks/api/customers.ts | 24 +++ apps/web/hooks/contacts/query.ts | 8 - apps/web/hooks/profile/index.ts | 23 --- apps/web/stores/order/hooks.tsx | 16 +- packages/graphql/api/base.ts | 15 ++ packages/graphql/api/customer.ts | 108 -------------- packages/graphql/api/customers.ts | 140 ++++++++++++++++++ packages/graphql/api/index.ts | 4 +- packages/graphql/eslint.config.js | 2 +- packages/graphql/operations/customer.graphql | 2 +- packages/graphql/package.json | 8 +- .../graphql/types/operations.generated.ts | 6 +- packages/typescript-config/base.json | 4 +- pnpm-lock.yaml | 3 + 31 files changed, 361 insertions(+), 269 deletions(-) create mode 100644 apps/web/actions/api/customers.ts create mode 100644 apps/web/actions/api/lib/service.ts delete mode 100644 apps/web/actions/contacts.ts delete mode 100644 apps/web/actions/profile.ts create mode 100644 apps/web/actions/session.ts rename apps/web/hooks/{ => api}/contacts/index.ts (100%) create mode 100644 apps/web/hooks/api/contacts/query.ts rename apps/web/hooks/{ => api}/contacts/use-customer-contacts.ts (62%) create mode 100644 apps/web/hooks/api/customers.ts delete mode 100644 apps/web/hooks/contacts/query.ts delete mode 100644 apps/web/hooks/profile/index.ts create mode 100644 packages/graphql/api/base.ts delete mode 100644 packages/graphql/api/customer.ts create mode 100644 packages/graphql/api/customers.ts diff --git a/apps/web/actions/api/customers.ts b/apps/web/actions/api/customers.ts new file mode 100644 index 0000000..85e3a1c --- /dev/null +++ b/apps/web/actions/api/customers.ts @@ -0,0 +1,50 @@ +'use server'; + +import { useService } from './lib/service'; +import { CustomersService } from '@repo/graphql/api/customers'; + +const getService = useService(CustomersService); + +export async function createOrUpdateCustomer( + ...variables: Parameters +) { + const service = await getService(); + + return service.createOrUpdateCustomer(...variables); +} + +export async function getCustomer(...variables: Parameters) { + const service = await getService(); + + return service.getCustomer(...variables); +} + +export async function getCustomerClients( + ...variables: Parameters +) { + const service = await getService(); + + return service.getCustomerClients(...variables); +} + +export async function getCustomerMasters( + ...variables: Parameters +) { + const service = await getService(); + + return service.getCustomerMasters(...variables); +} + +export async function updateCustomer(...variables: Parameters) { + const service = await getService(); + + return service.updateCustomer(...variables); +} + +export async function updateCustomerMaster( + ...variables: Parameters +) { + const service = await getService(); + + return service.updateCustomerMaster(...variables); +} diff --git a/apps/web/actions/api/lib/service.ts b/apps/web/actions/api/lib/service.ts new file mode 100644 index 0000000..9f99685 --- /dev/null +++ b/apps/web/actions/api/lib/service.ts @@ -0,0 +1,22 @@ +/* eslint-disable canonical/id-match */ +import { authOptions } from '@/config/auth'; +import { type BaseService } from '@repo/graphql/api/base'; +import { getServerSession } from 'next-auth'; + +export async function _temporaryGetCustomer() { + const session = await getServerSession(authOptions); + if (!session?.user?.telegramId) throw new Error('Unauthorized'); + + return { telegramId: session.user.telegramId }; +} + +export function useService(service: T) { + return async function () { + const session = await getServerSession(authOptions); + if (!session?.user?.telegramId) throw new Error('Unauthorized'); + + const customer = { telegramId: session.user.telegramId }; + + return new service(customer) as InstanceType; + }; +} diff --git a/apps/web/actions/contacts.ts b/apps/web/actions/contacts.ts deleted file mode 100644 index 3b1bcf4..0000000 --- a/apps/web/actions/contacts.ts +++ /dev/null @@ -1,26 +0,0 @@ -'use server'; -import { authOptions } from '@/config/auth'; -import { getCustomerClients, getCustomerMasters } from '@repo/graphql/api'; -import { getServerSession } from 'next-auth/next'; - -export async function getClients() { - const session = await getServerSession(authOptions); - if (!session) throw new Error('Missing session'); - - const { user } = session; - - const response = await getCustomerClients({ telegramId: user?.telegramId }); - - return response.data?.customers?.at(0); -} - -export async function getMasters() { - const session = await getServerSession(authOptions); - if (!session) throw new Error('Missing session'); - - const { user } = session; - - const response = await getCustomerMasters({ telegramId: user?.telegramId }); - - return response.data?.customers?.at(0); -} diff --git a/apps/web/actions/orders.ts b/apps/web/actions/orders.ts index 600ff09..70d0112 100644 --- a/apps/web/actions/orders.ts +++ b/apps/web/actions/orders.ts @@ -2,7 +2,8 @@ 'use server'; // eslint-disable-next-line sonarjs/no-internal-api-use import type * as ApolloTypes from '../../../packages/graphql/node_modules/@apollo/client/core'; -import { getProfile } from './profile'; +import { getCustomer, getCustomerMasters } from './api/customers'; +import { _temporaryGetCustomer } from './api/lib/service'; import { formatDate, formatTime, sumTime } from '@/utils/date'; import * as api from '@repo/graphql/api'; import { Enum_Customer_Role, Enum_Slot_State } from '@repo/graphql/types'; @@ -21,7 +22,12 @@ type OrderInput = { }; export async function createOrder(input: OrderInput) { - const customer = await getProfile(); + const variables = await _temporaryGetCustomer(); + const { customer } = await getCustomer(variables); + + if (!customer) { + throw new Error('Missing customer'); + } if (!input.slotId) { throw new Error('Missing slot'); @@ -40,9 +46,9 @@ export async function createOrder(input: OrderInput) { const masterId = data.slot?.master?.documentId; - const masters = await api.getCustomerMasters(customer); + const masters = await getCustomerMasters(customer); - if (!masters.data.customers.some((master) => master?.documentId === masterId)) { + if (!masters.customers.some((master) => master?.documentId === masterId)) { throw new Error('Invalid master'); } } diff --git a/apps/web/actions/profile.ts b/apps/web/actions/profile.ts deleted file mode 100644 index 021ec7a..0000000 --- a/apps/web/actions/profile.ts +++ /dev/null @@ -1,37 +0,0 @@ -'use server'; -import { authOptions } from '@/config/auth'; -import { getCustomer, updateCustomerProfile } from '@repo/graphql/api'; -import { type CustomerInput, type GetCustomerQueryVariables } from '@repo/graphql/types'; -import { getServerSession } from 'next-auth/next'; - -export async function getProfile(input?: GetCustomerQueryVariables) { - const session = await getServerSession(authOptions); - if (!session) throw new Error('Missing session'); - - const { user } = session; - const telegramId = input?.telegramId || user?.telegramId; - - const { data } = await getCustomer({ telegramId }); - const customer = data?.customers?.at(0); - - if (!customer) throw new Error('Customer not found'); - - return customer; -} - -export async function updateProfile(input: CustomerInput) { - const session = await getServerSession(authOptions); - if (!session) throw new Error('Missing session'); - - const { user } = session; - - const { data } = await getCustomer({ telegramId: user?.telegramId }); - const customer = data.customers.at(0); - - if (!customer) throw new Error('Customer not found'); - - await updateCustomerProfile({ - data: input, - documentId: customer?.documentId, - }); -} diff --git a/apps/web/actions/services.ts b/apps/web/actions/services.ts index fa19552..6cec874 100644 --- a/apps/web/actions/services.ts +++ b/apps/web/actions/services.ts @@ -1,5 +1,6 @@ 'use server'; -import { getProfile } from './profile'; +import { getCustomer } from './api/customers'; +import { _temporaryGetCustomer } from './api/lib/service'; import * as api from '@repo/graphql/api/service'; // eslint-disable-next-line sonarjs/no-internal-api-use import type * as ApolloTypes from '@repo/graphql/node_modules/@apollo/client/core'; @@ -9,9 +10,10 @@ import { type GetServicesQueryVariables } from '@repo/graphql/types'; type FixTypescriptCringe = ApolloTypes.FetchResult; export async function getServices(input?: GetServicesQueryVariables) { - const customer = await getProfile(); + const variables = await _temporaryGetCustomer(); + const { customer } = await getCustomer(variables); - const filters = input || { filters: { master: { documentId: { eq: customer.documentId } } } }; + const filters = input || { filters: { master: { documentId: { eq: customer?.documentId } } } }; return api.getServices(filters); } diff --git a/apps/web/actions/session.ts b/apps/web/actions/session.ts new file mode 100644 index 0000000..7ef1c33 --- /dev/null +++ b/apps/web/actions/session.ts @@ -0,0 +1,12 @@ +'use server'; +import { authOptions } from '@/config/auth'; +import { getServerSession } from 'next-auth/next'; + +export async function getSessionUser() { + const session = await getServerSession(authOptions); + const user = session?.user; + + if (!user?.telegramId) throw new Error('Missing session'); + + return user; +} diff --git a/apps/web/actions/slots.ts b/apps/web/actions/slots.ts index 19d7dff..ca5bae0 100644 --- a/apps/web/actions/slots.ts +++ b/apps/web/actions/slots.ts @@ -1,7 +1,8 @@ 'use server'; // eslint-disable-next-line sonarjs/no-internal-api-use import type * as ApolloTypes from '../../../packages/graphql/node_modules/@apollo/client/core'; -import { getProfile } from './profile'; +import { getCustomer } from './api/customers'; +import { _temporaryGetCustomer } from './api/lib/service'; import { formatDate, formatTime } from '@/utils/date'; import * as api from '@repo/graphql/api'; import type * as GQL from '@repo/graphql/types'; @@ -12,7 +13,8 @@ type AddSlotInput = Omit; type FixTypescriptCringe = ApolloTypes.FetchResult; export async function addSlot(input: AddSlotInput) { - const customer = await getProfile(); + const variables = await _temporaryGetCustomer(); + const { customer } = await getCustomer(variables); return api.createSlot({ ...input, @@ -24,13 +26,14 @@ export async function addSlot(input: AddSlotInput) { } export async function getSlots(input: GQL.GetSlotsQueryVariables) { - const customer = await getProfile(); + const variables = await _temporaryGetCustomer(); + const { customer } = await getCustomer(variables); return api.getSlots({ filters: { master: { documentId: { - eq: customer.documentId, + eq: customer?.documentId, }, }, ...input.filters, @@ -39,8 +42,6 @@ export async function getSlots(input: GQL.GetSlotsQueryVariables) { } export async function updateSlot(input: GQL.UpdateSlotMutationVariables) { - await getProfile(); - return api.updateSlot({ ...input, data: { diff --git a/apps/web/components/auth/update-profile.tsx b/apps/web/components/auth/update-profile.tsx index fae567e..2c110ca 100644 --- a/apps/web/components/auth/update-profile.tsx +++ b/apps/web/components/auth/update-profile.tsx @@ -1,18 +1,20 @@ 'use client'; -import { useProfileMutation } from '@/hooks/profile'; +import { useCustomerMutation } from '@/hooks/api/customers'; import { initData, useSignal } from '@telegram-apps/sdk-react'; import { useEffect, useState } from 'react'; export function UpdateProfile() { const initDataUser = useSignal(initData.user); - const { mutate: updateProfile } = useProfileMutation(); + const { mutate: updateProfile } = useCustomerMutation(); const [hasUpdated, setHasUpdated] = useState(false); useEffect(() => { if (!hasUpdated) { updateProfile({ - active: true, - photoUrl: initDataUser?.photoUrl || undefined, + data: { + active: true, + photoUrl: initDataUser?.photoUrl || undefined, + }, }); setHasUpdated(true); } diff --git a/apps/web/components/contacts/contacts-list.tsx b/apps/web/components/contacts/contacts-list.tsx index 3db173d..2bba376 100644 --- a/apps/web/components/contacts/contacts-list.tsx +++ b/apps/web/components/contacts/contacts-list.tsx @@ -1,6 +1,6 @@ 'use client'; import { LoadingSpinner } from '../common/spinner'; -import { useCustomerContacts } from '@/hooks/contacts'; +import { useCustomerContacts } from '@/hooks/api/contacts'; import * as GQL from '@repo/graphql/types'; import { Avatar, AvatarFallback, AvatarImage } from '@repo/ui/components/ui/avatar'; import Link from 'next/link'; diff --git a/apps/web/components/orders/components/contacts-grid/index.tsx b/apps/web/components/orders/components/contacts-grid/index.tsx index ec0612b..0b9eddc 100644 --- a/apps/web/components/orders/components/contacts-grid/index.tsx +++ b/apps/web/components/orders/components/contacts-grid/index.tsx @@ -2,13 +2,13 @@ import { ContactsGridBase } from './components'; import { LoadingSpinner } from '@/components/common/spinner'; import { ContactsFilterProvider } from '@/context/contacts-filter'; -import { useCustomerContacts } from '@/hooks/contacts'; +import { useCustomerContacts } from '@/hooks/api/contacts'; import { useOrderStore } from '@/stores/order'; import { withContext } from '@/utils/context'; import { useEffect } from 'react'; export const MastersGrid = withContext(ContactsFilterProvider)(function () { - const { contacts, isLoading, setFilter } = useCustomerContacts({ includeSelf: true }); + const { contacts, isLoading, setFilter } = useCustomerContacts(); const masterId = useOrderStore((store) => store.masterId); const setMasterId = useOrderStore((store) => store.setMasterId); diff --git a/apps/web/components/profile/data-card.tsx b/apps/web/components/profile/data-card.tsx index 739f36d..afa5e0d 100644 --- a/apps/web/components/profile/data-card.tsx +++ b/apps/web/components/profile/data-card.tsx @@ -2,14 +2,14 @@ import { CardSectionHeader } from '../ui'; import { CheckboxWithText, DataField } from './components'; import { type ProfileProps } from './types'; -import { useProfileMutation, useProfileQuery } from '@/hooks/profile'; +import { useCustomerMutation, useCustomerQuery } from '@/hooks/api/customers'; import { Enum_Customer_Role as Role } from '@repo/graphql/types'; import { Button } from '@repo/ui/components/ui/button'; import { Card } from '@repo/ui/components/ui/card'; import Link from 'next/link'; export function ContactDataCard({ telegramId }: Readonly) { - const { data: customer } = useProfileQuery({ telegramId }); + const { data: { customer } = {} } = useCustomerQuery({ telegramId }); if (!customer) return null; @@ -30,8 +30,8 @@ export function ContactDataCard({ telegramId }: Readonly) { } export function ProfileDataCard() { - const { data: customer } = useProfileQuery(); - const { mutate: updateProfile } = useProfileMutation(); + const { data: { customer } = {} } = useCustomerQuery(); + const { mutate: updateCustomer } = useCustomerMutation(); if (!customer) return null; @@ -43,14 +43,18 @@ export function ProfileDataCard() { fieldName="name" id="name" label="Имя" - onChange={updateProfile} + onChange={({ name }) => updateCustomer({ data: { name } })} value={customer?.name ?? ''} /> updateProfile({ role: checked ? Role.Master : Role.Client })} + onChange={(checked) => + updateCustomer({ + data: { role: checked ? Role.Master : Role.Client }, + }) + } text="Быть мастером" /> diff --git a/apps/web/components/profile/links-card.tsx b/apps/web/components/profile/links-card.tsx index 0a7aa63..56cda90 100644 --- a/apps/web/components/profile/links-card.tsx +++ b/apps/web/components/profile/links-card.tsx @@ -2,11 +2,11 @@ 'use client'; import { LinkButton } from './components'; import { type ProfileProps } from './types'; -import { useProfileQuery } from '@/hooks/profile'; +import { useCustomerQuery } from '@/hooks/api/customers'; import { Enum_Customer_Role } from '@repo/graphql/types'; export function LinksCard({ telegramId }: Readonly) { - const { data: customer } = useProfileQuery({ telegramId }); + const { data: { customer } = {} } = useCustomerQuery({ telegramId }); const isMaster = customer?.role === Enum_Customer_Role.Master; diff --git a/apps/web/components/profile/person-card.tsx b/apps/web/components/profile/person-card.tsx index 0d15d9e..29fb5fa 100644 --- a/apps/web/components/profile/person-card.tsx +++ b/apps/web/components/profile/person-card.tsx @@ -1,12 +1,12 @@ 'use client'; import { LoadingSpinner } from '../common/spinner'; import { type ProfileProps } from './types'; -import { useProfileQuery } from '@/hooks/profile'; +import { useCustomerQuery } from '@/hooks/api/customers'; import { Avatar, AvatarFallback, AvatarImage } from '@repo/ui/components/ui/avatar'; import { Card } from '@repo/ui/components/ui/card'; export function PersonCard({ telegramId }: Readonly) { - const { data: customer, isLoading } = useProfileQuery({ telegramId }); + const { data: { customer } = {}, isLoading } = useCustomerQuery({ telegramId }); if (isLoading || !customer) return ( diff --git a/apps/web/hooks/contacts/index.ts b/apps/web/hooks/api/contacts/index.ts similarity index 100% rename from apps/web/hooks/contacts/index.ts rename to apps/web/hooks/api/contacts/index.ts diff --git a/apps/web/hooks/api/contacts/query.ts b/apps/web/hooks/api/contacts/query.ts new file mode 100644 index 0000000..f28aa61 --- /dev/null +++ b/apps/web/hooks/api/contacts/query.ts @@ -0,0 +1,25 @@ +import { getCustomerClients, getCustomerMasters } from '@/actions/api/customers'; +import { useQuery } from '@tanstack/react-query'; +import { useSession } from 'next-auth/react'; + +export const useClientsQuery = (props?: Parameters[0]) => { + const { data: session } = useSession(); + const telegramId = props?.telegramId || session?.user?.telegramId; + + return useQuery({ + enabled: false, + queryFn: () => getCustomerClients({ telegramId }), + queryKey: ['customer', 'telegramId', telegramId, 'clients', 'get'], + }); +}; + +export const useMastersQuery = (props?: Parameters[0]) => { + const { data: session } = useSession(); + const telegramId = props?.telegramId || session?.user?.telegramId; + + return useQuery({ + enabled: false, + queryFn: () => getCustomerMasters({ telegramId }), + queryKey: ['customer', 'telegramId', telegramId, 'masters', 'get'], + }); +}; diff --git a/apps/web/hooks/contacts/use-customer-contacts.ts b/apps/web/hooks/api/contacts/use-customer-contacts.ts similarity index 62% rename from apps/web/hooks/contacts/use-customer-contacts.ts rename to apps/web/hooks/api/contacts/use-customer-contacts.ts index 92b300d..2945af4 100644 --- a/apps/web/hooks/contacts/use-customer-contacts.ts +++ b/apps/web/hooks/api/contacts/use-customer-contacts.ts @@ -1,19 +1,11 @@ -/* eslint-disable canonical/id-match */ 'use client'; -import { useProfileQuery } from '../profile'; import { useClientsQuery, useMastersQuery } from './query'; import { ContactsFilterContext } from '@/context/contacts-filter'; -import { Enum_Customer_Role } from '@repo/graphql/types'; import { sift } from 'radash'; import { use, useEffect, useMemo } from 'react'; -type Parameters_ = { - includeSelf: boolean; -}; - -export function useCustomerContacts(parameters?: Parameters_) { +export function useCustomerContacts() { const { filter, setFilter } = use(ContactsFilterContext); - const { data: customer, isLoading: isLoadingProfile } = useProfileQuery(); const { data: clientsData, @@ -27,14 +19,10 @@ export function useCustomerContacts(parameters?: Parameters_) { refetch: refetchMasters, } = useMastersQuery(); - const clients = clientsData?.clients || []; - let masters = mastersData?.masters || []; + const clients = clientsData?.customers?.at(0)?.clients || []; + const masters = mastersData?.customers?.at(0)?.masters || []; - if (parameters?.includeSelf && customer?.role === Enum_Customer_Role.Master) { - masters = [{ ...customer, name: 'Я' }, ...masters]; - } - - const isLoading = isLoadingClients || isLoadingMasters || isLoadingProfile; + const isLoading = isLoadingClients || isLoadingMasters; useEffect(() => { if (filter === 'clients') { diff --git a/apps/web/hooks/api/customers.ts b/apps/web/hooks/api/customers.ts new file mode 100644 index 0000000..eac4886 --- /dev/null +++ b/apps/web/hooks/api/customers.ts @@ -0,0 +1,24 @@ +'use client'; +import { getCustomer, updateCustomer } from '@/actions/api/customers'; +import { useMutation, useQuery } from '@tanstack/react-query'; +import { useSession } from 'next-auth/react'; + +export const useCustomerQuery = (props?: Parameters[0]) => { + const { data: session } = useSession(); + const telegramId = props?.telegramId || session?.user?.telegramId; + + return useQuery({ + queryFn: () => getCustomer({ telegramId }), + queryKey: ['customer', 'telegramId', telegramId, 'get'], + }); +}; + +export const useCustomerMutation = () => { + const { refetch } = useCustomerQuery(); + + return useMutation({ + mutationFn: updateCustomer, + mutationKey: ['customer', 'update'], + onSuccess: () => refetch(), + }); +}; diff --git a/apps/web/hooks/contacts/query.ts b/apps/web/hooks/contacts/query.ts deleted file mode 100644 index 7c4c397..0000000 --- a/apps/web/hooks/contacts/query.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { getClients, getMasters } from '@/actions/contacts'; -import { useQuery } from '@tanstack/react-query'; - -export const useClientsQuery = () => - useQuery({ enabled: false, queryFn: getClients, queryKey: ['contacts', 'clients', 'get'] }); - -export const useMastersQuery = () => - useQuery({ enabled: false, queryFn: getMasters, queryKey: ['contacts', 'masters', 'get'] }); diff --git a/apps/web/hooks/profile/index.ts b/apps/web/hooks/profile/index.ts deleted file mode 100644 index 45bc79b..0000000 --- a/apps/web/hooks/profile/index.ts +++ /dev/null @@ -1,23 +0,0 @@ -'use client'; -import { getProfile, updateProfile } from '@/actions/profile'; -import { type ProfileProps } from '@/components/profile/types'; -import { useMutation, useQuery } from '@tanstack/react-query'; - -export const useProfileQuery = (props?: ProfileProps) => { - const telegramId = props?.telegramId; - - return useQuery({ - queryFn: () => getProfile({ telegramId }), - queryKey: telegramId ? ['profile', 'telegramId', telegramId, 'get'] : ['profile', 'get'], - }); -}; - -export const useProfileMutation = () => { - const { refetch } = useProfileQuery(); - - return useMutation({ - mutationFn: updateProfile, - mutationKey: ['profile', 'update'], - onSuccess: () => refetch(), - }); -}; diff --git a/apps/web/stores/order/hooks.tsx b/apps/web/stores/order/hooks.tsx index b6091f3..2bfb40f 100644 --- a/apps/web/stores/order/hooks.tsx +++ b/apps/web/stores/order/hooks.tsx @@ -2,7 +2,7 @@ 'use client'; import { OrderStoreContext } from './context'; import { type OrderStore, type Steps } from './types'; -import { useProfileQuery } from '@/hooks/profile'; +import { useCustomerQuery } from '@/hooks/api/customers'; import { Enum_Customer_Role } from '@repo/graphql/types'; import { useContext, useEffect } from 'react'; import { useStore } from 'zustand'; @@ -28,7 +28,7 @@ export const MASTER_STEPS: Steps[] = STEPS.filter((step) => step !== 'master-sel export const CLIENT_STEPS: Steps[] = STEPS.filter((step) => step !== 'client-select'); export function useInitOrderStore() { - const { data } = useProfileQuery(); + const { data: { customer } = {} } = useCustomerQuery(); const setMasterId = useOrderStore((store) => store.setMasterId); const setClientId = useOrderStore((store) => store.setClientId); @@ -36,14 +36,14 @@ export function useInitOrderStore() { const setStepsSequence = useOrderStore((store) => store._setStepSequence); useEffect(() => { - const role = data?.role; + const role = customer?.role; - if (role === Enum_Customer_Role.Master && data) { - setMasterId(data?.documentId); + if (role === Enum_Customer_Role.Master && customer) { + setMasterId(customer?.documentId); } - if (role === Enum_Customer_Role.Client && data) { - setClientId(data?.documentId); + if (role === Enum_Customer_Role.Client && customer) { + setClientId(customer?.documentId); } const steps = role === Enum_Customer_Role.Master ? MASTER_STEPS : CLIENT_STEPS; @@ -51,5 +51,5 @@ export function useInitOrderStore() { setStepsSequence(steps); setStep(initialStep); - }, [data, setClientId, setMasterId, setStep, setStepsSequence]); + }, [customer, setClientId, setMasterId, setStep, setStepsSequence]); } diff --git a/packages/graphql/api/base.ts b/packages/graphql/api/base.ts new file mode 100644 index 0000000..c76043a --- /dev/null +++ b/packages/graphql/api/base.ts @@ -0,0 +1,15 @@ +type CustomerProfile = { + telegramId: string; +}; + +export class BaseService { + protected customer: CustomerProfile; + + constructor(customer: CustomerProfile) { + if (!customer?.telegramId) { + throw new Error('Invalid customer profile: telegramId required'); + } + + this.customer = customer; + } +} diff --git a/packages/graphql/api/customer.ts b/packages/graphql/api/customer.ts deleted file mode 100644 index 024a0de..0000000 --- a/packages/graphql/api/customer.ts +++ /dev/null @@ -1,108 +0,0 @@ -'use server'; -import { getClientWithToken } from '../apollo/client'; -import * as GQL from '../types'; - -export async function createOrUpdateUser(input: GQL.CreateCustomerMutationVariables) { - if (!input.phone && !input.telegramId) throw new Error('Missing phone and telegramId'); - - const { query, mutate } = await getClientWithToken(); - - const response = await query({ - query: GQL.GetCustomerDocument, - variables: input, - }); - - const customer = response?.data?.customers?.at(0); - - if (customer && customer.phone === input.phone) { - return mutate({ - mutation: GQL.UpdateCustomerProfileDocument, - variables: { - documentId: customer.documentId, - data: { ...input }, - }, - }); - } - - return mutate({ - mutation: GQL.CreateCustomerDocument, - variables: input, - }); -} - -export async function getCustomer(input: GQL.GetCustomerQueryVariables) { - const { query } = await getClientWithToken(); - - return query({ - query: GQL.GetCustomerDocument, - variables: input, - }); -} - -export async function getCustomerMasters(input: GQL.GetCustomerMastersQueryVariables) { - const { query } = await getClientWithToken(); - - return query({ - query: GQL.GetCustomerMastersDocument, - variables: input, - }); -} - -export async function getCustomerClients(input: GQL.GetCustomerClientsQueryVariables) { - const { query } = await getClientWithToken(); - - return query({ - query: GQL.GetCustomerClientsDocument, - variables: input, - }); -} - -type AddCustomerMasterInput = Pick & { - masterId: GQL.Scalars['ID']['input']; - operation: 'add' | 'remove'; -}; - -export async function updateCustomerMaster(input: AddCustomerMasterInput) { - if (!input.phone && !input.telegramId) throw new Error('Missing phone and telegramId'); - - const { query } = await getClientWithToken(); - - const response = await query({ - query: GQL.GetCustomerMastersDocument, - variables: input, - }); - - const customer = response?.data?.customers?.at(0); - - if (customer) { - let newMastersIds = customer.masters.map((x) => x?.documentId); - - switch (input.operation) { - case 'add': - if (newMastersIds.includes(input.masterId)) return; - newMastersIds = [...newMastersIds, input.masterId]; - break; - case 'remove': - newMastersIds = newMastersIds.filter((x) => x !== input.masterId); - break; - default: - break; - } - - return updateCustomerProfile({ - documentId: customer.documentId, - data: { - masters: newMastersIds, - }, - }); - } -} - -export async function updateCustomerProfile(input: GQL.UpdateCustomerProfileMutationVariables) { - const { mutate } = await getClientWithToken(); - - return mutate({ - mutation: GQL.UpdateCustomerProfileDocument, - variables: input, - }); -} diff --git a/packages/graphql/api/customers.ts b/packages/graphql/api/customers.ts new file mode 100644 index 0000000..b63f0f7 --- /dev/null +++ b/packages/graphql/api/customers.ts @@ -0,0 +1,140 @@ +import { getClientWithToken } from '../apollo/client'; +import * as GQL from '../types'; +import { BaseService } from './base'; +import { type VariablesOf } from '@graphql-typed-document-node/core'; + +export class CustomersService extends BaseService { + async createOrUpdateCustomer(variables: VariablesOf) { + if (!variables.phone && !variables.telegramId) throw new Error('Missing phone and telegramId'); + + const { customer } = await this.getCustomer(variables); + + const { mutate } = await getClientWithToken(); + + let mutationResult; + if (customer && customer.phone === variables.phone) { + mutationResult = await mutate({ + mutation: GQL.UpdateCustomerDocument, + variables: { + data: { ...variables }, + documentId: customer.documentId, + }, + }); + } else { + mutationResult = await mutate({ + mutation: GQL.CreateCustomerDocument, + variables, + }); + } + + const error = mutationResult.errors?.at(0); + if (error) throw new Error(error.message); + + return mutationResult.data; + } + + async getCustomer(variables: VariablesOf) { + const { query } = await getClientWithToken(); + + const result = await query({ + query: GQL.GetCustomerDocument, + variables, + }); + + if (result.error) throw new Error(result.error.message); + + const customer = result.data.customers.at(0); + + return { customer }; + } + + async getCustomerClients(variables?: VariablesOf) { + const { query } = await getClientWithToken(); + + const result = await query({ + query: GQL.GetCustomerClientsDocument, + variables: { + telegramId: variables?.telegramId || this.customer.telegramId, + }, + }); + + if (result.error) throw new Error(result.error.message); + + return result.data; + } + + async getCustomerMasters(variables?: VariablesOf) { + const { query } = await getClientWithToken(); + + const result = await query({ + query: GQL.GetCustomerMastersDocument, + variables: { + telegramId: variables?.telegramId || this.customer.telegramId, + }, + }); + + if (result.error) throw new Error(result.error.message); + + return result.data; + } + + async updateCustomer( + variables: Omit, 'documentId'>, + ) { + const { customer } = await this.getCustomer(this.customer); + + if (!customer) throw new Error('Customer not found'); + const { mutate } = await getClientWithToken(); + + const mutationResult = await mutate({ + mutation: GQL.UpdateCustomerDocument, + variables: { + data: variables.data, + documentId: customer.documentId, + }, + }); + + const error = mutationResult.errors?.at(0); + if (error) throw new Error(error.message); + + return mutationResult.data; + } + + async updateCustomerMaster( + variables: VariablesOf & { + masterId: GQL.Scalars['ID']['input']; + operation: 'add' | 'remove'; + }, + ) { + if (!variables.phone && !variables.telegramId) throw new Error('Missing phone and telegramId'); + + const { query } = await getClientWithToken(); + + const response = await query({ query: GQL.GetCustomerMastersDocument, variables }); + + if (response.error) throw new Error(response.error.message); + + const customer = response?.data?.customers?.at(0); + if (!customer) throw new Error('Customer not found'); + + let newMastersIds = customer.masters.map((x) => x?.documentId); + + switch (variables.operation) { + case 'add': + if (newMastersIds.includes(variables.masterId)) break; + newMastersIds = [...newMastersIds, variables.masterId]; + break; + case 'remove': + newMastersIds = newMastersIds.filter((x) => x !== variables.masterId); + break; + default: + break; + } + + return this.updateCustomer({ + data: { + masters: newMastersIds, + }, + }); + } +} diff --git a/packages/graphql/api/index.ts b/packages/graphql/api/index.ts index 4f5a143..ae9c1ca 100644 --- a/packages/graphql/api/index.ts +++ b/packages/graphql/api/index.ts @@ -1,5 +1,5 @@ export * from './auth'; -export * from './customer'; -export * from './slot'; +export * from './customers'; export * from './order'; export * from './service'; +export * from './slot'; diff --git a/packages/graphql/eslint.config.js b/packages/graphql/eslint.config.js index e5e51e2..3fb0644 100644 --- a/packages/graphql/eslint.config.js +++ b/packages/graphql/eslint.config.js @@ -4,6 +4,6 @@ import { typescript } from '@repo/eslint-config/typescript'; export default [ ...typescript, { - ignores: ['**/graphql/types.ts', '**/schema.graphql'], + ignores: ['**/types/**'], }, ]; diff --git a/packages/graphql/operations/customer.graphql b/packages/graphql/operations/customer.graphql index b3868cf..abfcb9c 100644 --- a/packages/graphql/operations/customer.graphql +++ b/packages/graphql/operations/customer.graphql @@ -48,7 +48,7 @@ query GetCustomerClients($phone: String, $telegramId: Long) { } } -mutation UpdateCustomerProfile($documentId: ID!, $data: CustomerInput!) { +mutation UpdateCustomer($documentId: ID!, $data: CustomerInput!) { updateCustomer(documentId: $documentId, data: $data) { ...CustomerFields } diff --git a/packages/graphql/package.json b/packages/graphql/package.json index cef2fac..66199b8 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -13,15 +13,15 @@ "zod": "catalog:" }, "devDependencies": { - "graphql": "catalog:", - "@repo/eslint-config": "workspace:*", - "@repo/typescript-config": "workspace:*", - "@types/jsonwebtoken": "^9.0.7", "@graphql-codegen/cli": "^5.0.3", "@graphql-codegen/typed-document-node": "^5.0.12", "@graphql-codegen/typescript": "^4.1.2", "@graphql-codegen/typescript-operations": "^4.4.0", "@graphql-typed-document-node/core": "^3.2.0", + "@repo/eslint-config": "workspace:*", + "@repo/typescript-config": "workspace:*", + "@types/jsonwebtoken": "^9.0.7", + "graphql": "catalog:", "vite-tsconfig-paths": "catalog:", "vitest": "catalog:" } diff --git a/packages/graphql/types/operations.generated.ts b/packages/graphql/types/operations.generated.ts index c0eb19a..13ee904 100644 --- a/packages/graphql/types/operations.generated.ts +++ b/packages/graphql/types/operations.generated.ts @@ -686,13 +686,13 @@ export type GetCustomerClientsQueryVariables = Exact<{ export type GetCustomerClientsQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', documentId: string, clients: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: any | null | undefined } | null | undefined> } | null | undefined> }; -export type UpdateCustomerProfileMutationVariables = Exact<{ +export type UpdateCustomerMutationVariables = Exact<{ documentId: Scalars['ID']['input']; data: CustomerInput; }>; -export type UpdateCustomerProfileMutation = { __typename?: 'Mutation', updateCustomer?: { __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: any | null | undefined } | null | undefined }; +export type UpdateCustomerMutation = { __typename?: 'Mutation', updateCustomer?: { __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: any | null | undefined } | null | undefined }; export type OrderFieldsFragment = { __typename?: 'Order', documentId: string, time_start?: any | null | undefined, time_end?: any | null | undefined, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name?: string | null | undefined } | null | undefined>, client?: { __typename?: 'Customer', name: string, documentId: string, photoUrl?: string | null | undefined } | null | undefined }; @@ -774,7 +774,7 @@ export const CreateCustomerDocument = {"kind":"Document","definitions":[{"kind": export const GetCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; export const GetCustomerMastersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomerMasters"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"and"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"active"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"BooleanValue","value":true}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"masters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; export const GetCustomerClientsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomerClients"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"and"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"active"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"BooleanValue","value":true}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"clients"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; -export const UpdateCustomerProfileDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateCustomerProfile"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CustomerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; +export const UpdateCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CustomerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; export const GetOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"order"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OrderFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Order"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"time_start"}},{"kind":"Field","name":{"kind":"Name","value":"time_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"order_number"}},{"kind":"Field","name":{"kind":"Name","value":"services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}}]}}]}}]} as unknown as DocumentNode; export const CreateOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOrder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"OrderInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOrder"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OrderFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Order"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"time_start"}},{"kind":"Field","name":{"kind":"Name","value":"time_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"order_number"}},{"kind":"Field","name":{"kind":"Name","value":"services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}}]}}]}}]} as unknown as DocumentNode; export const GetServicesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetServices"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ServiceFiltersInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"services"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ServiceFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Service"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}}]}}]} as unknown as DocumentNode; diff --git a/packages/typescript-config/base.json b/packages/typescript-config/base.json index 5117f2a..4086abd 100644 --- a/packages/typescript-config/base.json +++ b/packages/typescript-config/base.json @@ -1,8 +1,8 @@ { "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { - "declaration": true, - "declarationMap": true, + "declaration": false, + "declarationMap": false, "esModuleInterop": true, "incremental": false, "isolatedModules": true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a73f200..46f8ba2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -290,6 +290,9 @@ importers: '@graphql-typed-document-node/core': specifier: ^3.2.0 version: 3.2.0(graphql@16.9.0) + '@repo/eslint-config': + specifier: workspace:* + version: link:../eslint-config '@repo/typescript-config': specifier: workspace:* version: link:../typescript-config -- 2.47.2 From b09283c33d0ce9a5453e279706d4f7f59a5547a2 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Fri, 16 May 2025 15:12:18 +0300 Subject: [PATCH 02/12] refactor slots api --- apps/web/actions/api/slots.ts | 35 +++++++ apps/web/actions/orders.ts | 11 ++- apps/web/actions/slots.ts | 57 ----------- .../components/time-select.tsx | 17 +++- .../schedule/components/slot-card.tsx | 5 +- .../schedule/components/slot-date.tsx | 7 +- .../schedule/components/slot-time.tsx | 12 +-- .../schedule/components/time-range.tsx | 2 +- .../components/schedule/day-slot-add-form.tsx | 13 +-- .../components/schedule/day-slots-list.tsx | 5 +- apps/web/components/schedule/slot-buttons.tsx | 15 +-- .../components/schedule/slot-orders-list.tsx | 5 +- apps/web/hooks/api/slots.ts | 84 ++++++++++++++++ apps/web/hooks/slots/index.ts | 71 -------------- apps/web/package.json | 2 +- packages/graphql/api/index.ts | 2 +- packages/graphql/api/slot.ts | 48 --------- packages/graphql/api/slots.ts | 98 +++++++++++++++++++ packages/graphql/package.json | 1 + .../graphql/utils/datetime-format.ts | 0 pnpm-lock.yaml | 6 ++ pnpm-workspace.yaml | 1 + 22 files changed, 274 insertions(+), 223 deletions(-) create mode 100644 apps/web/actions/api/slots.ts delete mode 100644 apps/web/actions/slots.ts create mode 100644 apps/web/hooks/api/slots.ts delete mode 100644 apps/web/hooks/slots/index.ts delete mode 100644 packages/graphql/api/slot.ts create mode 100644 packages/graphql/api/slots.ts rename apps/web/utils/date/index.ts => packages/graphql/utils/datetime-format.ts (100%) diff --git a/apps/web/actions/api/slots.ts b/apps/web/actions/api/slots.ts new file mode 100644 index 0000000..2e0c3b5 --- /dev/null +++ b/apps/web/actions/api/slots.ts @@ -0,0 +1,35 @@ +'use server'; +import { useService } from './lib/service'; +import { SlotsService } from '@repo/graphql/api/slots'; + +const getService = useService(SlotsService); + +export async function createSlot(...variables: Parameters) { + const service = await getService(); + + return service.createSlot(...variables); +} + +export async function deleteSlot(...variables: Parameters) { + const service = await getService(); + + return service.deleteSlot(...variables); +} + +export async function getSlot(...variables: Parameters) { + const service = await getService(); + + return service.getSlot(...variables); +} + +export async function getSlots(...variables: Parameters) { + const service = await getService(); + + return service.getSlots(...variables); +} + +export async function updateSlot(...variables: Parameters) { + const service = await getService(); + + return service.updateSlot(...variables); +} diff --git a/apps/web/actions/orders.ts b/apps/web/actions/orders.ts index 70d0112..ac7dc78 100644 --- a/apps/web/actions/orders.ts +++ b/apps/web/actions/orders.ts @@ -4,9 +4,10 @@ import type * as ApolloTypes from '../../../packages/graphql/node_modules/@apollo/client/core'; import { getCustomer, getCustomerMasters } from './api/customers'; import { _temporaryGetCustomer } from './api/lib/service'; -import { formatDate, formatTime, sumTime } from '@/utils/date'; +import { getSlot } from './api/slots'; import * as api from '@repo/graphql/api'; import { Enum_Customer_Role, Enum_Slot_State } from '@repo/graphql/types'; +import { formatDate, formatTime, sumTime } from '@repo/graphql/utils/datetime-format'; // eslint-disable-next-line @typescript-eslint/no-unused-vars type FixTypescriptCringe = ApolloTypes.FetchResult; @@ -33,9 +34,9 @@ export async function createOrder(input: OrderInput) { throw new Error('Missing slot'); } - const { data } = await api.getSlot({ documentId: input.slotId }); + const { slot } = await getSlot({ documentId: input.slotId }); - if (data.slot?.state === Enum_Slot_State.Closed) { + if (slot?.state === Enum_Slot_State.Closed) { throw new Error('Slot is closed'); } @@ -44,7 +45,7 @@ export async function createOrder(input: OrderInput) { throw new Error('Invalid client'); } - const masterId = data.slot?.master?.documentId; + const masterId = slot?.master?.documentId; const masters = await getCustomerMasters(customer); @@ -55,7 +56,7 @@ export async function createOrder(input: OrderInput) { if ( customer.role === Enum_Customer_Role.Master && - data.slot?.master?.documentId !== customer.documentId + slot?.master?.documentId !== customer.documentId ) { throw new Error('Invalid master'); } diff --git a/apps/web/actions/slots.ts b/apps/web/actions/slots.ts deleted file mode 100644 index ca5bae0..0000000 --- a/apps/web/actions/slots.ts +++ /dev/null @@ -1,57 +0,0 @@ -'use server'; -// eslint-disable-next-line sonarjs/no-internal-api-use -import type * as ApolloTypes from '../../../packages/graphql/node_modules/@apollo/client/core'; -import { getCustomer } from './api/customers'; -import { _temporaryGetCustomer } from './api/lib/service'; -import { formatDate, formatTime } from '@/utils/date'; -import * as api from '@repo/graphql/api'; -import type * as GQL from '@repo/graphql/types'; - -type AddSlotInput = Omit; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -type FixTypescriptCringe = ApolloTypes.FetchResult; - -export async function addSlot(input: AddSlotInput) { - const variables = await _temporaryGetCustomer(); - const { customer } = await getCustomer(variables); - - return api.createSlot({ - ...input, - date: formatDate(input.date).db(), - master: customer?.documentId, - time_end: formatTime(input.time_end).db(), - time_start: formatTime(input.time_start).db(), - }); -} - -export async function getSlots(input: GQL.GetSlotsQueryVariables) { - const variables = await _temporaryGetCustomer(); - const { customer } = await getCustomer(variables); - - return api.getSlots({ - filters: { - master: { - documentId: { - eq: customer?.documentId, - }, - }, - ...input.filters, - }, - }); -} - -export async function updateSlot(input: GQL.UpdateSlotMutationVariables) { - return api.updateSlot({ - ...input, - data: { - ...input.data, - date: input.data?.date ? formatDate(input.data.date).db() : undefined, - time_end: input.data?.time_end ? formatTime(input.data.time_end).db() : undefined, - time_start: input.data?.time_start ? formatTime(input.data.time_start).db() : undefined, - }, - }); -} - -export const getSlot = api.getSlot; -export const deleteSlot = api.deleteSlot; diff --git a/apps/web/components/orders/components/datetime-select/components/time-select.tsx b/apps/web/components/orders/components/datetime-select/components/time-select.tsx index 4866e47..034b005 100644 --- a/apps/web/components/orders/components/datetime-select/components/time-select.tsx +++ b/apps/web/components/orders/components/datetime-select/components/time-select.tsx @@ -1,6 +1,6 @@ /* eslint-disable canonical/id-match */ 'use client'; -import { useSlots } from '@/hooks/slots'; +import { useSlots } from '@/hooks/api/slots'; import { useOrderStore } from '@/stores/order'; import { Enum_Slot_State, type SlotFieldsFragment } from '@repo/graphql/types'; import { Button } from '@repo/ui/components/ui/button'; @@ -25,9 +25,20 @@ const generateTimeSlots = (slots: SlotFieldsFragment[]): Array<{ slotId: string; export function TimeSelect() { const masterId = useOrderStore((store) => store.masterId); const date = useOrderStore((store) => store.date); - const { data } = useSlots({ date, masterId }); + const { data: { slots } = {} } = useSlots({ + filters: { + date: { + eq: date, + }, + master: { + documentId: { + eq: masterId, + }, + }, + }, + }); - const openedSlots = data?.data.slots.filter((slot) => slot?.state === Enum_Slot_State.Open); + const openedSlots = slots?.filter((slot) => slot?.state === Enum_Slot_State.Open); const timeSlots = generateTimeSlots(openedSlots ? sift(openedSlots) : []); const morning = timeSlots.filter((time) => time.time.hour() < 12); diff --git a/apps/web/components/schedule/components/slot-card.tsx b/apps/web/components/schedule/components/slot-card.tsx index 4d19768..8b1a2cc 100644 --- a/apps/web/components/schedule/components/slot-card.tsx +++ b/apps/web/components/schedule/components/slot-card.tsx @@ -1,7 +1,7 @@ /* eslint-disable canonical/id-match */ 'use client'; import { ReadonlyTimeRange } from './time-range'; -import { useSlotQuery } from '@/hooks/slots'; +import { useSlotQuery } from '@/hooks/api/slots'; import { Enum_Slot_State, type SlotFieldsFragment } from '@repo/graphql/types'; import { Badge } from '@repo/ui/components/ui/badge'; import { cn } from '@repo/ui/lib/utils'; @@ -18,8 +18,7 @@ export function SlotCard(props: Readonly) { const pathname = usePathname(); const { documentId } = props; - const { data } = useSlotQuery({ documentId }); - const slot = data?.data?.slot; + const { data: { slot } = {} } = useSlotQuery({ documentId }); const ordersNumber = slot?.orders?.length; const hasOrders = Boolean(ordersNumber); diff --git a/apps/web/components/schedule/components/slot-date.tsx b/apps/web/components/schedule/components/slot-date.tsx index 8493aca..cdec86d 100644 --- a/apps/web/components/schedule/components/slot-date.tsx +++ b/apps/web/components/schedule/components/slot-date.tsx @@ -1,11 +1,10 @@ 'use client'; import { type SlotComponentProps } from '../types'; -import { useSlotQuery } from '@/hooks/slots'; -import { formatDate } from '@/utils/date'; +import { useSlotQuery } from '@/hooks/api/slots'; +import { formatDate } from '@repo/graphql/utils/datetime-format'; export function SlotDate({ documentId }: Readonly) { - const { data } = useSlotQuery({ documentId }); - const slot = data?.data?.slot; + const { data: { slot } = {} } = useSlotQuery({ documentId }); if (!slot) return null; diff --git a/apps/web/components/schedule/components/slot-time.tsx b/apps/web/components/schedule/components/slot-time.tsx index d0b0c62..3c85b34 100644 --- a/apps/web/components/schedule/components/slot-time.tsx +++ b/apps/web/components/schedule/components/slot-time.tsx @@ -3,7 +3,7 @@ import { ScheduleTimeContext } from '../context'; import { type SlotComponentProps } from '../types'; import { EditableTimeRangeForm, ReadonlyTimeRange } from './time-range'; -import { useSlotMutation, useSlotQuery } from '@/hooks/slots'; +import { useSlotMutation, useSlotQuery } from '@/hooks/api/slots'; import { Button } from '@repo/ui/components/ui/button'; import { PencilLine } from 'lucide-react'; import { use, useEffect } from 'react'; @@ -19,8 +19,7 @@ function SlotTimeEditForm({ documentId }: Readonly) { use(ScheduleTimeContext); const { isPending: isMutationPending, mutate: updateSlot } = useSlotMutation({ documentId }); - const { data, isPending: isQueryPending } = useSlotQuery({ documentId }); - const slot = data?.data?.slot; + const { data: { slot } = {}, isPending: isQueryPending } = useSlotQuery({ documentId }); const isPending = isMutationPending || isQueryPending; @@ -32,7 +31,7 @@ function SlotTimeEditForm({ documentId }: Readonly) { }, [editMode, setEndTime, setStartTime, slot]); function handleSubmit() { - updateSlot({ data: { time_end: endTime, time_start: startTime }, documentId }); + updateSlot({ data: { time_end: endTime, time_start: startTime } }); resetTime(); setEditMode(false); } @@ -46,11 +45,10 @@ function SlotTimeEditForm({ documentId }: Readonly) { ); } -function SlotTimeReadonly(props: Readonly) { +function SlotTimeReadonly({ documentId }: Readonly) { const { setEditMode } = use(ScheduleTimeContext); - const { data } = useSlotQuery(props); - const slot = data?.data?.slot; + const { data: { slot } = {} } = useSlotQuery({ documentId }); if (!slot) return null; diff --git a/apps/web/components/schedule/components/time-range.tsx b/apps/web/components/schedule/components/time-range.tsx index 6041ebd..d2f0edc 100644 --- a/apps/web/components/schedule/components/time-range.tsx +++ b/apps/web/components/schedule/components/time-range.tsx @@ -1,6 +1,6 @@ 'use client'; import { ScheduleTimeContext } from '../context'; -import { formatTime } from '@/utils/date'; +import { formatTime } from '@repo/graphql/utils/datetime-format'; import { Input } from '@repo/ui/components/ui/input'; import { cn } from '@repo/ui/lib/utils'; import { type FormEvent, type PropsWithChildren, use } from 'react'; diff --git a/apps/web/components/schedule/day-slot-add-form.tsx b/apps/web/components/schedule/day-slot-add-form.tsx index ccc593b..df5b64e 100644 --- a/apps/web/components/schedule/day-slot-add-form.tsx +++ b/apps/web/components/schedule/day-slot-add-form.tsx @@ -3,7 +3,7 @@ import { EditableTimeRangeForm } from './components/time-range'; import { ScheduleTimeContext, ScheduleTimeContextProvider } from './context'; import { ScheduleContext } from '@/context/schedule'; -import { useSlotAdd } from '@/hooks/slots'; +import { useSlotCreate } from '@/hooks/api/slots'; import { withContext } from '@/utils/context'; import { Enum_Slot_State } from '@repo/graphql/types'; import { Button } from '@repo/ui/components/ui/button'; @@ -15,16 +15,17 @@ export const DaySlotAddForm = withContext(ScheduleTimeContextProvider)(function const { selectedDate } = use(ScheduleContext); - const { isPending, mutate: addSlot } = useSlotAdd({ date: selectedDate }); + const { isPending, mutate: addSlot } = useSlotCreate({ date: selectedDate }); const handleSubmit = (event: FormEvent) => { event.preventDefault(); if (startTime && endTime) { addSlot({ - date: selectedDate, - state: Enum_Slot_State.Open, - time_end: endTime, - time_start: startTime, + input: { + state: Enum_Slot_State.Open, + time_end: endTime, + time_start: startTime, + }, }); resetTime(); diff --git a/apps/web/components/schedule/day-slots-list.tsx b/apps/web/components/schedule/day-slots-list.tsx index caf06b2..3f87ff7 100644 --- a/apps/web/components/schedule/day-slots-list.tsx +++ b/apps/web/components/schedule/day-slots-list.tsx @@ -3,13 +3,12 @@ import { SlotCard } from './components/slot-card'; import { DaySlotAddForm } from './day-slot-add-form'; import { LoadingSpinner } from '@/components/common/spinner'; import { ScheduleContext } from '@/context/schedule'; -import { useSlots } from '@/hooks/slots'; +import { useSlots } from '@/hooks/api/slots'; import { use } from 'react'; export function DaySlotsList() { const { selectedDate } = use(ScheduleContext); - const { data, isLoading } = useSlots({ date: selectedDate }); - const slots = data?.data.slots; + const { data: { slots } = {}, isLoading } = useSlots({ filters: { date: { eq: selectedDate } } }); if (isLoading) return ; diff --git a/apps/web/components/schedule/slot-buttons.tsx b/apps/web/components/schedule/slot-buttons.tsx index c9ce691..654f70e 100644 --- a/apps/web/components/schedule/slot-buttons.tsx +++ b/apps/web/components/schedule/slot-buttons.tsx @@ -2,36 +2,31 @@ /* eslint-disable canonical/id-match */ 'use client'; import { type SlotComponentProps } from './types'; -import { ScheduleContext } from '@/context/schedule'; -import { useSlotDelete, useSlotMutation, useSlotQuery } from '@/hooks/slots'; +import { useSlotDelete, useSlotMutation, useSlotQuery } from '@/hooks/api/slots'; import { Enum_Slot_State } from '@repo/graphql/types'; import { Button } from '@repo/ui/components/ui/button'; import { useRouter } from 'next/navigation'; -import { use } from 'react'; export function SlotButtons({ documentId }: Readonly) { - const { data } = useSlotQuery({ documentId }); + const { data: { slot } = {} } = useSlotQuery({ documentId }); const { mutate: updateSlot } = useSlotMutation({ documentId }); - const { selectedDate } = use(ScheduleContext); - const { mutate: deleteSlot } = useSlotDelete({ date: selectedDate, documentId }); + const { mutate: deleteSlot } = useSlotDelete({ documentId }); const router = useRouter(); - const slot = data?.data?.slot; - if (!slot) return null; const isOpened = slot?.state === Enum_Slot_State.Open; const isClosed = slot?.state === Enum_Slot_State.Closed; function handleOpenSlot() { - return updateSlot({ data: { state: Enum_Slot_State.Open }, documentId }); + return updateSlot({ data: { state: Enum_Slot_State.Open } }); } function handleCloseSlot() { - return updateSlot({ data: { state: Enum_Slot_State.Closed }, documentId }); + return updateSlot({ data: { state: Enum_Slot_State.Closed } }); } function handleDeleteSlot() { diff --git a/apps/web/components/schedule/slot-orders-list.tsx b/apps/web/components/schedule/slot-orders-list.tsx index f84226d..80ed9f1 100644 --- a/apps/web/components/schedule/slot-orders-list.tsx +++ b/apps/web/components/schedule/slot-orders-list.tsx @@ -1,11 +1,10 @@ 'use client'; import { OrderCard } from './components/order-card'; import { type SlotComponentProps } from './types'; -import { useSlotQuery } from '@/hooks/slots'; +import { useSlotQuery } from '@/hooks/api/slots'; export function SlotOrdersList({ documentId }: Readonly) { - const { data } = useSlotQuery({ documentId }); - const slot = data?.data?.slot; + const { data: { slot } = {} } = useSlotQuery({ documentId }); if (!slot) return null; diff --git a/apps/web/hooks/api/slots.ts b/apps/web/hooks/api/slots.ts new file mode 100644 index 0000000..78db87f --- /dev/null +++ b/apps/web/hooks/api/slots.ts @@ -0,0 +1,84 @@ +'use client'; +import { useCustomerQuery } from './customers'; +import { createSlot, deleteSlot, getSlot, getSlots, updateSlot } from '@/actions/api/slots'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; + +export const useSlots = (variables: Parameters[0]) => { + const { data: { customer } = {} } = useCustomerQuery(); + + const masterId = variables.filters?.master?.documentId?.eq || customer?.documentId; + const date = variables.filters?.date?.eq; + + return useQuery({ + queryFn: () => getSlots(variables), + queryKey: ['slots', 'master', masterId, 'list', date.toISOString()], + }); +}; + +export const useSlotQuery = (variables: Parameters[0]) => { + const { documentId } = variables; + + return useQuery({ + queryFn: () => getSlot(variables), + queryKey: ['slots', 'get', documentId], + }); +}; + +export const useSlotMutation = ({ + documentId, +}: Pick[0], 'documentId'>) => { + const queryClient = useQueryClient(); + function handleOnSuccess() { + queryClient.invalidateQueries({ + queryKey: ['slots', 'get', documentId], + }); + } + + return useMutation({ + mutationFn: ({ data }: Pick[0], 'data'>) => + updateSlot({ data, documentId }), + mutationKey: ['slots', 'update', documentId], + onSuccess: handleOnSuccess, + }); +}; + +export const useSlotCreate = ({ date }: { date: Date }) => { + const { data: { customer } = {} } = useCustomerQuery(); + const masterId = customer?.documentId; + + const queryClient = useQueryClient(); + + function handleOnSuccess() { + queryClient.invalidateQueries({ + queryKey: ['slots', 'master', masterId, 'list', date.toISOString()], + }); + } + + return useMutation({ + mutationFn: ({ input }: { input: Omit[0]['input'], 'date'> }) => + createSlot({ input: { ...input, date } }), + mutationKey: ['slots', 'create', 'date', date.toISOString(), 'master', masterId], + onSuccess: handleOnSuccess, + }); +}; + +export const useSlotDelete = ({ documentId }: Parameters[0]) => { + const { data: { slot } = {} } = useSlotQuery({ documentId }); + + const queryClient = useQueryClient(); + + function handleOnSuccess() { + const date = slot?.date; + const masterId = slot?.master; + + queryClient.invalidateQueries({ + queryKey: ['slots', 'master', masterId, 'list', date.toISOString()], + }); + } + + return useMutation({ + mutationFn: () => deleteSlot({ documentId }), + mutationKey: ['slots', 'delete', documentId], + onSuccess: handleOnSuccess, + }); +}; diff --git a/apps/web/hooks/slots/index.ts b/apps/web/hooks/slots/index.ts deleted file mode 100644 index eacc4c0..0000000 --- a/apps/web/hooks/slots/index.ts +++ /dev/null @@ -1,71 +0,0 @@ -'use client'; -import { addSlot, deleteSlot, getSlot, getSlots, updateSlot } from '@/actions/slots'; -import { formatDate } from '@/utils/date'; -// eslint-disable-next-line sonarjs/no-internal-api-use -import type * as ApolloTypes from '@repo/graphql/node_modules/@apollo/client/core'; -import { useMutation, useQuery } from '@tanstack/react-query'; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -type FixTypescriptCringe = ApolloTypes.FetchResult; - -type SlotAddInput = { - date: Date; -}; - -type SlotMutationInput = { - documentId: string; -}; - -type SlotQueryInput = { - date: Date; - masterId?: null | string; -}; - -export const useSlots = ({ date, masterId }: SlotQueryInput) => { - return useQuery({ - queryFn: () => - getSlots({ - filters: { - date: { eq: formatDate(date).db() }, - master: masterId ? { documentId: { eq: masterId } } : undefined, - }, - }), - queryKey: ['slots', 'master', masterId, 'list', date], - }); -}; - -export const useSlotQuery = ({ documentId }: SlotMutationInput) => - useQuery({ - queryFn: () => getSlot({ documentId }), - queryKey: ['slots', 'get', documentId], - }); - -export const useSlotMutation = ({ documentId }: SlotMutationInput) => { - const { refetch } = useSlotQuery({ documentId }); - - return useMutation({ - mutationFn: updateSlot, - mutationKey: ['slots', 'update', documentId], - onSuccess: () => refetch(), - }); -}; - -export const useSlotAdd = ({ date }: SlotAddInput) => { - const { refetch } = useSlots({ date }); - - return useMutation({ - mutationFn: addSlot, - mutationKey: ['slots', 'add'], - onSuccess: () => refetch(), - }); -}; - -export const useSlotDelete = ({ date, documentId }: SlotAddInput & SlotMutationInput) => { - const { refetch } = useSlots({ date }); - - return useMutation({ - mutationFn: () => deleteSlot({ documentId }), - mutationKey: ['slots', 'delete', documentId], - onSuccess: () => refetch(), - }); -}; diff --git a/apps/web/package.json b/apps/web/package.json index c76fca5..4db2048 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -17,7 +17,7 @@ "@repo/ui": "workspace:*", "@tanstack/react-query": "^5.64.1", "@telegram-apps/sdk-react": "^2.0.19", - "dayjs": "^1.11.13", + "dayjs": "catalog:", "graphql": "catalog:", "lucide-react": "catalog:", "next": "15.3.0", diff --git a/packages/graphql/api/index.ts b/packages/graphql/api/index.ts index ae9c1ca..c37fc52 100644 --- a/packages/graphql/api/index.ts +++ b/packages/graphql/api/index.ts @@ -2,4 +2,4 @@ export * from './auth'; export * from './customers'; export * from './order'; export * from './service'; -export * from './slot'; +export * from './slots'; diff --git a/packages/graphql/api/slot.ts b/packages/graphql/api/slot.ts deleted file mode 100644 index 8e872ad..0000000 --- a/packages/graphql/api/slot.ts +++ /dev/null @@ -1,48 +0,0 @@ -'use server'; -import { getClientWithToken } from '../apollo/client'; -import * as GQL from '../types'; - -export async function createSlot(input: GQL.CreateSlotMutationVariables['input']) { - const { mutate } = await getClientWithToken(); - - return mutate({ - mutation: GQL.CreateSlotDocument, - variables: { input }, - }); -} - -export async function getSlots(input: GQL.GetSlotsQueryVariables) { - const { query } = await getClientWithToken(); - - return query({ - query: GQL.GetSlotsDocument, - variables: input, - }); -} - -export async function getSlot(input: GQL.GetSlotQueryVariables) { - const { query } = await getClientWithToken(); - - return query({ - query: GQL.GetSlotDocument, - variables: input, - }); -} - -export async function updateSlot(input: GQL.UpdateSlotMutationVariables) { - const { mutate } = await getClientWithToken(); - - return mutate({ - mutation: GQL.UpdateSlotDocument, - variables: input, - }); -} - -export async function deleteSlot(input: GQL.DeleteSlotMutationVariables) { - const { mutate } = await getClientWithToken(); - - return mutate({ - mutation: GQL.DeleteSlotDocument, - variables: input, - }); -} diff --git a/packages/graphql/api/slots.ts b/packages/graphql/api/slots.ts new file mode 100644 index 0000000..6b50c94 --- /dev/null +++ b/packages/graphql/api/slots.ts @@ -0,0 +1,98 @@ +import { getClientWithToken } from '../apollo/client'; +import * as GQL from '../types'; +import { formatDate, formatTime } from '../utils/datetime-format'; +import { BaseService } from './base'; +import { CustomersService } from './customers'; +import { type VariablesOf } from '@graphql-typed-document-node/core'; + +export class SlotsService extends BaseService { + async createSlot(variables: VariablesOf) { + const customerService = new CustomersService(this.customer); + + const { customer } = await customerService.getCustomer(this.customer); + + const { mutate } = await getClientWithToken(); + + const mutationResult = await mutate({ + mutation: GQL.CreateSlotDocument, + variables: { + ...variables, + date: formatDate(variables.input.date).db(), + master: customer?.documentId, + time_end: formatTime(variables.input.time_end).db(), + time_start: formatTime(variables.input.time_start).db(), + }, + }); + + const error = mutationResult.errors?.at(0); + if (error) throw new Error(error.message); + + return mutationResult.data; + } + + async deleteSlot(variables: VariablesOf) { + const { mutate } = await getClientWithToken(); + + const mutationResult = await mutate({ + mutation: GQL.DeleteSlotDocument, + variables, + }); + + const error = mutationResult.errors?.at(0); + if (error) throw new Error(error.message); + + return mutationResult.data; + } + + async getSlot(variables: VariablesOf) { + const { query } = await getClientWithToken(); + + const result = await query({ + query: GQL.GetSlotDocument, + variables, + }); + + if (result.error) throw new Error(result.error.message); + + return result.data; + } + + async getSlots(variables: VariablesOf) { + const { query } = await getClientWithToken(); + + const result = await query({ + query: GQL.GetSlotsDocument, + variables: { + filters: { + ...variables.filters, + date: { eq: formatDate(variables?.filters?.date?.eq).db() }, + }, + }, + }); + + if (result.error) throw new Error(result.error.message); + + return result.data; + } + + async updateSlot(variables: VariablesOf) { + const { mutate } = await getClientWithToken(); + + const mutationResult = await mutate({ + mutation: GQL.UpdateSlotDocument, + variables: { + ...variables, + date: variables.data?.date ? formatDate(variables.data.date).db() : undefined, + time_end: variables.data?.time_end ? formatTime(variables.data.time_end).db() : undefined, + time_start: variables.data?.time_start + ? formatTime(variables.data.time_start).db() + : undefined, + }, + }); + + const error = mutationResult.errors?.at(0); + if (error) throw new Error(error.message); + + return mutationResult.data; + } +} diff --git a/packages/graphql/package.json b/packages/graphql/package.json index 66199b8..c44907b 100644 --- a/packages/graphql/package.json +++ b/packages/graphql/package.json @@ -9,6 +9,7 @@ }, "dependencies": { "@apollo/client": "catalog:", + "dayjs": "catalog:", "jsonwebtoken": "catalog:", "zod": "catalog:" }, diff --git a/apps/web/utils/date/index.ts b/packages/graphql/utils/datetime-format.ts similarity index 100% rename from apps/web/utils/date/index.ts rename to packages/graphql/utils/datetime-format.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 46f8ba2..afc67bc 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,6 +24,9 @@ catalogs: autoprefixer: specifier: ^10.4.20 version: 10.4.20 + dayjs: + specifier: ^1.11.3 + version: 1.11.13 dotenv-cli: specifier: ^7.4.4 version: 7.4.4 @@ -268,6 +271,9 @@ importers: '@apollo/client': specifier: 'catalog:' version: 3.12.4(@types/react@19.1.2)(graphql-ws@5.16.0(graphql@16.9.0))(graphql@16.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + dayjs: + specifier: 'catalog:' + version: 1.11.13 jsonwebtoken: specifier: 'catalog:' version: 9.0.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6c1fe15..853b936 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -8,6 +8,7 @@ catalog: "@types/react-dom": ^19.1.2 "@vchikalkin/eslint-config-awesome": ^2.2.2 autoprefixer: ^10.4.20 + dayjs: ^1.11.3 dotenv-cli: ^7.4.4 eslint: ^9.17.0 graphql: ^16.9.0 -- 2.47.2 From 7d368d9ef053d517caf32284fb9914c028590062 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Fri, 16 May 2025 16:05:09 +0300 Subject: [PATCH 03/12] hooks/customers: use invalidateQueries --- apps/web/hooks/api/customers.ts | 15 ++++++++++++--- apps/web/hooks/api/slots.ts | 1 + 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/web/hooks/api/customers.ts b/apps/web/hooks/api/customers.ts index eac4886..527d598 100644 --- a/apps/web/hooks/api/customers.ts +++ b/apps/web/hooks/api/customers.ts @@ -1,6 +1,6 @@ 'use client'; import { getCustomer, updateCustomer } from '@/actions/api/customers'; -import { useMutation, useQuery } from '@tanstack/react-query'; +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useSession } from 'next-auth/react'; export const useCustomerQuery = (props?: Parameters[0]) => { @@ -14,11 +14,20 @@ export const useCustomerQuery = (props?: Parameters[0]) => { }; export const useCustomerMutation = () => { - const { refetch } = useCustomerQuery(); + const { data: session } = useSession(); + const telegramId = session?.user?.telegramId; + + const queryClient = useQueryClient(); + + function handleOnSuccess() { + queryClient.invalidateQueries({ + queryKey: ['customer', 'telegramId', telegramId, 'get'], + }); + } return useMutation({ mutationFn: updateCustomer, mutationKey: ['customer', 'update'], - onSuccess: () => refetch(), + onSuccess: handleOnSuccess, }); }; diff --git a/apps/web/hooks/api/slots.ts b/apps/web/hooks/api/slots.ts index 78db87f..67066a3 100644 --- a/apps/web/hooks/api/slots.ts +++ b/apps/web/hooks/api/slots.ts @@ -28,6 +28,7 @@ export const useSlotMutation = ({ documentId, }: Pick[0], 'documentId'>) => { const queryClient = useQueryClient(); + function handleOnSuccess() { queryClient.invalidateQueries({ queryKey: ['slots', 'get', documentId], -- 2.47.2 From 39498dbcff396602bc44ed180158d53ad72617be Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Fri, 16 May 2025 16:47:12 +0300 Subject: [PATCH 04/12] refactor services api --- apps/web/actions/api/services.ts | 18 +++++++++++ apps/web/actions/orders.ts | 5 +-- apps/web/actions/services.ts | 21 ------------ .../orders/components/service-select.tsx | 20 +++++++++--- apps/web/hooks/api/services.ts | 18 +++++++++++ apps/web/hooks/services/index.ts | 15 --------- packages/graphql/api/index.ts | 2 +- packages/graphql/api/service.ts | 21 ------------ packages/graphql/api/services.ts | 32 +++++++++++++++++++ 9 files changed, 87 insertions(+), 65 deletions(-) create mode 100644 apps/web/actions/api/services.ts delete mode 100644 apps/web/actions/services.ts create mode 100644 apps/web/hooks/api/services.ts delete mode 100644 apps/web/hooks/services/index.ts delete mode 100644 packages/graphql/api/service.ts create mode 100644 packages/graphql/api/services.ts diff --git a/apps/web/actions/api/services.ts b/apps/web/actions/api/services.ts new file mode 100644 index 0000000..577a04d --- /dev/null +++ b/apps/web/actions/api/services.ts @@ -0,0 +1,18 @@ +'use server'; + +import { useService } from './lib/service'; +import { ServicesService } from '@repo/graphql/api/services'; + +const getServicesService = useService(ServicesService); + +export async function getService(...variables: Parameters) { + const service = await getServicesService(); + + return service.getService(...variables); +} + +export async function getServices(...variables: Parameters) { + const service = await getServicesService(); + + return service.getServices(...variables); +} diff --git a/apps/web/actions/orders.ts b/apps/web/actions/orders.ts index ac7dc78..1ed6d25 100644 --- a/apps/web/actions/orders.ts +++ b/apps/web/actions/orders.ts @@ -4,6 +4,7 @@ import type * as ApolloTypes from '../../../packages/graphql/node_modules/@apollo/client/core'; import { getCustomer, getCustomerMasters } from './api/customers'; import { _temporaryGetCustomer } from './api/lib/service'; +import { getService } from './api/services'; import { getSlot } from './api/slots'; import * as api from '@repo/graphql/api'; import { Enum_Customer_Role, Enum_Slot_State } from '@repo/graphql/types'; @@ -61,9 +62,9 @@ export async function createOrder(input: OrderInput) { throw new Error('Invalid master'); } - const service = await api.getService({ documentId: input.serviceId }); + const { service } = await getService({ documentId: input.serviceId }); - const endTime = sumTime(input.time, service?.data?.service?.duration); + const endTime = sumTime(input.time, service?.duration); const payload = { client: input.clientId, date: formatDate(input.date).db(), diff --git a/apps/web/actions/services.ts b/apps/web/actions/services.ts deleted file mode 100644 index 6cec874..0000000 --- a/apps/web/actions/services.ts +++ /dev/null @@ -1,21 +0,0 @@ -'use server'; -import { getCustomer } from './api/customers'; -import { _temporaryGetCustomer } from './api/lib/service'; -import * as api from '@repo/graphql/api/service'; -// eslint-disable-next-line sonarjs/no-internal-api-use -import type * as ApolloTypes from '@repo/graphql/node_modules/@apollo/client/core'; -import { type GetServicesQueryVariables } from '@repo/graphql/types'; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -type FixTypescriptCringe = ApolloTypes.FetchResult; - -export async function getServices(input?: GetServicesQueryVariables) { - const variables = await _temporaryGetCustomer(); - const { customer } = await getCustomer(variables); - - const filters = input || { filters: { master: { documentId: { eq: customer?.documentId } } } }; - - return api.getServices(filters); -} - -export const getService = api.getService; diff --git a/apps/web/components/orders/components/service-select.tsx b/apps/web/components/orders/components/service-select.tsx index e341c01..73273e2 100644 --- a/apps/web/components/orders/components/service-select.tsx +++ b/apps/web/components/orders/components/service-select.tsx @@ -1,17 +1,27 @@ 'use client'; -import { useServicesQuery } from '@/hooks/services'; +import { useServicesQuery } from '@/hooks/api/services'; import { useOrderStore } from '@/stores/order'; import { type ServiceFieldsFragment } from '@repo/graphql/types'; import { cn } from '@repo/ui/lib/utils'; export function ServiceSelect() { - const { data } = useServicesQuery(); + const masterId = useOrderStore((store) => store.masterId); + + const { data: { services } = {} } = useServicesQuery({ + filters: { + master: { + documentId: { + eq: masterId, + }, + }, + }, + }); + + if (!services?.length) return null; return (
- {data?.data.services.map( - (service) => service && , - )} + {services.map((service) => service && )}
); } diff --git a/apps/web/hooks/api/services.ts b/apps/web/hooks/api/services.ts new file mode 100644 index 0000000..8af6c6d --- /dev/null +++ b/apps/web/hooks/api/services.ts @@ -0,0 +1,18 @@ +'use client'; +import { getService, getServices } from '@/actions/api/services'; +import { useQuery } from '@tanstack/react-query'; + +export const useServicesQuery = (input: Parameters[0]) => { + const masterId = input.filters?.master?.documentId?.eq; + + return useQuery({ + queryFn: () => getServices(input), + queryKey: ['services', 'master', masterId, 'list'], + }); +}; + +export const useServiceQuery = (input: Parameters[0]) => + useQuery({ + queryFn: () => getService(input), + queryKey: ['services', 'documentId', input.documentId], + }); diff --git a/apps/web/hooks/services/index.ts b/apps/web/hooks/services/index.ts deleted file mode 100644 index c8e8475..0000000 --- a/apps/web/hooks/services/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -'use client'; -import { getServices } from '@/actions/services'; -// eslint-disable-next-line sonarjs/no-internal-api-use -import type * as ApolloTypes from '@repo/graphql/node_modules/@apollo/client/core'; -import { type GetServicesQueryVariables } from '@repo/graphql/types'; -import { useQuery } from '@tanstack/react-query'; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -type FixTypescriptCringe = ApolloTypes.FetchResult; - -export const useServicesQuery = (input?: GetServicesQueryVariables) => - useQuery({ - queryFn: () => getServices(input), - queryKey: ['services', 'list'], - }); diff --git a/packages/graphql/api/index.ts b/packages/graphql/api/index.ts index c37fc52..00a4b07 100644 --- a/packages/graphql/api/index.ts +++ b/packages/graphql/api/index.ts @@ -1,5 +1,5 @@ export * from './auth'; export * from './customers'; export * from './order'; -export * from './service'; +export * from './services'; export * from './slots'; diff --git a/packages/graphql/api/service.ts b/packages/graphql/api/service.ts deleted file mode 100644 index fd4b00e..0000000 --- a/packages/graphql/api/service.ts +++ /dev/null @@ -1,21 +0,0 @@ -'use server'; -import { getClientWithToken } from '../apollo/client'; -import * as GQL from '../types'; - -export async function getServices(input: GQL.GetServicesQueryVariables) { - const { query } = await getClientWithToken(); - - return query({ - query: GQL.GetServicesDocument, - variables: input, - }); -} - -export async function getService(input: GQL.GetServiceQueryVariables) { - const { query } = await getClientWithToken(); - - return query({ - query: GQL.GetServiceDocument, - variables: input, - }); -} diff --git a/packages/graphql/api/services.ts b/packages/graphql/api/services.ts new file mode 100644 index 0000000..202d514 --- /dev/null +++ b/packages/graphql/api/services.ts @@ -0,0 +1,32 @@ +import { getClientWithToken } from '../apollo/client'; +import * as GQL from '../types'; +import { BaseService } from './base'; +import { type VariablesOf } from '@graphql-typed-document-node/core'; + +export class ServicesService extends BaseService { + async getService(variables: VariablesOf) { + const { query } = await getClientWithToken(); + + const result = await query({ + query: GQL.GetServiceDocument, + variables, + }); + + if (result.error) throw new Error(result.error.message); + + return result.data; + } + + async getServices(variables: VariablesOf) { + const { query } = await getClientWithToken(); + + const result = await query({ + query: GQL.GetServicesDocument, + variables, + }); + + if (result.error) throw new Error(result.error.message); + + return result.data; + } +} -- 2.47.2 From f77e21b8151d7144742f3b73c9f0e08fe4a8c6c0 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Fri, 16 May 2025 17:25:51 +0300 Subject: [PATCH 05/12] optimize hooks queryKey --- apps/web/hooks/api/contacts/query.ts | 4 +- apps/web/hooks/api/customers.ts | 13 ++++--- apps/web/hooks/api/services.ts | 11 +++--- apps/web/hooks/api/slots.ts | 56 ++++++++++++++-------------- 4 files changed, 41 insertions(+), 43 deletions(-) diff --git a/apps/web/hooks/api/contacts/query.ts b/apps/web/hooks/api/contacts/query.ts index f28aa61..935500d 100644 --- a/apps/web/hooks/api/contacts/query.ts +++ b/apps/web/hooks/api/contacts/query.ts @@ -9,7 +9,7 @@ export const useClientsQuery = (props?: Parameters[0] return useQuery({ enabled: false, queryFn: () => getCustomerClients({ telegramId }), - queryKey: ['customer', 'telegramId', telegramId, 'clients', 'get'], + queryKey: ['customer', 'telegramId', telegramId, 'clients'], }); }; @@ -20,6 +20,6 @@ export const useMastersQuery = (props?: Parameters[0] return useQuery({ enabled: false, queryFn: () => getCustomerMasters({ telegramId }), - queryKey: ['customer', 'telegramId', telegramId, 'masters', 'get'], + queryKey: ['customer', 'telegramId', telegramId, 'masters'], }); }; diff --git a/apps/web/hooks/api/customers.ts b/apps/web/hooks/api/customers.ts index 527d598..5c69a04 100644 --- a/apps/web/hooks/api/customers.ts +++ b/apps/web/hooks/api/customers.ts @@ -8,26 +8,27 @@ export const useCustomerQuery = (props?: Parameters[0]) => { const telegramId = props?.telegramId || session?.user?.telegramId; return useQuery({ + enabled: Boolean(telegramId), queryFn: () => getCustomer({ telegramId }), - queryKey: ['customer', 'telegramId', telegramId, 'get'], + queryKey: ['customer', telegramId], }); }; export const useCustomerMutation = () => { const { data: session } = useSession(); const telegramId = session?.user?.telegramId; - const queryClient = useQueryClient(); - function handleOnSuccess() { + const handleOnSuccess = () => { + if (!telegramId) return; + queryClient.invalidateQueries({ - queryKey: ['customer', 'telegramId', telegramId, 'get'], + queryKey: ['customer', telegramId], }); - } + }; return useMutation({ mutationFn: updateCustomer, - mutationKey: ['customer', 'update'], onSuccess: handleOnSuccess, }); }; diff --git a/apps/web/hooks/api/services.ts b/apps/web/hooks/api/services.ts index 8af6c6d..062ee76 100644 --- a/apps/web/hooks/api/services.ts +++ b/apps/web/hooks/api/services.ts @@ -3,16 +3,15 @@ import { getService, getServices } from '@/actions/api/services'; import { useQuery } from '@tanstack/react-query'; export const useServicesQuery = (input: Parameters[0]) => { - const masterId = input.filters?.master?.documentId?.eq; - return useQuery({ queryFn: () => getServices(input), - queryKey: ['services', 'master', masterId, 'list'], + queryKey: ['services', input], }); }; -export const useServiceQuery = (input: Parameters[0]) => - useQuery({ +export const useServiceQuery = (input: Parameters[0]) => { + return useQuery({ queryFn: () => getService(input), - queryKey: ['services', 'documentId', input.documentId], + queryKey: ['service', input.documentId], }); +}; diff --git a/apps/web/hooks/api/slots.ts b/apps/web/hooks/api/slots.ts index 67066a3..664b120 100644 --- a/apps/web/hooks/api/slots.ts +++ b/apps/web/hooks/api/slots.ts @@ -11,7 +11,7 @@ export const useSlots = (variables: Parameters[0]) => { return useQuery({ queryFn: () => getSlots(variables), - queryKey: ['slots', 'master', masterId, 'list', date.toISOString()], + queryKey: ['slots', { date: date?.toISOString(), masterId }], }); }; @@ -20,7 +20,7 @@ export const useSlotQuery = (variables: Parameters[0]) => { return useQuery({ queryFn: () => getSlot(variables), - queryKey: ['slots', 'get', documentId], + queryKey: ['slot', documentId], }); }; @@ -29,17 +29,15 @@ export const useSlotMutation = ({ }: Pick[0], 'documentId'>) => { const queryClient = useQueryClient(); - function handleOnSuccess() { - queryClient.invalidateQueries({ - queryKey: ['slots', 'get', documentId], - }); - } - return useMutation({ mutationFn: ({ data }: Pick[0], 'data'>) => updateSlot({ data, documentId }), - mutationKey: ['slots', 'update', documentId], - onSuccess: handleOnSuccess, + mutationKey: ['slot', 'update', documentId], + onSuccess: () => { + if (documentId) { + queryClient.invalidateQueries({ queryKey: ['slot', documentId] }); + } + }, }); }; @@ -49,17 +47,17 @@ export const useSlotCreate = ({ date }: { date: Date }) => { const queryClient = useQueryClient(); - function handleOnSuccess() { - queryClient.invalidateQueries({ - queryKey: ['slots', 'master', masterId, 'list', date.toISOString()], - }); - } - return useMutation({ mutationFn: ({ input }: { input: Omit[0]['input'], 'date'> }) => createSlot({ input: { ...input, date } }), - mutationKey: ['slots', 'create', 'date', date.toISOString(), 'master', masterId], - onSuccess: handleOnSuccess, + mutationKey: ['slot', 'create'], + onSuccess: () => { + if (masterId && date) { + queryClient.invalidateQueries({ + queryKey: ['slots', { date: date?.toISOString(), masterId }], + }); + } + }, }); }; @@ -68,18 +66,18 @@ export const useSlotDelete = ({ documentId }: Parameters[0]) const queryClient = useQueryClient(); - function handleOnSuccess() { - const date = slot?.date; - const masterId = slot?.master; - - queryClient.invalidateQueries({ - queryKey: ['slots', 'master', masterId, 'list', date.toISOString()], - }); - } - return useMutation({ mutationFn: () => deleteSlot({ documentId }), - mutationKey: ['slots', 'delete', documentId], - onSuccess: handleOnSuccess, + mutationKey: ['slot', 'delete', documentId], + onSuccess: () => { + const date = slot?.date; + const masterId = slot?.master; + + if (date && masterId) { + queryClient.invalidateQueries({ + queryKey: ['slots', { date: date?.toISOString(), masterId }], + }); + } + }, }); }; -- 2.47.2 From 30001b993ef5dd32261418fe21c181871c006f86 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Fri, 16 May 2025 19:14:00 +0300 Subject: [PATCH 06/12] refactor orders api --- apps/web/actions/api/lib/service.ts | 8 -- apps/web/actions/api/orders.ts | 18 ++++ apps/web/actions/orders.ts | 77 -------------- .../orders/components/back-button.tsx | 2 +- .../orders/components/submit-button.tsx | 12 ++- .../schedule/components/order-card.tsx | 5 +- apps/web/hooks/api/orders.ts | 15 +++ apps/web/hooks/orders/index.ts | 24 ----- packages/graphql/api/index.ts | 5 - packages/graphql/api/order.ts | 21 ---- packages/graphql/api/orders.ts | 100 ++++++++++++++++++ packages/graphql/config/token.ts | 2 +- 12 files changed, 147 insertions(+), 142 deletions(-) create mode 100644 apps/web/actions/api/orders.ts delete mode 100644 apps/web/actions/orders.ts create mode 100644 apps/web/hooks/api/orders.ts delete mode 100644 apps/web/hooks/orders/index.ts delete mode 100644 packages/graphql/api/index.ts delete mode 100644 packages/graphql/api/order.ts create mode 100644 packages/graphql/api/orders.ts diff --git a/apps/web/actions/api/lib/service.ts b/apps/web/actions/api/lib/service.ts index 9f99685..ca34466 100644 --- a/apps/web/actions/api/lib/service.ts +++ b/apps/web/actions/api/lib/service.ts @@ -1,15 +1,7 @@ -/* eslint-disable canonical/id-match */ import { authOptions } from '@/config/auth'; import { type BaseService } from '@repo/graphql/api/base'; import { getServerSession } from 'next-auth'; -export async function _temporaryGetCustomer() { - const session = await getServerSession(authOptions); - if (!session?.user?.telegramId) throw new Error('Unauthorized'); - - return { telegramId: session.user.telegramId }; -} - export function useService(service: T) { return async function () { const session = await getServerSession(authOptions); diff --git a/apps/web/actions/api/orders.ts b/apps/web/actions/api/orders.ts new file mode 100644 index 0000000..286a719 --- /dev/null +++ b/apps/web/actions/api/orders.ts @@ -0,0 +1,18 @@ +'use server'; + +import { useService } from './lib/service'; +import { OrdersService } from '@repo/graphql/api/orders'; + +const getServicesService = useService(OrdersService); + +export async function createOrder(...variables: Parameters) { + const service = await getServicesService(); + + return service.createOrder(...variables); +} + +export async function getOrder(...variables: Parameters) { + const service = await getServicesService(); + + return service.getOrder(...variables); +} diff --git a/apps/web/actions/orders.ts b/apps/web/actions/orders.ts deleted file mode 100644 index 1ed6d25..0000000 --- a/apps/web/actions/orders.ts +++ /dev/null @@ -1,77 +0,0 @@ -/* eslint-disable canonical/id-match */ -'use server'; -// eslint-disable-next-line sonarjs/no-internal-api-use -import type * as ApolloTypes from '../../../packages/graphql/node_modules/@apollo/client/core'; -import { getCustomer, getCustomerMasters } from './api/customers'; -import { _temporaryGetCustomer } from './api/lib/service'; -import { getService } from './api/services'; -import { getSlot } from './api/slots'; -import * as api from '@repo/graphql/api'; -import { Enum_Customer_Role, Enum_Slot_State } from '@repo/graphql/types'; -import { formatDate, formatTime, sumTime } from '@repo/graphql/utils/datetime-format'; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -type FixTypescriptCringe = ApolloTypes.FetchResult; - -export const getOrder = api.getOrder; - -type OrderInput = { - clientId: string; - date: Date; - serviceId: string; - slotId: string; - time: string; -}; - -export async function createOrder(input: OrderInput) { - const variables = await _temporaryGetCustomer(); - const { customer } = await getCustomer(variables); - - if (!customer) { - throw new Error('Missing customer'); - } - - if (!input.slotId) { - throw new Error('Missing slot'); - } - - const { slot } = await getSlot({ documentId: input.slotId }); - - if (slot?.state === Enum_Slot_State.Closed) { - throw new Error('Slot is closed'); - } - - if (customer.role === Enum_Customer_Role.Client) { - if (customer.documentId !== input.clientId) { - throw new Error('Invalid client'); - } - - const masterId = slot?.master?.documentId; - - const masters = await getCustomerMasters(customer); - - if (!masters.customers.some((master) => master?.documentId === masterId)) { - throw new Error('Invalid master'); - } - } - - if ( - customer.role === Enum_Customer_Role.Master && - slot?.master?.documentId !== customer.documentId - ) { - throw new Error('Invalid master'); - } - - const { service } = await getService({ documentId: input.serviceId }); - - const endTime = sumTime(input.time, service?.duration); - const payload = { - client: input.clientId, - date: formatDate(input.date).db(), - services: [input.serviceId], - slot: input.slotId, - time_end: formatTime(endTime).db(), - time_start: formatTime(input.time).db(), - }; - return api.createOrder(payload); -} diff --git a/apps/web/components/orders/components/back-button.tsx b/apps/web/components/orders/components/back-button.tsx index df5f0f7..0253a7a 100644 --- a/apps/web/components/orders/components/back-button.tsx +++ b/apps/web/components/orders/components/back-button.tsx @@ -1,5 +1,5 @@ 'use client'; -import { useOrderCreate } from '@/hooks/orders'; +import { useOrderCreate } from '@/hooks/api/orders'; import { useOrderStore } from '@/stores/order'; import { Button } from '@repo/ui/components/ui/button'; diff --git a/apps/web/components/orders/components/submit-button.tsx b/apps/web/components/orders/components/submit-button.tsx index 35a660f..fa3878b 100644 --- a/apps/web/components/orders/components/submit-button.tsx +++ b/apps/web/components/orders/components/submit-button.tsx @@ -1,5 +1,5 @@ 'use client'; -import { useOrderCreate } from '@/hooks/orders'; +import { useOrderCreate } from '@/hooks/api/orders'; import { useOrderStore } from '@/stores/order'; import { Button } from '@repo/ui/components/ui/button'; import { LoadingSpinner } from '@repo/ui/components/ui/spinner'; @@ -14,7 +14,15 @@ export function SubmitButton() { const handleSubmit = () => { if (isDisabled) return; - createOrder({ clientId, date, serviceId, slotId, time }); + createOrder({ + input: { + client: clientId, + date, + services: [serviceId], + slot: slotId, + time_start: time, + }, + }); }; useEffect(() => { diff --git a/apps/web/components/schedule/components/order-card.tsx b/apps/web/components/schedule/components/order-card.tsx index c75c39f..284366f 100644 --- a/apps/web/components/schedule/components/order-card.tsx +++ b/apps/web/components/schedule/components/order-card.tsx @@ -2,7 +2,7 @@ 'use client'; import { type OrderClient, type OrderComponentProps } from '../types'; import { ReadonlyTimeRange } from './time-range'; -import { useOrderQuery } from '@/hooks/orders'; +import { useOrderQuery } from '@/hooks/api/orders'; import { Enum_Order_State } from '@repo/graphql/types'; import { Avatar, AvatarFallback, AvatarImage } from '@repo/ui/components/ui/avatar'; import { Badge } from '@repo/ui/components/ui/badge'; @@ -10,8 +10,7 @@ import { cn } from '@repo/ui/lib/utils'; import Link from 'next/link'; export function OrderCard({ documentId }: Readonly) { - const { data } = useOrderQuery({ documentId }); - const order = data?.data?.order; + const { data: { order } = {} } = useOrderQuery({ documentId }); if (!order) return null; diff --git a/apps/web/hooks/api/orders.ts b/apps/web/hooks/api/orders.ts new file mode 100644 index 0000000..b25bd15 --- /dev/null +++ b/apps/web/hooks/api/orders.ts @@ -0,0 +1,15 @@ +'use client'; +import { createOrder, getOrder } from '@/actions/api/orders'; +import { useMutation, useQuery } from '@tanstack/react-query'; + +export const useOrderQuery = ({ documentId }: Parameters[0]) => + useQuery({ + queryFn: () => getOrder({ documentId }), + queryKey: ['order', documentId], + }); + +export const useOrderCreate = () => + useMutation({ + mutationFn: createOrder, + mutationKey: ['order', 'create'], + }); diff --git a/apps/web/hooks/orders/index.ts b/apps/web/hooks/orders/index.ts deleted file mode 100644 index bdd0957..0000000 --- a/apps/web/hooks/orders/index.ts +++ /dev/null @@ -1,24 +0,0 @@ -'use client'; -import { createOrder, getOrder } from '@/actions/orders'; -// eslint-disable-next-line sonarjs/no-internal-api-use -import type * as ApolloTypes from '@repo/graphql/node_modules/@apollo/client/core'; -import { useMutation, useQuery } from '@tanstack/react-query'; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -type FixTypescriptCringe = ApolloTypes.FetchResult; - -type Props = { - documentId: string; -}; - -export const useOrderQuery = ({ documentId }: Props) => - useQuery({ - queryFn: () => getOrder({ documentId }), - queryKey: ['orders', 'get', documentId], - }); - -export const useOrderCreate = () => - useMutation({ - mutationFn: createOrder, - mutationKey: ['orders', 'create'], - }); diff --git a/packages/graphql/api/index.ts b/packages/graphql/api/index.ts deleted file mode 100644 index 00a4b07..0000000 --- a/packages/graphql/api/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './auth'; -export * from './customers'; -export * from './order'; -export * from './services'; -export * from './slots'; diff --git a/packages/graphql/api/order.ts b/packages/graphql/api/order.ts deleted file mode 100644 index 80298f7..0000000 --- a/packages/graphql/api/order.ts +++ /dev/null @@ -1,21 +0,0 @@ -'use server'; -import { getClientWithToken } from '../apollo/client'; -import * as GQL from '../types'; - -export async function getOrder(variables: GQL.GetOrderQueryVariables) { - const { query } = await getClientWithToken(); - - return query({ - query: GQL.GetOrderDocument, - variables, - }); -} - -export async function createOrder(input: GQL.CreateOrderMutationVariables['input']) { - const { mutate } = await getClientWithToken(); - - return mutate({ - mutation: GQL.CreateOrderDocument, - variables: { input }, - }); -} diff --git a/packages/graphql/api/orders.ts b/packages/graphql/api/orders.ts new file mode 100644 index 0000000..9f66bc6 --- /dev/null +++ b/packages/graphql/api/orders.ts @@ -0,0 +1,100 @@ +/* eslint-disable canonical/id-match */ +import { getClientWithToken } from '../apollo/client'; +import * as GQL from '../types'; +import { Enum_Customer_Role, Enum_Slot_State } from '../types'; +import { formatDate, formatTime, sumTime } from '../utils/datetime-format'; +import { BaseService } from './base'; +import { CustomersService } from './customers'; +import { ServicesService } from './services'; +import { SlotsService } from './slots'; +import { type VariablesOf } from '@graphql-typed-document-node/core'; + +const ERRORS = { + INVALID_CLIENT: 'Invalid client', + INVALID_MASTER: 'Invalid master', + MISSING_CLIENT: 'Missing client id', + MISSING_SERVICE_ID: 'Missing service id', + MISSING_SERVICES: 'Missing services', + MISSING_SLOT: 'Missing slot id', + MISSING_START_TIME: 'Missing time start', + SLOT_CLOSED: 'Slot is closed', +}; + +export class OrdersService extends BaseService { + async createOrder(variables: { + input: Omit['input'], 'time_end'>; + }) { + if (!variables.input.slot) throw new Error(ERRORS.MISSING_SLOT); + if (!variables.input.client) throw new Error(ERRORS.MISSING_CLIENT); + if (!variables.input.services?.length) throw new Error(ERRORS.MISSING_SERVICES); + if (!variables.input.services[0]) throw new Error(ERRORS.MISSING_SERVICE_ID); + if (!variables.input.time_start) throw new Error(ERRORS.MISSING_START_TIME); + + const customersService = new CustomersService(this.customer); + const slotsService = new SlotsService(this.customer); + const servicesService = new ServicesService(this.customer); + + const { customer } = await customersService.getCustomer(this.customer); + const { slot } = await slotsService.getSlot({ documentId: variables.input.slot }); + + if (slot?.state === Enum_Slot_State.Closed) { + throw new Error(ERRORS.SLOT_CLOSED); + } + + if (customer?.role === Enum_Customer_Role.Client) { + if (customer.documentId !== variables.input.client) { + throw new Error(ERRORS.INVALID_CLIENT); + } + + const masters = await customersService.getCustomerMasters(this.customer); + const masterId = slot?.master?.documentId; + if (!masters.customers.some((master) => master?.documentId === masterId)) { + throw new Error(ERRORS.INVALID_MASTER); + } + } + + if ( + customer?.role === Enum_Customer_Role.Master && + slot?.master?.documentId !== customer.documentId + ) { + throw new Error(ERRORS.INVALID_MASTER); + } + + const { service } = await servicesService.getService({ + documentId: variables.input.services[0], + }); + const endTime = sumTime(variables.input.time_start, service?.duration); + const { mutate } = await getClientWithToken(); + const mutationResult = await mutate({ + mutation: GQL.CreateOrderDocument, + variables: { + input: { + client: variables.input.client, + date: formatDate(variables.input.date).db(), + services: variables.input.services, + slot: variables.input.slot, + time_end: formatTime(endTime).db(), + time_start: formatTime(variables.input.time_start).db(), + }, + }, + }); + + const error = mutationResult.errors?.at(0); + if (error) throw new Error(error.message); + + return mutationResult.data; + } + + async getOrder(variables: VariablesOf) { + const { query } = await getClientWithToken(); + + const result = await query({ + query: GQL.GetOrderDocument, + variables, + }); + + if (result.error) throw new Error(result.error.message); + + return result.data; + } +} diff --git a/packages/graphql/config/token.ts b/packages/graphql/config/token.ts index deb26c2..e61bfc6 100644 --- a/packages/graphql/config/token.ts +++ b/packages/graphql/config/token.ts @@ -1,4 +1,4 @@ -import { login } from '../api'; +import { login } from '../api/auth'; import { isTokenExpired } from '../utils/jwt'; export const token: null | string = null; -- 2.47.2 From c91a188761b3c6c2deef9ddc187c4c1b9a72f802 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Fri, 16 May 2025 19:30:01 +0300 Subject: [PATCH 07/12] typo refactor hooks --- .../components/time-select.tsx | 4 +-- .../components/schedule/day-slot-add-form.tsx | 5 +--- .../components/schedule/day-slots-list.tsx | 6 +++-- apps/web/hooks/api/customers.ts | 4 +-- apps/web/hooks/api/slots.ts | 25 +++++++++++-------- 5 files changed, 23 insertions(+), 21 deletions(-) diff --git a/apps/web/components/orders/components/datetime-select/components/time-select.tsx b/apps/web/components/orders/components/datetime-select/components/time-select.tsx index 034b005..d34b859 100644 --- a/apps/web/components/orders/components/datetime-select/components/time-select.tsx +++ b/apps/web/components/orders/components/datetime-select/components/time-select.tsx @@ -1,6 +1,6 @@ /* eslint-disable canonical/id-match */ 'use client'; -import { useSlots } from '@/hooks/api/slots'; +import { useSlotsQuery } from '@/hooks/api/slots'; import { useOrderStore } from '@/stores/order'; import { Enum_Slot_State, type SlotFieldsFragment } from '@repo/graphql/types'; import { Button } from '@repo/ui/components/ui/button'; @@ -25,7 +25,7 @@ const generateTimeSlots = (slots: SlotFieldsFragment[]): Array<{ slotId: string; export function TimeSelect() { const masterId = useOrderStore((store) => store.masterId); const date = useOrderStore((store) => store.date); - const { data: { slots } = {} } = useSlots({ + const { data: { slots } = {} } = useSlotsQuery({ filters: { date: { eq: date, diff --git a/apps/web/components/schedule/day-slot-add-form.tsx b/apps/web/components/schedule/day-slot-add-form.tsx index df5b64e..82d421a 100644 --- a/apps/web/components/schedule/day-slot-add-form.tsx +++ b/apps/web/components/schedule/day-slot-add-form.tsx @@ -2,7 +2,6 @@ 'use client'; import { EditableTimeRangeForm } from './components/time-range'; import { ScheduleTimeContext, ScheduleTimeContextProvider } from './context'; -import { ScheduleContext } from '@/context/schedule'; import { useSlotCreate } from '@/hooks/api/slots'; import { withContext } from '@/utils/context'; import { Enum_Slot_State } from '@repo/graphql/types'; @@ -13,9 +12,7 @@ import { type FormEvent, use } from 'react'; export const DaySlotAddForm = withContext(ScheduleTimeContextProvider)(function () { const { endTime, resetTime, startTime } = use(ScheduleTimeContext); - const { selectedDate } = use(ScheduleContext); - - const { isPending, mutate: addSlot } = useSlotCreate({ date: selectedDate }); + const { isPending, mutate: addSlot } = useSlotCreate(); const handleSubmit = (event: FormEvent) => { event.preventDefault(); diff --git a/apps/web/components/schedule/day-slots-list.tsx b/apps/web/components/schedule/day-slots-list.tsx index 3f87ff7..a066280 100644 --- a/apps/web/components/schedule/day-slots-list.tsx +++ b/apps/web/components/schedule/day-slots-list.tsx @@ -3,12 +3,14 @@ import { SlotCard } from './components/slot-card'; import { DaySlotAddForm } from './day-slot-add-form'; import { LoadingSpinner } from '@/components/common/spinner'; import { ScheduleContext } from '@/context/schedule'; -import { useSlots } from '@/hooks/api/slots'; +import { useSlotsQuery } from '@/hooks/api/slots'; import { use } from 'react'; export function DaySlotsList() { const { selectedDate } = use(ScheduleContext); - const { data: { slots } = {}, isLoading } = useSlots({ filters: { date: { eq: selectedDate } } }); + const { data: { slots } = {}, isLoading } = useSlotsQuery({ + filters: { date: { eq: selectedDate } }, + }); if (isLoading) return ; diff --git a/apps/web/hooks/api/customers.ts b/apps/web/hooks/api/customers.ts index 5c69a04..6c97848 100644 --- a/apps/web/hooks/api/customers.ts +++ b/apps/web/hooks/api/customers.ts @@ -3,9 +3,9 @@ import { getCustomer, updateCustomer } from '@/actions/api/customers'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useSession } from 'next-auth/react'; -export const useCustomerQuery = (props?: Parameters[0]) => { +export const useCustomerQuery = (variables?: Parameters[0]) => { const { data: session } = useSession(); - const telegramId = props?.telegramId || session?.user?.telegramId; + const telegramId = variables?.telegramId || session?.user?.telegramId; return useQuery({ enabled: Boolean(telegramId), diff --git a/apps/web/hooks/api/slots.ts b/apps/web/hooks/api/slots.ts index 664b120..a216db6 100644 --- a/apps/web/hooks/api/slots.ts +++ b/apps/web/hooks/api/slots.ts @@ -3,7 +3,7 @@ import { useCustomerQuery } from './customers'; import { createSlot, deleteSlot, getSlot, getSlots, updateSlot } from '@/actions/api/slots'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -export const useSlots = (variables: Parameters[0]) => { +export const useSlotsQuery = (variables: Parameters[0]) => { const { data: { customer } = {} } = useCustomerQuery(); const masterId = variables.filters?.master?.documentId?.eq || customer?.documentId; @@ -41,23 +41,22 @@ export const useSlotMutation = ({ }); }; -export const useSlotCreate = ({ date }: { date: Date }) => { +export const useSlotCreate = () => { const { data: { customer } = {} } = useCustomerQuery(); const masterId = customer?.documentId; const queryClient = useQueryClient(); return useMutation({ - mutationFn: ({ input }: { input: Omit[0]['input'], 'date'> }) => - createSlot({ input: { ...input, date } }), + mutationFn: createSlot, mutationKey: ['slot', 'create'], - onSuccess: () => { - if (masterId && date) { - queryClient.invalidateQueries({ - queryKey: ['slots', { date: date?.toISOString(), masterId }], - }); - } - }, + onSuccess: masterId + ? (data) => { + queryClient.invalidateQueries({ + queryKey: ['slots', { date: data?.createSlot?.date?.toISOString(), masterId }], + }); + } + : undefined, }); }; @@ -77,6 +76,10 @@ export const useSlotDelete = ({ documentId }: Parameters[0]) queryClient.invalidateQueries({ queryKey: ['slots', { date: date?.toISOString(), masterId }], }); + + queryClient.invalidateQueries({ + queryKey: ['slot', documentId], + }); } }, }); -- 2.47.2 From 35134e9663c05171f8d0cf0c7b44e92974e13659 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Fri, 16 May 2025 20:06:09 +0300 Subject: [PATCH 08/12] fix telegramId type (number) --- apps/web/app/(auth)/browser/page.tsx | 2 +- apps/web/app/(auth)/telegram/page.tsx | 2 +- apps/web/app/(main)/profile/[telegramId]/page.tsx | 2 +- apps/web/components/profile/types.tsx | 2 +- apps/web/config/auth.ts | 2 +- apps/web/mocks/get-telegram-user.ts | 2 +- apps/web/types/next-auth.d.ts | 4 ++-- packages/graphql/api/base.ts | 2 +- packages/graphql/graphql.config.cjs | 5 ++++- packages/graphql/types/operations.generated.ts | 12 ++++++------ 10 files changed, 19 insertions(+), 16 deletions(-) diff --git a/apps/web/app/(auth)/browser/page.tsx b/apps/web/app/(auth)/browser/page.tsx index a989110..e7a5d2e 100644 --- a/apps/web/app/(auth)/browser/page.tsx +++ b/apps/web/app/(auth)/browser/page.tsx @@ -21,7 +21,7 @@ export default function Auth() { signIn('telegram', { callbackUrl: '/profile', redirect: false, - telegramId: String(user?.id), + telegramId: user?.id, }); }); } diff --git a/apps/web/app/(auth)/telegram/page.tsx b/apps/web/app/(auth)/telegram/page.tsx index 36e24d1..9d0c08f 100644 --- a/apps/web/app/(auth)/telegram/page.tsx +++ b/apps/web/app/(auth)/telegram/page.tsx @@ -28,7 +28,7 @@ function useAuth() { signIn('telegram', { callbackUrl: '/profile', redirect: false, - telegramId: String(initDataUser.id), + telegramId: initDataUser.id, }).then(() => redirect('/profile')); } }, [initDataUser?.id, status]); diff --git a/apps/web/app/(main)/profile/[telegramId]/page.tsx b/apps/web/app/(main)/profile/[telegramId]/page.tsx index dac16f6..8f1e663 100644 --- a/apps/web/app/(main)/profile/[telegramId]/page.tsx +++ b/apps/web/app/(main)/profile/[telegramId]/page.tsx @@ -7,7 +7,7 @@ type Props = { params: Promise<{ telegramId: string }> }; export default async function ProfilePage(props: Readonly) { const parameters = await props.params; - const { telegramId } = parameters; + const telegramId = Number.parseInt(parameters.telegramId, 10); const queryClient = new QueryClient(); diff --git a/apps/web/components/profile/types.tsx b/apps/web/components/profile/types.tsx index 974e69f..3090f9a 100644 --- a/apps/web/components/profile/types.tsx +++ b/apps/web/components/profile/types.tsx @@ -1,3 +1,3 @@ export type ProfileProps = { - readonly telegramId?: string; + readonly telegramId?: number; }; diff --git a/apps/web/config/auth.ts b/apps/web/config/auth.ts index 3846dcb..d7e9522 100644 --- a/apps/web/config/auth.ts +++ b/apps/web/config/auth.ts @@ -12,7 +12,7 @@ export const authOptions: AuthOptions = { }, async session({ session, token }) { if (token?.id && session?.user) { - session.user.telegramId = token.id as string; + session.user.telegramId = token.id as number; } return session; diff --git a/apps/web/mocks/get-telegram-user.ts b/apps/web/mocks/get-telegram-user.ts index 1516fd5..f05cb73 100644 --- a/apps/web/mocks/get-telegram-user.ts +++ b/apps/web/mocks/get-telegram-user.ts @@ -5,7 +5,7 @@ import { env } from '@/config/env'; export async function getTelegramUser() { if (process.env.NODE_ENV !== 'production') return { - id: env.__DEV_TELEGRAM_ID, + id: Number.parseInt(env.__DEV_TELEGRAM_ID, 10), }; return null; diff --git a/apps/web/types/next-auth.d.ts b/apps/web/types/next-auth.d.ts index 9bf0e89..4dcef7c 100644 --- a/apps/web/types/next-auth.d.ts +++ b/apps/web/types/next-auth.d.ts @@ -4,11 +4,11 @@ import { type DefaultSession } from 'next-auth'; declare module 'next-auth' { interface Session extends DefaultSession { user?: { - telegramId?: null | string; + telegramId?: null | number; }; } interface User extends DefaultUser { - telegramId?: null | string; + telegramId?: null | number; } } diff --git a/packages/graphql/api/base.ts b/packages/graphql/api/base.ts index c76043a..f2531bd 100644 --- a/packages/graphql/api/base.ts +++ b/packages/graphql/api/base.ts @@ -1,5 +1,5 @@ type CustomerProfile = { - telegramId: string; + telegramId: number; }; export class BaseService { diff --git a/packages/graphql/graphql.config.cjs b/packages/graphql/graphql.config.cjs index c1d69b9..f575ce9 100644 --- a/packages/graphql/graphql.config.cjs +++ b/packages/graphql/graphql.config.cjs @@ -5,9 +5,12 @@ module.exports = { './types/operations.generated.ts': { config: { avoidOptionals: false, + maybeValue: 'T | null | undefined', onlyOperationTypes: true, + scalars: { + Long: 'number', + }, useTypeImports: true, - maybeValue: 'T | null | undefined' }, plugins: ['typescript', 'typescript-operations', 'typed-document-node'], }, diff --git a/packages/graphql/types/operations.generated.ts b/packages/graphql/types/operations.generated.ts index 13ee904..587b88c 100644 --- a/packages/graphql/types/operations.generated.ts +++ b/packages/graphql/types/operations.generated.ts @@ -16,7 +16,7 @@ export type Scalars = { Date: { input: any; output: any; } DateTime: { input: any; output: any; } JSON: { input: any; output: any; } - Long: { input: any; output: any; } + Long: { input: number; output: number; } Time: { input: any; output: any; } }; @@ -651,7 +651,7 @@ export type LoginMutationVariables = Exact<{ export type LoginMutation = { __typename?: 'Mutation', login: { __typename?: 'UsersPermissionsLoginPayload', jwt?: string | null | undefined } }; -export type CustomerFieldsFragment = { __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: any | null | undefined }; +export type CustomerFieldsFragment = { __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined }; export type CreateCustomerMutationVariables = Exact<{ name: Scalars['String']['input']; @@ -668,7 +668,7 @@ export type GetCustomerQueryVariables = Exact<{ }>; -export type GetCustomerQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: any | null | undefined } | null | undefined> }; +export type GetCustomerQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined> }; export type GetCustomerMastersQueryVariables = Exact<{ phone?: InputMaybe; @@ -676,7 +676,7 @@ export type GetCustomerMastersQueryVariables = Exact<{ }>; -export type GetCustomerMastersQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', documentId: string, masters: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: any | null | undefined } | null | undefined> } | null | undefined> }; +export type GetCustomerMastersQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', documentId: string, masters: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined> } | null | undefined> }; export type GetCustomerClientsQueryVariables = Exact<{ phone?: InputMaybe; @@ -684,7 +684,7 @@ export type GetCustomerClientsQueryVariables = Exact<{ }>; -export type GetCustomerClientsQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', documentId: string, clients: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: any | null | undefined } | null | undefined> } | null | undefined> }; +export type GetCustomerClientsQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', documentId: string, clients: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined> } | null | undefined> }; export type UpdateCustomerMutationVariables = Exact<{ documentId: Scalars['ID']['input']; @@ -692,7 +692,7 @@ export type UpdateCustomerMutationVariables = Exact<{ }>; -export type UpdateCustomerMutation = { __typename?: 'Mutation', updateCustomer?: { __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: any | null | undefined } | null | undefined }; +export type UpdateCustomerMutation = { __typename?: 'Mutation', updateCustomer?: { __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined }; export type OrderFieldsFragment = { __typename?: 'Order', documentId: string, time_start?: any | null | undefined, time_end?: any | null | undefined, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name?: string | null | undefined } | null | undefined>, client?: { __typename?: 'Customer', name: string, documentId: string, photoUrl?: string | null | undefined } | null | undefined }; -- 2.47.2 From de05bc8cb6d9fc8fb25e62d736ac96af1c84441d Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Sat, 17 May 2025 12:34:02 +0300 Subject: [PATCH 09/12] fix bot with new api --- apps/bot/src/index.ts | 79 ++++++++++------- apps/web/actions/api/customers.ts | 20 ++--- packages/graphql/api/customers.ts | 88 +++++++------------ packages/graphql/operations/customer.graphql | 12 ++- .../graphql/types/operations.generated.ts | 5 +- pnpm-lock.yaml | 2 +- 6 files changed, 99 insertions(+), 107 deletions(-) diff --git a/apps/bot/src/index.ts b/apps/bot/src/index.ts index 2e9c648..01c3d3e 100644 --- a/apps/bot/src/index.ts +++ b/apps/bot/src/index.ts @@ -3,12 +3,7 @@ import { env as environment } from './config/env'; import { commandsList, KEYBOARD_REMOVE, KEYBOARD_SHARE_PHONE, MESSAGE_NOT_MASTER } from './message'; import { normalizePhoneNumber } from './utils/phone'; -import { - createOrUpdateUser, - getCustomer, - updateCustomerMaster, - updateCustomerProfile, -} from '@repo/graphql/api'; +import { CustomersService } from '@repo/graphql/api/customers'; import { Enum_Customer_Role } from '@repo/graphql/types'; import { Telegraf } from 'telegraf'; import { message } from 'telegraf/filters'; @@ -16,8 +11,10 @@ import { message } from 'telegraf/filters'; const bot = new Telegraf(environment.BOT_TOKEN); bot.start(async (context) => { - const data = await getCustomer({ telegramId: context.from.id }); - const customer = data?.data?.customers?.at(0); + const telegramId = context.from.id; + + const customerService = new CustomersService({ telegramId }); + const { customer } = await customerService.getCustomer({ telegramId }); if (customer) { return context.reply( @@ -34,8 +31,10 @@ bot.start(async (context) => { }); bot.command('addcontact', async (context) => { - const data = await getCustomer({ telegramId: context.from.id }); - const customer = data?.data?.customers?.at(0); + const telegramId = context.from.id; + + const customerService = new CustomersService({ telegramId }); + const { customer } = await customerService.getCustomer({ telegramId }); if (!customer) { return context.reply( @@ -52,8 +51,10 @@ bot.command('addcontact', async (context) => { }); bot.command('becomemaster', async (context) => { - const data = await getCustomer({ telegramId: context.from.id }); - const customer = data?.data?.customers?.at(0); + const telegramId = context.from.id; + + const customerService = new CustomersService({ telegramId }); + const { customer } = await customerService.getCustomer({ telegramId }); if (!customer) { return context.reply('Сначала поделитесь своим номером телефона.', KEYBOARD_SHARE_PHONE); @@ -63,12 +64,15 @@ bot.command('becomemaster', async (context) => { return context.reply('Вы уже являетесь мастером.'); } - const response = await updateCustomerProfile({ - data: { role: Enum_Customer_Role.Master }, - documentId: customer.documentId, - }).catch((error) => { - context.reply('Произошла ошибка.\n' + error); - }); + const response = await customerService + .updateCustomer({ + data: { + role: Enum_Customer_Role.Master, + }, + }) + .catch((error) => { + context.reply('Произошла ошибка.\n' + error); + }); if (response) { return context.reply('Вы стали мастером'); @@ -76,8 +80,10 @@ bot.command('becomemaster', async (context) => { }); bot.on(message('contact'), async (context) => { - const data = await getCustomer({ telegramId: context.from.id }); - const customer = data?.data?.customers?.at(0); + const telegramId = context.from.id; + + const customerService = new CustomersService({ telegramId }); + const { customer } = await customerService.getCustomer({ telegramId }); const isRegistration = !customer; @@ -86,13 +92,15 @@ bot.on(message('contact'), async (context) => { const phone = normalizePhoneNumber(contact.phone_number); if (isRegistration) { - const response = await createOrUpdateUser({ - name, - phone, - telegramId: context.from.id, - }).catch((error) => { - context.reply('Произошла ошибка.\n' + error); - }); + const response = await customerService + .createCustomer({ + name, + phone, + telegramId: context.from.id, + }) + .catch((error) => { + context.reply('Произошла ошибка.\n' + error); + }); if (response) { return context.reply( @@ -107,12 +115,19 @@ bot.on(message('contact'), async (context) => { } try { - await createOrUpdateUser({ name, phone }); + const createCustomerResult = await customerService.createCustomer({ name, phone }); - await updateCustomerMaster({ - masterId: customer.documentId, - operation: 'add', - phone, + const documentId = createCustomerResult?.createCustomer?.documentId; + + if (!documentId) { + throw new Error('Customer not created'); + } + + const masters = [customer.documentId]; + + await customerService.addMasters({ + data: { masters }, + documentId, }); return context.reply( diff --git a/apps/web/actions/api/customers.ts b/apps/web/actions/api/customers.ts index 85e3a1c..f63f642 100644 --- a/apps/web/actions/api/customers.ts +++ b/apps/web/actions/api/customers.ts @@ -5,12 +5,16 @@ import { CustomersService } from '@repo/graphql/api/customers'; const getService = useService(CustomersService); -export async function createOrUpdateCustomer( - ...variables: Parameters -) { +export async function addMasters(...variables: Parameters) { const service = await getService(); - return service.createOrUpdateCustomer(...variables); + return service.addMasters(...variables); +} + +export async function createCustomer(...variables: Parameters) { + const service = await getService(); + + return service.createCustomer(...variables); } export async function getCustomer(...variables: Parameters) { @@ -40,11 +44,3 @@ export async function updateCustomer(...variables: Parameters -) { - const service = await getService(); - - return service.updateCustomerMaster(...variables); -} diff --git a/packages/graphql/api/customers.ts b/packages/graphql/api/customers.ts index b63f0f7..a99c3b3 100644 --- a/packages/graphql/api/customers.ts +++ b/packages/graphql/api/customers.ts @@ -4,28 +4,42 @@ import { BaseService } from './base'; import { type VariablesOf } from '@graphql-typed-document-node/core'; export class CustomersService extends BaseService { - async createOrUpdateCustomer(variables: VariablesOf) { - if (!variables.phone && !variables.telegramId) throw new Error('Missing phone and telegramId'); + async addMasters(variables: VariablesOf) { + const newMasterIds = variables.data.masters; - const { customer } = await this.getCustomer(variables); + const { mutate, query } = await getClientWithToken(); + const getCustomerMastersResult = await query({ + query: GQL.GetCustomerMastersDocument, + variables, + }); + const existingMasterIds = getCustomerMastersResult?.data?.customers + ?.at(0) + ?.masters.map((x) => x?.documentId); + + const newMastersIds = [...new Set([...(existingMasterIds || []), ...(newMasterIds || [])])]; + + const mutationResult = await mutate({ + mutation: GQL.UpdateCustomerDocument, + variables: { + data: { masters: newMastersIds }, + documentId: variables.documentId, + }, + }); + + const error = mutationResult.errors?.at(0); + if (error) throw new Error(error.message); + + return mutationResult.data; + } + + async createCustomer(variables: VariablesOf) { const { mutate } = await getClientWithToken(); - let mutationResult; - if (customer && customer.phone === variables.phone) { - mutationResult = await mutate({ - mutation: GQL.UpdateCustomerDocument, - variables: { - data: { ...variables }, - documentId: customer.documentId, - }, - }); - } else { - mutationResult = await mutate({ - mutation: GQL.CreateCustomerDocument, - variables, - }); - } + const mutationResult = await mutate({ + mutation: GQL.CreateCustomerDocument, + variables, + }); const error = mutationResult.errors?.at(0); if (error) throw new Error(error.message); @@ -99,42 +113,4 @@ export class CustomersService extends BaseService { return mutationResult.data; } - - async updateCustomerMaster( - variables: VariablesOf & { - masterId: GQL.Scalars['ID']['input']; - operation: 'add' | 'remove'; - }, - ) { - if (!variables.phone && !variables.telegramId) throw new Error('Missing phone and telegramId'); - - const { query } = await getClientWithToken(); - - const response = await query({ query: GQL.GetCustomerMastersDocument, variables }); - - if (response.error) throw new Error(response.error.message); - - const customer = response?.data?.customers?.at(0); - if (!customer) throw new Error('Customer not found'); - - let newMastersIds = customer.masters.map((x) => x?.documentId); - - switch (variables.operation) { - case 'add': - if (newMastersIds.includes(variables.masterId)) break; - newMastersIds = [...newMastersIds, variables.masterId]; - break; - case 'remove': - newMastersIds = newMastersIds.filter((x) => x !== variables.masterId); - break; - default: - break; - } - - return this.updateCustomer({ - data: { - masters: newMastersIds, - }, - }); - } } diff --git a/packages/graphql/operations/customer.graphql b/packages/graphql/operations/customer.graphql index abfcb9c..f1b30e3 100644 --- a/packages/graphql/operations/customer.graphql +++ b/packages/graphql/operations/customer.graphql @@ -20,11 +20,15 @@ query GetCustomer($phone: String, $telegramId: Long) { } } -query GetCustomerMasters($phone: String, $telegramId: Long) { +query GetCustomerMasters($phone: String, $telegramId: Long, $documentId: ID) { customers( filters: { - or: [{ phone: { eq: $phone } }, { telegramId: { eq: $telegramId } }] - and: [{ active: { eq: true } }] + or: [ + { phone: { eq: $phone } } + { telegramId: { eq: $telegramId } } + { documentId: { eq: $documentId } } + ] + # and: [{ active: { eq: true } }] } ) { documentId @@ -38,7 +42,7 @@ query GetCustomerClients($phone: String, $telegramId: Long) { customers( filters: { or: [{ phone: { eq: $phone } }, { telegramId: { eq: $telegramId } }] - and: [{ active: { eq: true } }] + # and: [{ active: { eq: true } }] } ) { documentId diff --git a/packages/graphql/types/operations.generated.ts b/packages/graphql/types/operations.generated.ts index 587b88c..834059b 100644 --- a/packages/graphql/types/operations.generated.ts +++ b/packages/graphql/types/operations.generated.ts @@ -673,6 +673,7 @@ export type GetCustomerQuery = { __typename?: 'Query', customers: Array<{ __type export type GetCustomerMastersQueryVariables = Exact<{ phone?: InputMaybe; telegramId?: InputMaybe; + documentId?: InputMaybe; }>; @@ -772,8 +773,8 @@ export const RegisterDocument = {"kind":"Document","definitions":[{"kind":"Opera export const LoginDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"Login"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"identifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"password"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"identifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identifier"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"password"},"value":{"kind":"Variable","name":{"kind":"Name","value":"password"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"jwt"}}]}}]}}]} as unknown as DocumentNode; export const CreateCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"role"},"value":{"kind":"EnumValue","value":"client"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}}]}}]}}]} as unknown as DocumentNode; export const GetCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; -export const GetCustomerMastersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomerMasters"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"and"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"active"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"BooleanValue","value":true}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"masters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; -export const GetCustomerClientsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomerClients"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"and"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"active"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"BooleanValue","value":true}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"clients"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; +export const GetCustomerMastersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomerMasters"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"documentId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"masters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; +export const GetCustomerClientsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomerClients"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"clients"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; export const UpdateCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CustomerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; export const GetOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"order"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OrderFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Order"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"time_start"}},{"kind":"Field","name":{"kind":"Name","value":"time_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"order_number"}},{"kind":"Field","name":{"kind":"Name","value":"services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}}]}}]}}]} as unknown as DocumentNode; export const CreateOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOrder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"OrderInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOrder"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OrderFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Order"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"time_start"}},{"kind":"Field","name":{"kind":"Name","value":"time_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"order_number"}},{"kind":"Field","name":{"kind":"Name","value":"services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}}]}}]}}]} as unknown as DocumentNode; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index afc67bc..2d3f899 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -151,7 +151,7 @@ importers: specifier: ^2.0.19 version: 2.0.19(@types/react@19.1.2)(react@19.1.0) dayjs: - specifier: ^1.11.13 + specifier: 'catalog:' version: 1.11.13 graphql: specifier: 'catalog:' -- 2.47.2 From 75b85a88e5afce248eca30f03522b68631c03b39 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Sat, 17 May 2025 12:36:45 +0300 Subject: [PATCH 10/12] rename customers masters & clients query --- apps/web/actions/api/customers.ts | 20 ++++++++----------- apps/web/hooks/api/contacts/query.ts | 10 +++++----- packages/graphql/api/customers.ts | 14 ++++++------- packages/graphql/api/orders.ts | 2 +- packages/graphql/operations/customer.graphql | 4 ++-- .../graphql/types/operations.generated.ts | 12 +++++------ 6 files changed, 29 insertions(+), 33 deletions(-) diff --git a/apps/web/actions/api/customers.ts b/apps/web/actions/api/customers.ts index f63f642..9ea6faf 100644 --- a/apps/web/actions/api/customers.ts +++ b/apps/web/actions/api/customers.ts @@ -17,26 +17,22 @@ export async function createCustomer(...variables: Parameters) { + const service = await getService(); + + return service.getClients(...variables); +} + export async function getCustomer(...variables: Parameters) { const service = await getService(); return service.getCustomer(...variables); } -export async function getCustomerClients( - ...variables: Parameters -) { +export async function getMasters(...variables: Parameters) { const service = await getService(); - return service.getCustomerClients(...variables); -} - -export async function getCustomerMasters( - ...variables: Parameters -) { - const service = await getService(); - - return service.getCustomerMasters(...variables); + return service.getMasters(...variables); } export async function updateCustomer(...variables: Parameters) { diff --git a/apps/web/hooks/api/contacts/query.ts b/apps/web/hooks/api/contacts/query.ts index 935500d..6c8134f 100644 --- a/apps/web/hooks/api/contacts/query.ts +++ b/apps/web/hooks/api/contacts/query.ts @@ -1,25 +1,25 @@ -import { getCustomerClients, getCustomerMasters } from '@/actions/api/customers'; +import { getClients, getMasters } from '@/actions/api/customers'; import { useQuery } from '@tanstack/react-query'; import { useSession } from 'next-auth/react'; -export const useClientsQuery = (props?: Parameters[0]) => { +export const useClientsQuery = (props?: Parameters[0]) => { const { data: session } = useSession(); const telegramId = props?.telegramId || session?.user?.telegramId; return useQuery({ enabled: false, - queryFn: () => getCustomerClients({ telegramId }), + queryFn: () => getClients({ telegramId }), queryKey: ['customer', 'telegramId', telegramId, 'clients'], }); }; -export const useMastersQuery = (props?: Parameters[0]) => { +export const useMastersQuery = (props?: Parameters[0]) => { const { data: session } = useSession(); const telegramId = props?.telegramId || session?.user?.telegramId; return useQuery({ enabled: false, - queryFn: () => getCustomerMasters({ telegramId }), + queryFn: () => getMasters({ telegramId }), queryKey: ['customer', 'telegramId', telegramId, 'masters'], }); }; diff --git a/packages/graphql/api/customers.ts b/packages/graphql/api/customers.ts index a99c3b3..802c732 100644 --- a/packages/graphql/api/customers.ts +++ b/packages/graphql/api/customers.ts @@ -8,12 +8,12 @@ export class CustomersService extends BaseService { const newMasterIds = variables.data.masters; const { mutate, query } = await getClientWithToken(); - const getCustomerMastersResult = await query({ - query: GQL.GetCustomerMastersDocument, + const getMastersResult = await query({ + query: GQL.GetMastersDocument, variables, }); - const existingMasterIds = getCustomerMastersResult?.data?.customers + const existingMasterIds = getMastersResult?.data?.customers ?.at(0) ?.masters.map((x) => x?.documentId); @@ -62,11 +62,11 @@ export class CustomersService extends BaseService { return { customer }; } - async getCustomerClients(variables?: VariablesOf) { + async getClients(variables?: VariablesOf) { const { query } = await getClientWithToken(); const result = await query({ - query: GQL.GetCustomerClientsDocument, + query: GQL.GetClientsDocument, variables: { telegramId: variables?.telegramId || this.customer.telegramId, }, @@ -77,11 +77,11 @@ export class CustomersService extends BaseService { return result.data; } - async getCustomerMasters(variables?: VariablesOf) { + async getMasters(variables?: VariablesOf) { const { query } = await getClientWithToken(); const result = await query({ - query: GQL.GetCustomerMastersDocument, + query: GQL.GetMastersDocument, variables: { telegramId: variables?.telegramId || this.customer.telegramId, }, diff --git a/packages/graphql/api/orders.ts b/packages/graphql/api/orders.ts index 9f66bc6..dfe002b 100644 --- a/packages/graphql/api/orders.ts +++ b/packages/graphql/api/orders.ts @@ -46,7 +46,7 @@ export class OrdersService extends BaseService { throw new Error(ERRORS.INVALID_CLIENT); } - const masters = await customersService.getCustomerMasters(this.customer); + const masters = await customersService.getMasters(this.customer); const masterId = slot?.master?.documentId; if (!masters.customers.some((master) => master?.documentId === masterId)) { throw new Error(ERRORS.INVALID_MASTER); diff --git a/packages/graphql/operations/customer.graphql b/packages/graphql/operations/customer.graphql index f1b30e3..d45f566 100644 --- a/packages/graphql/operations/customer.graphql +++ b/packages/graphql/operations/customer.graphql @@ -20,7 +20,7 @@ query GetCustomer($phone: String, $telegramId: Long) { } } -query GetCustomerMasters($phone: String, $telegramId: Long, $documentId: ID) { +query GetMasters($phone: String, $telegramId: Long, $documentId: ID) { customers( filters: { or: [ @@ -38,7 +38,7 @@ query GetCustomerMasters($phone: String, $telegramId: Long, $documentId: ID) { } } -query GetCustomerClients($phone: String, $telegramId: Long) { +query GetClients($phone: String, $telegramId: Long) { customers( filters: { or: [{ phone: { eq: $phone } }, { telegramId: { eq: $telegramId } }] diff --git a/packages/graphql/types/operations.generated.ts b/packages/graphql/types/operations.generated.ts index 834059b..15dbf92 100644 --- a/packages/graphql/types/operations.generated.ts +++ b/packages/graphql/types/operations.generated.ts @@ -670,22 +670,22 @@ export type GetCustomerQueryVariables = Exact<{ export type GetCustomerQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined> }; -export type GetCustomerMastersQueryVariables = Exact<{ +export type GetMastersQueryVariables = Exact<{ phone?: InputMaybe; telegramId?: InputMaybe; documentId?: InputMaybe; }>; -export type GetCustomerMastersQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', documentId: string, masters: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined> } | null | undefined> }; +export type GetMastersQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', documentId: string, masters: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined> } | null | undefined> }; -export type GetCustomerClientsQueryVariables = Exact<{ +export type GetClientsQueryVariables = Exact<{ phone?: InputMaybe; telegramId?: InputMaybe; }>; -export type GetCustomerClientsQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', documentId: string, clients: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined> } | null | undefined> }; +export type GetClientsQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', documentId: string, clients: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined> } | null | undefined> }; export type UpdateCustomerMutationVariables = Exact<{ documentId: Scalars['ID']['input']; @@ -773,8 +773,8 @@ export const RegisterDocument = {"kind":"Document","definitions":[{"kind":"Opera export const LoginDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"Login"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"identifier"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"password"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"login"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"identifier"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identifier"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"password"},"value":{"kind":"Variable","name":{"kind":"Name","value":"password"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"jwt"}}]}}]}}]} as unknown as DocumentNode; export const CreateCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"name"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"name"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"role"},"value":{"kind":"EnumValue","value":"client"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}}]}}]}}]} as unknown as DocumentNode; export const GetCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; -export const GetCustomerMastersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomerMasters"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"documentId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"masters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; -export const GetCustomerClientsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomerClients"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"clients"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; +export const GetMastersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMasters"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"documentId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"masters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; +export const GetClientsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetClients"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"clients"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; export const UpdateCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CustomerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode; export const GetOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"order"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OrderFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Order"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"time_start"}},{"kind":"Field","name":{"kind":"Name","value":"time_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"order_number"}},{"kind":"Field","name":{"kind":"Name","value":"services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}}]}}]}}]} as unknown as DocumentNode; export const CreateOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateOrder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"OrderInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createOrder"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OrderFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Order"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"time_start"}},{"kind":"Field","name":{"kind":"Name","value":"time_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"order_number"}},{"kind":"Field","name":{"kind":"Name","value":"services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}}]}}]}}]} as unknown as DocumentNode; -- 2.47.2 From 7eac84cafb042e55be8e2cbd65515ffc806d6a64 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Sat, 17 May 2025 12:43:50 +0300 Subject: [PATCH 11/12] fix useClientsQuery & useMastersQuery query --- apps/web/hooks/api/contacts/query.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/web/hooks/api/contacts/query.ts b/apps/web/hooks/api/contacts/query.ts index 6c8134f..afe8e97 100644 --- a/apps/web/hooks/api/contacts/query.ts +++ b/apps/web/hooks/api/contacts/query.ts @@ -7,7 +7,6 @@ export const useClientsQuery = (props?: Parameters[0]) => { const telegramId = props?.telegramId || session?.user?.telegramId; return useQuery({ - enabled: false, queryFn: () => getClients({ telegramId }), queryKey: ['customer', 'telegramId', telegramId, 'clients'], }); @@ -18,7 +17,6 @@ export const useMastersQuery = (props?: Parameters[0]) => { const telegramId = props?.telegramId || session?.user?.telegramId; return useQuery({ - enabled: false, queryFn: () => getMasters({ telegramId }), queryKey: ['customer', 'telegramId', telegramId, 'masters'], }); -- 2.47.2 From 5d1d79b24156ad6c2b52f5e847ef08c0b9a74288 Mon Sep 17 00:00:00 2001 From: vchikalkin Date: Sat, 17 May 2025 12:46:00 +0300 Subject: [PATCH 12/12] new line after 'use client' & 'use server' directives --- apps/web/actions/api/slots.ts | 1 + apps/web/actions/session.ts | 1 + apps/web/app/(auth)/browser/page.tsx | 1 + apps/web/app/(auth)/page.tsx | 1 + apps/web/app/(auth)/telegram/page.tsx | 1 + apps/web/components/auth/update-profile.tsx | 1 + apps/web/components/contacts/contacts-list.tsx | 1 + apps/web/components/contacts/dropdown-filter.tsx | 1 + apps/web/components/navigation/bottom-nav.tsx | 1 + apps/web/components/navigation/components/back-button.tsx | 1 + apps/web/components/navigation/components/nav-button.tsx | 1 + apps/web/components/navigation/header.tsx | 1 + apps/web/components/orders/components/back-button.tsx | 1 + apps/web/components/orders/components/contacts-grid/index.tsx | 1 + .../orders/components/datetime-select/components/date-select.tsx | 1 + .../orders/components/datetime-select/components/time-select.tsx | 1 + apps/web/components/orders/components/next-button.tsx | 1 + apps/web/components/orders/components/result.tsx | 1 + apps/web/components/orders/components/service-select.tsx | 1 + apps/web/components/orders/components/submit-button.tsx | 1 + apps/web/components/orders/order-form.tsx | 1 + apps/web/components/profile/components/checkbox-field.tsx | 1 + apps/web/components/profile/components/text-field.tsx | 1 + apps/web/components/profile/data-card.tsx | 1 + apps/web/components/profile/links-card.tsx | 1 + apps/web/components/profile/person-card.tsx | 1 + apps/web/components/schedule/calendar.tsx | 1 + apps/web/components/schedule/components/order-card.tsx | 1 + apps/web/components/schedule/components/slot-card.tsx | 1 + apps/web/components/schedule/components/slot-date.tsx | 1 + apps/web/components/schedule/components/slot-time.tsx | 1 + apps/web/components/schedule/components/time-range.tsx | 1 + apps/web/components/schedule/context/index.tsx | 1 + apps/web/components/schedule/day-slot-add-form.tsx | 1 + apps/web/components/schedule/day-slots-list.tsx | 1 + apps/web/components/schedule/slot-buttons.tsx | 1 + apps/web/components/schedule/slot-datetime.tsx | 1 + apps/web/components/schedule/slot-orders-list.tsx | 1 + apps/web/context/contacts-filter.tsx | 1 + apps/web/context/schedule.tsx | 1 + apps/web/hooks/api/contacts/use-customer-contacts.ts | 1 + apps/web/hooks/api/customers.ts | 1 + apps/web/hooks/api/orders.ts | 1 + apps/web/hooks/api/services.ts | 1 + apps/web/hooks/api/slots.ts | 1 + apps/web/providers/auth.tsx | 1 + apps/web/providers/telegram.tsx | 1 + apps/web/providers/theme-provider.tsx | 1 + apps/web/stores/order/context.tsx | 1 + apps/web/stores/order/hooks.tsx | 1 + packages/ui/src/components/ui/avatar.tsx | 1 + packages/ui/src/components/ui/calendar.tsx | 1 + 52 files changed, 52 insertions(+) diff --git a/apps/web/actions/api/slots.ts b/apps/web/actions/api/slots.ts index 2e0c3b5..eba96cd 100644 --- a/apps/web/actions/api/slots.ts +++ b/apps/web/actions/api/slots.ts @@ -1,4 +1,5 @@ 'use server'; + import { useService } from './lib/service'; import { SlotsService } from '@repo/graphql/api/slots'; diff --git a/apps/web/actions/session.ts b/apps/web/actions/session.ts index 7ef1c33..e3f57b7 100644 --- a/apps/web/actions/session.ts +++ b/apps/web/actions/session.ts @@ -1,4 +1,5 @@ 'use server'; + import { authOptions } from '@/config/auth'; import { getServerSession } from 'next-auth/next'; diff --git a/apps/web/app/(auth)/browser/page.tsx b/apps/web/app/(auth)/browser/page.tsx index e7a5d2e..99666e7 100644 --- a/apps/web/app/(auth)/browser/page.tsx +++ b/apps/web/app/(auth)/browser/page.tsx @@ -1,5 +1,6 @@ /* eslint-disable promise/prefer-await-to-then */ 'use client'; + import { getTelegramUser } from '@/mocks/get-telegram-user'; import { LoadingSpinner } from '@repo/ui/components/ui/spinner'; import { signIn, useSession } from 'next-auth/react'; diff --git a/apps/web/app/(auth)/page.tsx b/apps/web/app/(auth)/page.tsx index 82767ed..2276c75 100644 --- a/apps/web/app/(auth)/page.tsx +++ b/apps/web/app/(auth)/page.tsx @@ -1,4 +1,5 @@ 'use client'; + import { useClientOnce } from '@/hooks/telegram'; import { isTMA } from '@telegram-apps/sdk-react'; import { redirect } from 'next/navigation'; diff --git a/apps/web/app/(auth)/telegram/page.tsx b/apps/web/app/(auth)/telegram/page.tsx index 9d0c08f..fbfcbd2 100644 --- a/apps/web/app/(auth)/telegram/page.tsx +++ b/apps/web/app/(auth)/telegram/page.tsx @@ -1,5 +1,6 @@ /* eslint-disable promise/prefer-await-to-then */ 'use client'; + import { initData, isMiniAppDark, useSignal } from '@telegram-apps/sdk-react'; import { signIn, useSession } from 'next-auth/react'; import { useTheme } from 'next-themes'; diff --git a/apps/web/components/auth/update-profile.tsx b/apps/web/components/auth/update-profile.tsx index 2c110ca..be66aef 100644 --- a/apps/web/components/auth/update-profile.tsx +++ b/apps/web/components/auth/update-profile.tsx @@ -1,4 +1,5 @@ 'use client'; + import { useCustomerMutation } from '@/hooks/api/customers'; import { initData, useSignal } from '@telegram-apps/sdk-react'; import { useEffect, useState } from 'react'; diff --git a/apps/web/components/contacts/contacts-list.tsx b/apps/web/components/contacts/contacts-list.tsx index 2bba376..748424b 100644 --- a/apps/web/components/contacts/contacts-list.tsx +++ b/apps/web/components/contacts/contacts-list.tsx @@ -1,4 +1,5 @@ 'use client'; + import { LoadingSpinner } from '../common/spinner'; import { useCustomerContacts } from '@/hooks/api/contacts'; import * as GQL from '@repo/graphql/types'; diff --git a/apps/web/components/contacts/dropdown-filter.tsx b/apps/web/components/contacts/dropdown-filter.tsx index 62645f9..698d99a 100644 --- a/apps/web/components/contacts/dropdown-filter.tsx +++ b/apps/web/components/contacts/dropdown-filter.tsx @@ -1,4 +1,5 @@ 'use client'; + import { ContactsFilterContext, type FilterType } from '@/context/contacts-filter'; import { Button } from '@repo/ui/components/ui/button'; import { diff --git a/apps/web/components/navigation/bottom-nav.tsx b/apps/web/components/navigation/bottom-nav.tsx index 8866808..36d3ac0 100644 --- a/apps/web/components/navigation/bottom-nav.tsx +++ b/apps/web/components/navigation/bottom-nav.tsx @@ -1,4 +1,5 @@ 'use client'; + import { NavButton } from './components/nav-button'; import { BookOpen, Newspaper, PlusCircle, User, Users } from 'lucide-react'; import { usePathname } from 'next/navigation'; diff --git a/apps/web/components/navigation/components/back-button.tsx b/apps/web/components/navigation/components/back-button.tsx index 6486c48..8ac1865 100644 --- a/apps/web/components/navigation/components/back-button.tsx +++ b/apps/web/components/navigation/components/back-button.tsx @@ -1,4 +1,5 @@ 'use client'; + import { ArrowLeft } from 'lucide-react'; import { useRouter } from 'next/navigation'; diff --git a/apps/web/components/navigation/components/nav-button.tsx b/apps/web/components/navigation/components/nav-button.tsx index 9141105..52227c1 100644 --- a/apps/web/components/navigation/components/nav-button.tsx +++ b/apps/web/components/navigation/components/nav-button.tsx @@ -1,4 +1,5 @@ 'use client'; + import { Button } from '@repo/ui/components/ui/button'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; diff --git a/apps/web/components/navigation/header.tsx b/apps/web/components/navigation/header.tsx index 7876515..d8c70d9 100644 --- a/apps/web/components/navigation/header.tsx +++ b/apps/web/components/navigation/header.tsx @@ -1,4 +1,5 @@ 'use client'; + import { BackButton } from './components/back-button'; type Props = { title: string | undefined }; diff --git a/apps/web/components/orders/components/back-button.tsx b/apps/web/components/orders/components/back-button.tsx index 0253a7a..f2bb01d 100644 --- a/apps/web/components/orders/components/back-button.tsx +++ b/apps/web/components/orders/components/back-button.tsx @@ -1,4 +1,5 @@ 'use client'; + import { useOrderCreate } from '@/hooks/api/orders'; import { useOrderStore } from '@/stores/order'; import { Button } from '@repo/ui/components/ui/button'; diff --git a/apps/web/components/orders/components/contacts-grid/index.tsx b/apps/web/components/orders/components/contacts-grid/index.tsx index 0b9eddc..9b2a13b 100644 --- a/apps/web/components/orders/components/contacts-grid/index.tsx +++ b/apps/web/components/orders/components/contacts-grid/index.tsx @@ -1,4 +1,5 @@ 'use client'; + import { ContactsGridBase } from './components'; import { LoadingSpinner } from '@/components/common/spinner'; import { ContactsFilterProvider } from '@/context/contacts-filter'; diff --git a/apps/web/components/orders/components/datetime-select/components/date-select.tsx b/apps/web/components/orders/components/datetime-select/components/date-select.tsx index 015cf5a..eff3db7 100644 --- a/apps/web/components/orders/components/datetime-select/components/date-select.tsx +++ b/apps/web/components/orders/components/datetime-select/components/date-select.tsx @@ -1,4 +1,5 @@ 'use client'; + import { useOrderStore } from '@/stores/order'; import { Calendar } from '@repo/ui/components/ui/calendar'; import dayjs from 'dayjs'; diff --git a/apps/web/components/orders/components/datetime-select/components/time-select.tsx b/apps/web/components/orders/components/datetime-select/components/time-select.tsx index d34b859..da25224 100644 --- a/apps/web/components/orders/components/datetime-select/components/time-select.tsx +++ b/apps/web/components/orders/components/datetime-select/components/time-select.tsx @@ -1,5 +1,6 @@ /* eslint-disable canonical/id-match */ 'use client'; + import { useSlotsQuery } from '@/hooks/api/slots'; import { useOrderStore } from '@/stores/order'; import { Enum_Slot_State, type SlotFieldsFragment } from '@repo/graphql/types'; diff --git a/apps/web/components/orders/components/next-button.tsx b/apps/web/components/orders/components/next-button.tsx index 081be1c..53bec02 100644 --- a/apps/web/components/orders/components/next-button.tsx +++ b/apps/web/components/orders/components/next-button.tsx @@ -1,4 +1,5 @@ 'use client'; + import { useOrderStore } from '@/stores/order'; import { Button } from '@repo/ui/components/ui/button'; diff --git a/apps/web/components/orders/components/result.tsx b/apps/web/components/orders/components/result.tsx index 195d40b..9f09837 100644 --- a/apps/web/components/orders/components/result.tsx +++ b/apps/web/components/orders/components/result.tsx @@ -1,4 +1,5 @@ 'use client'; + import { useOrderStore } from '@/stores/order'; import { Button } from '@repo/ui/components/ui/button'; import { Card, CardContent } from '@repo/ui/components/ui/card'; diff --git a/apps/web/components/orders/components/service-select.tsx b/apps/web/components/orders/components/service-select.tsx index 73273e2..a3f0ceb 100644 --- a/apps/web/components/orders/components/service-select.tsx +++ b/apps/web/components/orders/components/service-select.tsx @@ -1,4 +1,5 @@ 'use client'; + import { useServicesQuery } from '@/hooks/api/services'; import { useOrderStore } from '@/stores/order'; import { type ServiceFieldsFragment } from '@repo/graphql/types'; diff --git a/apps/web/components/orders/components/submit-button.tsx b/apps/web/components/orders/components/submit-button.tsx index fa3878b..464fafb 100644 --- a/apps/web/components/orders/components/submit-button.tsx +++ b/apps/web/components/orders/components/submit-button.tsx @@ -1,4 +1,5 @@ 'use client'; + import { useOrderCreate } from '@/hooks/api/orders'; import { useOrderStore } from '@/stores/order'; import { Button } from '@repo/ui/components/ui/button'; diff --git a/apps/web/components/orders/order-form.tsx b/apps/web/components/orders/order-form.tsx index 0292c13..778a15f 100644 --- a/apps/web/components/orders/order-form.tsx +++ b/apps/web/components/orders/order-form.tsx @@ -1,4 +1,5 @@ 'use client'; + import { LoadingSpinner } from '../common/spinner'; import { BackButton, diff --git a/apps/web/components/profile/components/checkbox-field.tsx b/apps/web/components/profile/components/checkbox-field.tsx index 522301b..07dcaf5 100644 --- a/apps/web/components/profile/components/checkbox-field.tsx +++ b/apps/web/components/profile/components/checkbox-field.tsx @@ -1,5 +1,6 @@ /* eslint-disable promise/prefer-await-to-then */ 'use client'; + import { Checkbox, type CheckboxProps } from '@repo/ui/components/ui/checkbox'; import { useState } from 'react'; import { useDebouncedCallback } from 'use-debounce'; diff --git a/apps/web/components/profile/components/text-field.tsx b/apps/web/components/profile/components/text-field.tsx index c458223..fd0b18a 100644 --- a/apps/web/components/profile/components/text-field.tsx +++ b/apps/web/components/profile/components/text-field.tsx @@ -1,5 +1,6 @@ /* eslint-disable promise/prefer-await-to-then */ 'use client'; + import { type CustomerInput } from '@repo/graphql/types'; import { Input } from '@repo/ui/components/ui/input'; import { Label } from '@repo/ui/components/ui/label'; diff --git a/apps/web/components/profile/data-card.tsx b/apps/web/components/profile/data-card.tsx index afa5e0d..f13993f 100644 --- a/apps/web/components/profile/data-card.tsx +++ b/apps/web/components/profile/data-card.tsx @@ -1,4 +1,5 @@ 'use client'; + import { CardSectionHeader } from '../ui'; import { CheckboxWithText, DataField } from './components'; import { type ProfileProps } from './types'; diff --git a/apps/web/components/profile/links-card.tsx b/apps/web/components/profile/links-card.tsx index 56cda90..b6f8262 100644 --- a/apps/web/components/profile/links-card.tsx +++ b/apps/web/components/profile/links-card.tsx @@ -1,5 +1,6 @@ /* eslint-disable canonical/id-match */ 'use client'; + import { LinkButton } from './components'; import { type ProfileProps } from './types'; import { useCustomerQuery } from '@/hooks/api/customers'; diff --git a/apps/web/components/profile/person-card.tsx b/apps/web/components/profile/person-card.tsx index 29fb5fa..f7ac660 100644 --- a/apps/web/components/profile/person-card.tsx +++ b/apps/web/components/profile/person-card.tsx @@ -1,4 +1,5 @@ 'use client'; + import { LoadingSpinner } from '../common/spinner'; import { type ProfileProps } from './types'; import { useCustomerQuery } from '@/hooks/api/customers'; diff --git a/apps/web/components/schedule/calendar.tsx b/apps/web/components/schedule/calendar.tsx index 4a024b8..be9fa86 100644 --- a/apps/web/components/schedule/calendar.tsx +++ b/apps/web/components/schedule/calendar.tsx @@ -1,4 +1,5 @@ 'use client'; + import { ScheduleContext } from '@/context/schedule'; import { Calendar } from '@repo/ui/components/ui/calendar'; import dayjs from 'dayjs'; diff --git a/apps/web/components/schedule/components/order-card.tsx b/apps/web/components/schedule/components/order-card.tsx index 284366f..346832a 100644 --- a/apps/web/components/schedule/components/order-card.tsx +++ b/apps/web/components/schedule/components/order-card.tsx @@ -1,5 +1,6 @@ /* eslint-disable canonical/id-match */ 'use client'; + import { type OrderClient, type OrderComponentProps } from '../types'; import { ReadonlyTimeRange } from './time-range'; import { useOrderQuery } from '@/hooks/api/orders'; diff --git a/apps/web/components/schedule/components/slot-card.tsx b/apps/web/components/schedule/components/slot-card.tsx index 8b1a2cc..da76edb 100644 --- a/apps/web/components/schedule/components/slot-card.tsx +++ b/apps/web/components/schedule/components/slot-card.tsx @@ -1,5 +1,6 @@ /* eslint-disable canonical/id-match */ 'use client'; + import { ReadonlyTimeRange } from './time-range'; import { useSlotQuery } from '@/hooks/api/slots'; import { Enum_Slot_State, type SlotFieldsFragment } from '@repo/graphql/types'; diff --git a/apps/web/components/schedule/components/slot-date.tsx b/apps/web/components/schedule/components/slot-date.tsx index cdec86d..d967f69 100644 --- a/apps/web/components/schedule/components/slot-date.tsx +++ b/apps/web/components/schedule/components/slot-date.tsx @@ -1,4 +1,5 @@ 'use client'; + import { type SlotComponentProps } from '../types'; import { useSlotQuery } from '@/hooks/api/slots'; import { formatDate } from '@repo/graphql/utils/datetime-format'; diff --git a/apps/web/components/schedule/components/slot-time.tsx b/apps/web/components/schedule/components/slot-time.tsx index 3c85b34..e0b843c 100644 --- a/apps/web/components/schedule/components/slot-time.tsx +++ b/apps/web/components/schedule/components/slot-time.tsx @@ -1,5 +1,6 @@ /* eslint-disable react/jsx-no-bind */ 'use client'; + import { ScheduleTimeContext } from '../context'; import { type SlotComponentProps } from '../types'; import { EditableTimeRangeForm, ReadonlyTimeRange } from './time-range'; diff --git a/apps/web/components/schedule/components/time-range.tsx b/apps/web/components/schedule/components/time-range.tsx index d2f0edc..129357d 100644 --- a/apps/web/components/schedule/components/time-range.tsx +++ b/apps/web/components/schedule/components/time-range.tsx @@ -1,4 +1,5 @@ 'use client'; + import { ScheduleTimeContext } from '../context'; import { formatTime } from '@repo/graphql/utils/datetime-format'; import { Input } from '@repo/ui/components/ui/input'; diff --git a/apps/web/components/schedule/context/index.tsx b/apps/web/components/schedule/context/index.tsx index 13d0474..1629846 100644 --- a/apps/web/components/schedule/context/index.tsx +++ b/apps/web/components/schedule/context/index.tsx @@ -1,4 +1,5 @@ 'use client'; + import { createContext, type Dispatch, diff --git a/apps/web/components/schedule/day-slot-add-form.tsx b/apps/web/components/schedule/day-slot-add-form.tsx index 82d421a..e576c8b 100644 --- a/apps/web/components/schedule/day-slot-add-form.tsx +++ b/apps/web/components/schedule/day-slot-add-form.tsx @@ -1,5 +1,6 @@ /* eslint-disable canonical/id-match */ 'use client'; + import { EditableTimeRangeForm } from './components/time-range'; import { ScheduleTimeContext, ScheduleTimeContextProvider } from './context'; import { useSlotCreate } from '@/hooks/api/slots'; diff --git a/apps/web/components/schedule/day-slots-list.tsx b/apps/web/components/schedule/day-slots-list.tsx index a066280..a2bac63 100644 --- a/apps/web/components/schedule/day-slots-list.tsx +++ b/apps/web/components/schedule/day-slots-list.tsx @@ -1,4 +1,5 @@ 'use client'; + import { SlotCard } from './components/slot-card'; import { DaySlotAddForm } from './day-slot-add-form'; import { LoadingSpinner } from '@/components/common/spinner'; diff --git a/apps/web/components/schedule/slot-buttons.tsx b/apps/web/components/schedule/slot-buttons.tsx index 654f70e..587a3b2 100644 --- a/apps/web/components/schedule/slot-buttons.tsx +++ b/apps/web/components/schedule/slot-buttons.tsx @@ -1,6 +1,7 @@ /* eslint-disable react/jsx-no-bind */ /* eslint-disable canonical/id-match */ 'use client'; + import { type SlotComponentProps } from './types'; import { useSlotDelete, useSlotMutation, useSlotQuery } from '@/hooks/api/slots'; import { Enum_Slot_State } from '@repo/graphql/types'; diff --git a/apps/web/components/schedule/slot-datetime.tsx b/apps/web/components/schedule/slot-datetime.tsx index a76c0fc..9fc54b7 100644 --- a/apps/web/components/schedule/slot-datetime.tsx +++ b/apps/web/components/schedule/slot-datetime.tsx @@ -1,4 +1,5 @@ 'use client'; + import { SlotDate } from './components/slot-date'; import { SlotTime } from './components/slot-time'; import { ScheduleTimeContextProvider } from './context'; diff --git a/apps/web/components/schedule/slot-orders-list.tsx b/apps/web/components/schedule/slot-orders-list.tsx index 80ed9f1..61d01f7 100644 --- a/apps/web/components/schedule/slot-orders-list.tsx +++ b/apps/web/components/schedule/slot-orders-list.tsx @@ -1,4 +1,5 @@ 'use client'; + import { OrderCard } from './components/order-card'; import { type SlotComponentProps } from './types'; import { useSlotQuery } from '@/hooks/api/slots'; diff --git a/apps/web/context/contacts-filter.tsx b/apps/web/context/contacts-filter.tsx index 9a30bc5..df7125d 100644 --- a/apps/web/context/contacts-filter.tsx +++ b/apps/web/context/contacts-filter.tsx @@ -1,4 +1,5 @@ 'use client'; + import { createContext, type PropsWithChildren, useMemo, useState } from 'react'; export type FilterType = 'all' | 'clients' | 'masters'; diff --git a/apps/web/context/schedule.tsx b/apps/web/context/schedule.tsx index 97e747a..a8d9bd1 100644 --- a/apps/web/context/schedule.tsx +++ b/apps/web/context/schedule.tsx @@ -1,4 +1,5 @@ 'use client'; + import { createContext, useMemo, useState } from 'react'; type ContextType = { diff --git a/apps/web/hooks/api/contacts/use-customer-contacts.ts b/apps/web/hooks/api/contacts/use-customer-contacts.ts index 2945af4..43dd5d7 100644 --- a/apps/web/hooks/api/contacts/use-customer-contacts.ts +++ b/apps/web/hooks/api/contacts/use-customer-contacts.ts @@ -1,4 +1,5 @@ 'use client'; + import { useClientsQuery, useMastersQuery } from './query'; import { ContactsFilterContext } from '@/context/contacts-filter'; import { sift } from 'radash'; diff --git a/apps/web/hooks/api/customers.ts b/apps/web/hooks/api/customers.ts index 6c97848..32f3b3f 100644 --- a/apps/web/hooks/api/customers.ts +++ b/apps/web/hooks/api/customers.ts @@ -1,4 +1,5 @@ 'use client'; + import { getCustomer, updateCustomer } from '@/actions/api/customers'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useSession } from 'next-auth/react'; diff --git a/apps/web/hooks/api/orders.ts b/apps/web/hooks/api/orders.ts index b25bd15..4f5bffd 100644 --- a/apps/web/hooks/api/orders.ts +++ b/apps/web/hooks/api/orders.ts @@ -1,4 +1,5 @@ 'use client'; + import { createOrder, getOrder } from '@/actions/api/orders'; import { useMutation, useQuery } from '@tanstack/react-query'; diff --git a/apps/web/hooks/api/services.ts b/apps/web/hooks/api/services.ts index 062ee76..8fc7a54 100644 --- a/apps/web/hooks/api/services.ts +++ b/apps/web/hooks/api/services.ts @@ -1,4 +1,5 @@ 'use client'; + import { getService, getServices } from '@/actions/api/services'; import { useQuery } from '@tanstack/react-query'; diff --git a/apps/web/hooks/api/slots.ts b/apps/web/hooks/api/slots.ts index a216db6..ec7cbd5 100644 --- a/apps/web/hooks/api/slots.ts +++ b/apps/web/hooks/api/slots.ts @@ -1,4 +1,5 @@ 'use client'; + import { useCustomerQuery } from './customers'; import { createSlot, deleteSlot, getSlot, getSlots, updateSlot } from '@/actions/api/slots'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; diff --git a/apps/web/providers/auth.tsx b/apps/web/providers/auth.tsx index 2027d02..88520aa 100644 --- a/apps/web/providers/auth.tsx +++ b/apps/web/providers/auth.tsx @@ -1,4 +1,5 @@ 'use client'; + import { SessionProvider } from 'next-auth/react'; export function AuthProvider({ children }: { readonly children: React.ReactNode }) { diff --git a/apps/web/providers/telegram.tsx b/apps/web/providers/telegram.tsx index 999e97f..2be20d6 100644 --- a/apps/web/providers/telegram.tsx +++ b/apps/web/providers/telegram.tsx @@ -1,5 +1,6 @@ /* eslint-disable sonarjs/function-return-type */ 'use client'; + import { useClientOnce, useDidMount } from '@/hooks/telegram'; import { setLocale } from '@/utils/i18n/locale'; import { init } from '@/utils/telegram/init'; diff --git a/apps/web/providers/theme-provider.tsx b/apps/web/providers/theme-provider.tsx index dd140f5..ad9f335 100644 --- a/apps/web/providers/theme-provider.tsx +++ b/apps/web/providers/theme-provider.tsx @@ -1,4 +1,5 @@ 'use client'; + import { ThemeProvider as NextThemesProvider } from 'next-themes'; import { type ComponentProps, useEffect, useState } from 'react'; diff --git a/apps/web/stores/order/context.tsx b/apps/web/stores/order/context.tsx index 2800c31..cbb9ae5 100644 --- a/apps/web/stores/order/context.tsx +++ b/apps/web/stores/order/context.tsx @@ -1,4 +1,5 @@ 'use client'; + import { createOrderStore } from './store'; import { createContext, type PropsWithChildren, useRef } from 'react'; diff --git a/apps/web/stores/order/hooks.tsx b/apps/web/stores/order/hooks.tsx index 2bfb40f..8e830a7 100644 --- a/apps/web/stores/order/hooks.tsx +++ b/apps/web/stores/order/hooks.tsx @@ -1,5 +1,6 @@ /* eslint-disable canonical/id-match */ 'use client'; + import { OrderStoreContext } from './context'; import { type OrderStore, type Steps } from './types'; import { useCustomerQuery } from '@/hooks/api/customers'; diff --git a/packages/ui/src/components/ui/avatar.tsx b/packages/ui/src/components/ui/avatar.tsx index ffcf430..5339d21 100644 --- a/packages/ui/src/components/ui/avatar.tsx +++ b/packages/ui/src/components/ui/avatar.tsx @@ -1,4 +1,5 @@ 'use client'; + import * as AvatarPrimitive from '@radix-ui/react-avatar'; import { cn } from '@repo/ui/lib/utils'; import * as React from 'react'; diff --git a/packages/ui/src/components/ui/calendar.tsx b/packages/ui/src/components/ui/calendar.tsx index 8bfb4cd..772cd35 100644 --- a/packages/ui/src/components/ui/calendar.tsx +++ b/packages/ui/src/components/ui/calendar.tsx @@ -2,6 +2,7 @@ /* eslint-disable @typescript-eslint/no-shadow */ /* eslint-disable react/no-unstable-nested-components */ 'use client'; + import { buttonVariants } from '@repo/ui/components/ui/button'; import { cn } from '@repo/ui/lib/utils'; import { ru } from 'date-fns/locale'; -- 2.47.2