bring the results of queries and hooks into a single form

This commit is contained in:
vchikalkin 2025-02-13 17:41:19 +03:00
parent 2683e31570
commit 284142eb4f
12 changed files with 59 additions and 70 deletions

View File

@ -16,7 +16,8 @@ import { message } from 'telegraf/filters';
const bot = new Telegraf(environment.BOT_TOKEN);
bot.start(async (context) => {
const customer = await getCustomer({ telegramId: context.from.id });
const data = await getCustomer({ telegramId: context.from.id });
const customer = data?.data?.customers?.at(0);
if (customer) {
return context.reply(
@ -33,7 +34,8 @@ bot.start(async (context) => {
});
bot.command('addcontact', async (context) => {
const customer = await getCustomer({ telegramId: context.from.id });
const data = await getCustomer({ telegramId: context.from.id });
const customer = data?.data?.customers?.at(0);
if (!customer) {
return context.reply(
@ -50,7 +52,8 @@ bot.command('addcontact', async (context) => {
});
bot.command('becomemaster', async (context) => {
const customer = await getCustomer({ telegramId: context.from.id });
const data = await getCustomer({ telegramId: context.from.id });
const customer = data?.data?.customers?.at(0);
if (!customer) {
return context.reply('Сначала поделитесь своим номером телефона.', KEYBOARD_SHARE_PHONE);
@ -73,7 +76,9 @@ bot.command('becomemaster', async (context) => {
});
bot.on(message('contact'), async (context) => {
const customer = await getCustomer({ telegramId: context.from.id });
const data = await getCustomer({ telegramId: context.from.id });
const customer = data?.data?.customers?.at(0);
const isRegistration = !customer;
const { contact } = context.message;

View File

@ -9,11 +9,9 @@ export async function getClients() {
const { user } = session;
const getCustomerClientsResponse = await getCustomerClients({ telegramId: user?.telegramId });
const response = await getCustomerClients({ telegramId: user?.telegramId });
return {
clients: getCustomerClientsResponse?.clients,
};
return response.data?.customers?.at(0);
}
export async function getMasters() {
@ -22,9 +20,7 @@ export async function getMasters() {
const { user } = session;
const getCustomerMastersResponse = await getCustomerMasters({ telegramId: user?.telegramId });
const response = await getCustomerMasters({ telegramId: user?.telegramId });
return {
masters: getCustomerMastersResponse?.masters,
};
return response.data?.customers?.at(0);
}

View File

@ -9,10 +9,11 @@ export async function getProfile(input?: GetCustomerQueryVariables) {
if (!session) throw new Error('Missing session');
const { user } = session;
const telegramId = input?.telegramId || user?.telegramId;
const customer = await getCustomer({ telegramId });
const { data } = await getCustomer({ telegramId });
const customer = data?.customers?.at(0);
return customer;
}
@ -22,11 +23,12 @@ export async function updateProfile(input: CustomerInput) {
const { user } = session;
const customer = await getCustomer({ telegramId: user?.telegramId });
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,
documentId: customer?.documentId,
});
}

View File

@ -28,19 +28,21 @@ export const SlotCard = withContext(ContextProvider)(function (
const { documentId } = props;
const { data } = useSlotQuery({ documentId });
if (!data?.slot) return null;
const slot = data?.data?.slot;
const ordersNumber = data.slot.orders?.length;
if (!slot) return null;
const ordersNumber = slot.orders?.length;
const hasOrders = Boolean(ordersNumber);
const isOpened = data?.slot?.state === Enum_Slot_State.Open;
const isClosed = data?.slot?.state === Enum_Slot_State.Closed;
const isOpened = slot?.state === Enum_Slot_State.Open;
const isClosed = slot?.state === Enum_Slot_State.Closed;
return (
<Link href={`/slots/${documentId}`} rel="noopener noreferrer">
<div className="flex items-center justify-between rounded-2xl bg-background p-4 px-6 dark:bg-primary/5">
<div className="flex flex-col gap-2">
<ReadonlyTimeRange {...data.slot} />
<ReadonlyTimeRange {...slot} />
<span
className={cn(
'text-xs font-normal',
@ -50,14 +52,14 @@ export const SlotCard = withContext(ContextProvider)(function (
{hasOrders ? 'Есть записи' : 'Свободно'}
</span>
</div>
{data.slot.state && (
{slot.state && (
<Badge
className={cn(
isOpened ? 'bg-green-100 text-green-500 dark:bg-green-700 dark:text-green-100' : '',
isClosed ? 'bg-red-100 text-red-500 dark:bg-red-700 dark:text-red-100' : '',
)}
>
{getBadgeText(data.slot.state)}
{getBadgeText(slot.state)}
</Badge>
)}
</div>

View File

@ -5,17 +5,18 @@ import { formatDate, formatTime } from '@/utils/date';
export function DateTimeCard({ documentId }: Readonly<SlotComponentProps>) {
const { data } = useSlotQuery({ documentId });
const slot = data?.data?.slot;
if (!data?.slot) return null;
if (!slot) return null;
return (
<div className="flex flex-col">
<span className="mb-2 tracking-wide">{formatDate(data?.slot?.date).user()}</span>
<span className="mb-2 tracking-wide">{formatDate(slot?.date).user()}</span>
<span className="mt-2 text-3xl font-bold tracking-wide">
{formatTime(data?.slot?.time_start).user()}
{formatTime(slot?.time_start).user()}
</span>
<span className="mt-2 text-3xl font-bold tracking-wide">
{formatTime(data?.slot?.time_end).user()}
{formatTime(slot?.time_end).user()}
</span>
</div>
);

View File

@ -22,11 +22,12 @@ export function OrdersList({
documentId,
}: Readonly<SlotComponentProps> & { readonly className?: string }) {
const { data } = useSlotQuery({ documentId });
const slot = data?.data?.slot;
return (
<div className={cn('bg-transparent p-4', className)}>
<h1 className="mb-2 text-2xl font-bold">Записи</h1>
{data?.slot?.orders.map((order) => {
{slot?.orders.map((order) => {
return order && <Order key={order?.documentId} {...order} />;
})}
</div>
@ -41,26 +42,24 @@ function getBadgeText(state: Enum_Order_State) {
function Order({ documentId }: Readonly<OrderComponentProps>) {
const { data } = useOrderQuery({ documentId });
const order = data?.data?.order;
if (!data?.data.order) return null;
if (!order) return null;
const isCompleted = data?.data.order?.state === Enum_Order_State.Completed;
const isCancelled = data?.data.order?.state === Enum_Order_State.Cancelled;
const isCompleted = order?.state === Enum_Order_State.Completed;
const isCancelled = order?.state === Enum_Order_State.Cancelled;
return (
<Link href={`/records/${documentId}`} rel="noopener noreferrer">
<div className="flex items-center justify-between rounded-2xl bg-background p-4 px-6 dark:bg-primary/5">
<div className="flex flex-col justify-around gap-2">
<ReadonlyTimeRange
time_end={data?.data.order?.time_end}
time_start={data?.data.order?.time_start}
/>
<ReadonlyTimeRange time_end={order?.time_end} time_start={order?.time_start} />
<span className="truncate text-xs font-bold text-foreground">
{data.data.order.services.map((service) => service?.name).join(', ')}
{order.services.map((service) => service?.name).join(', ')}
</span>
<span className="text-xs text-foreground">{data?.data.order?.client?.name}</span>
<span className="text-xs text-foreground">{order?.client?.name}</span>
</div>
{data?.data.order?.state && (
{order?.state && (
<Badge
className={cn(
isCompleted
@ -69,7 +68,7 @@ function Order({ documentId }: Readonly<OrderComponentProps>) {
isCancelled ? 'bg-red-100 text-red-500 dark:bg-red-700 dark:text-red-100' : '',
)}
>
{getBadgeText(data?.data.order?.state)}
{getBadgeText(order?.state)}
</Badge>
)}
</div>

View File

@ -9,9 +9,10 @@ import { useRouter } from 'next/navigation';
export function SlotButtons({ documentId }: Readonly<SlotComponentProps>) {
const { data } = useSlotQuery({ documentId });
const slot = data?.data?.slot;
const isOpened = data?.slot?.state === Enum_Slot_State.Open;
const isClosed = data?.slot?.state === Enum_Slot_State.Closed;
const isOpened = slot?.state === Enum_Slot_State.Open;
const isClosed = slot?.state === Enum_Slot_State.Closed;
const { mutate: mutateSlot } = useSlotMutation({ documentId });

View File

@ -4,7 +4,8 @@ import { LoadingSpinner } from '@/components/common/spinner';
import { useSlots } from '@/hooks/slots';
export function SlotsList() {
const { data: slots, isLoading } = useSlots();
const { data, isLoading } = useSlots();
const slots = data?.data.slots;
if (isLoading) return <LoadingSpinner />;

View File

@ -1,10 +1,8 @@
import { getClients, getMasters } from '@/actions/contacts';
import { useQuery } from '@tanstack/react-query';
export const useClientsQuery = () => {
return useQuery({ queryFn: getClients, queryKey: ['contacts', 'clients', 'get'] });
};
export const useClientsQuery = () =>
useQuery({ queryFn: getClients, queryKey: ['contacts', 'clients', 'get'] });
export const useMastersQuery = () => {
return useQuery({ queryFn: getMasters, queryKey: ['contacts', 'masters', 'get'] });
};
export const useMastersQuery = () =>
useQuery({ queryFn: getMasters, queryKey: ['contacts', 'masters', 'get'] });

View File

@ -5,13 +5,11 @@ import * as GQL from '../types';
export async function login() {
const { mutate } = createApolloClient();
const response = await mutate({
return mutate({
mutation: GQL.LoginDocument,
variables: {
identifier: environment.LOGIN_GRAPHQL,
password: environment.PASSWORD_GRAPHQL,
},
});
return response;
}

View File

@ -33,34 +33,28 @@ export async function createOrUpdateUser(input: GQL.CreateCustomerMutationVariab
export async function getCustomer(input: GQL.GetCustomerQueryVariables) {
const { query } = await getClientWithToken();
const response = await query({
return query({
query: GQL.GetCustomerDocument,
variables: input,
});
return response?.data?.customers?.at(0);
}
export async function getCustomerMasters(input: GQL.GetCustomerMastersQueryVariables) {
const { query } = await getClientWithToken();
const response = await query({
return query({
query: GQL.GetCustomerMastersDocument,
variables: input,
});
return response?.data?.customers?.at(0);
}
export async function getCustomerClients(input: GQL.GetCustomerClientsQueryVariables) {
const { query } = await getClientWithToken();
const response = await query({
return query({
query: GQL.GetCustomerClientsDocument,
variables: input,
});
return response?.data?.customers?.at(0);
}
type AddCustomerMasterInput = Pick<GQL.CreateCustomerMutationVariables, 'phone' | 'telegramId'> & {

View File

@ -5,36 +5,28 @@ import * as GQL from '../types';
export async function createSlot(input: GQL.CreateSlotMutationVariables['input']) {
const { mutate } = await getClientWithToken();
// TODO: check slot does not overlap with others
return mutate({
mutation: GQL.CreateSlotDocument,
variables: { input },
}).catch((err) => {
console.error(JSON.stringify(err, null, 2));
});
}
export async function getSlots(input: GQL.GetSlotsQueryVariables) {
const { query } = await getClientWithToken();
const response = await query({
return query({
query: GQL.GetSlotsDocument,
variables: input,
});
return response?.data?.slots;
}
export async function getSlot(input: GQL.GetSlotQueryVariables) {
const { query } = await getClientWithToken();
const response = await query({
return query({
query: GQL.GetSlotDocument,
variables: input,
});
return response?.data;
}
export async function updateSlot(input: GQL.UpdateSlotMutationVariables) {