Compare commits
4 Commits
main
...
fix/bugs-f
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f1da2c834a | ||
|
|
57cab6209d | ||
|
|
60002a7169 | ||
|
|
8b0f93bf23 |
1
.github/workflows/deploy.yml
vendored
1
.github/workflows/deploy.yml
vendored
@ -98,5 +98,6 @@ jobs:
|
||||
cd /home/${{ secrets.VPS_USER }}/zapishis && \
|
||||
docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }} && \
|
||||
docker compose pull && \
|
||||
docker compose down && \
|
||||
docker compose up -d
|
||||
"
|
||||
|
||||
@ -1,36 +1,8 @@
|
||||
'use server';
|
||||
import * as customers from './server/customers';
|
||||
import { wrapClientAction } from '@/utils/actions';
|
||||
|
||||
import { useService } from './lib/service';
|
||||
import { CustomersService } from '@repo/graphql/api/customers';
|
||||
|
||||
const getService = useService(CustomersService);
|
||||
|
||||
export async function addMasters(...variables: Parameters<CustomersService['addMasters']>) {
|
||||
const service = await getService();
|
||||
|
||||
return service.addMasters(...variables);
|
||||
}
|
||||
|
||||
export async function getClients(...variables: Parameters<CustomersService['getClients']>) {
|
||||
const service = await getService();
|
||||
|
||||
return service.getClients(...variables);
|
||||
}
|
||||
|
||||
export async function getCustomer(...variables: Parameters<CustomersService['getCustomer']>) {
|
||||
const service = await getService();
|
||||
|
||||
return service.getCustomer(...variables);
|
||||
}
|
||||
|
||||
export async function getMasters(...variables: Parameters<CustomersService['getMasters']>) {
|
||||
const service = await getService();
|
||||
|
||||
return service.getMasters(...variables);
|
||||
}
|
||||
|
||||
export async function updateCustomer(...variables: Parameters<CustomersService['updateCustomer']>) {
|
||||
const service = await getService();
|
||||
|
||||
return service.updateCustomer(...variables);
|
||||
}
|
||||
export const addMasters = wrapClientAction(customers.addMasters);
|
||||
export const getClients = wrapClientAction(customers.getClients);
|
||||
export const getCustomer = wrapClientAction(customers.getCustomer);
|
||||
export const getMasters = wrapClientAction(customers.getMasters);
|
||||
export const updateCustomer = wrapClientAction(customers.updateCustomer);
|
||||
|
||||
@ -1,30 +1,7 @@
|
||||
'use server';
|
||||
import * as orders from './server/orders';
|
||||
import { wrapClientAction } from '@/utils/actions';
|
||||
|
||||
import { useService } from './lib/service';
|
||||
import { OrdersService } from '@repo/graphql/api/orders';
|
||||
|
||||
const getServicesService = useService(OrdersService);
|
||||
|
||||
export async function createOrder(...variables: Parameters<OrdersService['createOrder']>) {
|
||||
const service = await getServicesService();
|
||||
|
||||
return service.createOrder(...variables);
|
||||
}
|
||||
|
||||
export async function getOrder(...variables: Parameters<OrdersService['getOrder']>) {
|
||||
const service = await getServicesService();
|
||||
|
||||
return service.getOrder(...variables);
|
||||
}
|
||||
|
||||
export async function getOrders(...variables: Parameters<OrdersService['getOrders']>) {
|
||||
const service = await getServicesService();
|
||||
|
||||
return service.getOrders(...variables);
|
||||
}
|
||||
|
||||
export async function updateOrder(...variables: Parameters<OrdersService['updateOrder']>) {
|
||||
const service = await getServicesService();
|
||||
|
||||
return service.updateOrder(...variables);
|
||||
}
|
||||
export const createOrder = wrapClientAction(orders.createOrder);
|
||||
export const getOrder = wrapClientAction(orders.getOrder);
|
||||
export const getOrders = wrapClientAction(orders.getOrders);
|
||||
export const updateOrder = wrapClientAction(orders.updateOrder);
|
||||
|
||||
37
apps/web/actions/api/server/customers.ts
Normal file
37
apps/web/actions/api/server/customers.ts
Normal file
@ -0,0 +1,37 @@
|
||||
'use server';
|
||||
|
||||
import { useService } from '../lib/service';
|
||||
import { wrapServerAction } from '@/utils/actions';
|
||||
import { CustomersService } from '@repo/graphql/api/customers';
|
||||
|
||||
const getService = useService(CustomersService);
|
||||
|
||||
export async function addMasters(...variables: Parameters<CustomersService['addMasters']>) {
|
||||
const service = await getService();
|
||||
|
||||
return wrapServerAction(() => service.addMasters(...variables));
|
||||
}
|
||||
|
||||
export async function getClients(...variables: Parameters<CustomersService['getClients']>) {
|
||||
const service = await getService();
|
||||
|
||||
return wrapServerAction(() => service.getClients(...variables));
|
||||
}
|
||||
|
||||
export async function getCustomer(...variables: Parameters<CustomersService['getCustomer']>) {
|
||||
const service = await getService();
|
||||
|
||||
return wrapServerAction(() => service.getCustomer(...variables));
|
||||
}
|
||||
|
||||
export async function getMasters(...variables: Parameters<CustomersService['getMasters']>) {
|
||||
const service = await getService();
|
||||
|
||||
return wrapServerAction(() => service.getMasters(...variables));
|
||||
}
|
||||
|
||||
export async function updateCustomer(...variables: Parameters<CustomersService['updateCustomer']>) {
|
||||
const service = await getService();
|
||||
|
||||
return wrapServerAction(() => service.updateCustomer(...variables));
|
||||
}
|
||||
31
apps/web/actions/api/server/orders.ts
Normal file
31
apps/web/actions/api/server/orders.ts
Normal file
@ -0,0 +1,31 @@
|
||||
'use server';
|
||||
|
||||
import { useService } from '../lib/service';
|
||||
import { wrapServerAction } from '@/utils/actions';
|
||||
import { OrdersService } from '@repo/graphql/api/orders';
|
||||
|
||||
const getServicesService = useService(OrdersService);
|
||||
|
||||
export async function createOrder(...variables: Parameters<OrdersService['createOrder']>) {
|
||||
const service = await getServicesService();
|
||||
|
||||
return wrapServerAction(() => service.createOrder(...variables));
|
||||
}
|
||||
|
||||
export async function getOrder(...variables: Parameters<OrdersService['getOrder']>) {
|
||||
const service = await getServicesService();
|
||||
|
||||
return wrapServerAction(() => service.getOrder(...variables));
|
||||
}
|
||||
|
||||
export async function getOrders(...variables: Parameters<OrdersService['getOrders']>) {
|
||||
const service = await getServicesService();
|
||||
|
||||
return wrapServerAction(() => service.getOrders(...variables));
|
||||
}
|
||||
|
||||
export async function updateOrder(...variables: Parameters<OrdersService['updateOrder']>) {
|
||||
const service = await getServicesService();
|
||||
|
||||
return wrapServerAction(() => service.updateOrder(...variables));
|
||||
}
|
||||
19
apps/web/actions/api/server/services.ts
Normal file
19
apps/web/actions/api/server/services.ts
Normal file
@ -0,0 +1,19 @@
|
||||
'use server';
|
||||
|
||||
import { useService } from '../lib/service';
|
||||
import { wrapServerAction } from '@/utils/actions';
|
||||
import { ServicesService } from '@repo/graphql/api/services';
|
||||
|
||||
const getServicesService = useService(ServicesService);
|
||||
|
||||
export async function getService(...variables: Parameters<ServicesService['getService']>) {
|
||||
const service = await getServicesService();
|
||||
|
||||
return wrapServerAction(() => service.getService(...variables));
|
||||
}
|
||||
|
||||
export async function getServices(...variables: Parameters<ServicesService['getServices']>) {
|
||||
const service = await getServicesService();
|
||||
|
||||
return wrapServerAction(() => service.getServices(...variables));
|
||||
}
|
||||
45
apps/web/actions/api/server/slots.ts
Normal file
45
apps/web/actions/api/server/slots.ts
Normal file
@ -0,0 +1,45 @@
|
||||
'use server';
|
||||
|
||||
import { useService } from '../lib/service';
|
||||
import { wrapServerAction } from '@/utils/actions';
|
||||
import { SlotsService } from '@repo/graphql/api/slots';
|
||||
|
||||
const getService = useService(SlotsService);
|
||||
|
||||
export async function createSlot(...variables: Parameters<SlotsService['createSlot']>) {
|
||||
const service = await getService();
|
||||
|
||||
return wrapServerAction(() => service.createSlot(...variables));
|
||||
}
|
||||
|
||||
export async function deleteSlot(...variables: Parameters<SlotsService['deleteSlot']>) {
|
||||
const service = await getService();
|
||||
|
||||
return wrapServerAction(() => service.deleteSlot(...variables));
|
||||
}
|
||||
|
||||
export async function getAvailableTimeSlots(
|
||||
...variables: Parameters<SlotsService['getAvailableTimeSlots']>
|
||||
) {
|
||||
const service = await getService();
|
||||
|
||||
return wrapServerAction(() => service.getAvailableTimeSlots(...variables));
|
||||
}
|
||||
|
||||
export async function getSlot(...variables: Parameters<SlotsService['getSlot']>) {
|
||||
const service = await getService();
|
||||
|
||||
return wrapServerAction(() => service.getSlot(...variables));
|
||||
}
|
||||
|
||||
export async function getSlots(...variables: Parameters<SlotsService['getSlots']>) {
|
||||
const service = await getService();
|
||||
|
||||
return wrapServerAction(() => service.getSlots(...variables));
|
||||
}
|
||||
|
||||
export async function updateSlot(...variables: Parameters<SlotsService['updateSlot']>) {
|
||||
const service = await getService();
|
||||
|
||||
return wrapServerAction(() => service.updateSlot(...variables));
|
||||
}
|
||||
@ -1,18 +1,5 @@
|
||||
'use server';
|
||||
import * as services from './server/services';
|
||||
import { wrapClientAction } from '@/utils/actions';
|
||||
|
||||
import { useService } from './lib/service';
|
||||
import { ServicesService } from '@repo/graphql/api/services';
|
||||
|
||||
const getServicesService = useService(ServicesService);
|
||||
|
||||
export async function getService(...variables: Parameters<ServicesService['getService']>) {
|
||||
const service = await getServicesService();
|
||||
|
||||
return service.getService(...variables);
|
||||
}
|
||||
|
||||
export async function getServices(...variables: Parameters<ServicesService['getServices']>) {
|
||||
const service = await getServicesService();
|
||||
|
||||
return service.getServices(...variables);
|
||||
}
|
||||
export const getServices = wrapClientAction(services.getServices);
|
||||
export const getService = wrapClientAction(services.getService);
|
||||
|
||||
@ -1,44 +1,9 @@
|
||||
'use server';
|
||||
import * as slots from './server/slots';
|
||||
import { wrapClientAction } from '@/utils/actions';
|
||||
|
||||
import { useService } from './lib/service';
|
||||
import { SlotsService } from '@repo/graphql/api/slots';
|
||||
|
||||
const getService = useService(SlotsService);
|
||||
|
||||
export async function createSlot(...variables: Parameters<SlotsService['createSlot']>) {
|
||||
const service = await getService();
|
||||
|
||||
return service.createSlot(...variables);
|
||||
}
|
||||
|
||||
export async function deleteSlot(...variables: Parameters<SlotsService['deleteSlot']>) {
|
||||
const service = await getService();
|
||||
|
||||
return service.deleteSlot(...variables);
|
||||
}
|
||||
|
||||
export async function getAvailableTimeSlots(
|
||||
...variables: Parameters<SlotsService['getAvailableTimeSlots']>
|
||||
) {
|
||||
const service = await getService();
|
||||
|
||||
return service.getAvailableTimeSlots(...variables);
|
||||
}
|
||||
|
||||
export async function getSlot(...variables: Parameters<SlotsService['getSlot']>) {
|
||||
const service = await getService();
|
||||
|
||||
return service.getSlot(...variables);
|
||||
}
|
||||
|
||||
export async function getSlots(...variables: Parameters<SlotsService['getSlots']>) {
|
||||
const service = await getService();
|
||||
|
||||
return service.getSlots(...variables);
|
||||
}
|
||||
|
||||
export async function updateSlot(...variables: Parameters<SlotsService['updateSlot']>) {
|
||||
const service = await getService();
|
||||
|
||||
return service.updateSlot(...variables);
|
||||
}
|
||||
export const getSlot = wrapClientAction(slots.getSlot);
|
||||
export const getSlots = wrapClientAction(slots.getSlots);
|
||||
export const createSlot = wrapClientAction(slots.createSlot);
|
||||
export const updateSlot = wrapClientAction(slots.updateSlot);
|
||||
export const deleteSlot = wrapClientAction(slots.deleteSlot);
|
||||
export const getAvailableTimeSlots = wrapClientAction(slots.getAvailableTimeSlots);
|
||||
|
||||
@ -2,8 +2,9 @@
|
||||
|
||||
import { OrderCard } from '@/components/shared/order-card';
|
||||
import { useCustomerQuery, useIsMaster } from '@/hooks/api/customers';
|
||||
import { useOrdersQuery } from '@/hooks/api/orders';
|
||||
import { useOrdersInfiniteQuery } from '@/hooks/api/orders';
|
||||
import { useDateTimeStore } from '@/stores/datetime';
|
||||
import { Button } from '@repo/ui/components/ui/button';
|
||||
import { getDateUTCRange } from '@repo/utils/datetime-format';
|
||||
|
||||
export function ClientsOrdersList() {
|
||||
@ -14,7 +15,12 @@ export function ClientsOrdersList() {
|
||||
const selectedDate = useDateTimeStore((store) => store.date);
|
||||
const { endOfDay, startOfDay } = getDateUTCRange(selectedDate).day();
|
||||
|
||||
const { data: { orders } = {}, isLoading } = useOrdersQuery(
|
||||
const {
|
||||
data: { pages } = {},
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isLoading,
|
||||
} = useOrdersInfiniteQuery(
|
||||
{
|
||||
filters: {
|
||||
slot: {
|
||||
@ -31,19 +37,23 @@ export function ClientsOrdersList() {
|
||||
},
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
limit: selectedDate ? undefined : 10,
|
||||
},
|
||||
},
|
||||
Boolean(customer?.documentId) && isMaster,
|
||||
{ enabled: Boolean(customer?.documentId) && isMaster },
|
||||
);
|
||||
|
||||
const orders = pages?.flatMap((page) => page.orders) ?? [];
|
||||
|
||||
if (!orders?.length || isLoading || !isMaster) return null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col space-y-2">
|
||||
<h1 className="font-bold">Записи клиентов</h1>
|
||||
{orders?.map((order) => order && <OrderCard key={order.documentId} showDate {...order} />)}
|
||||
{hasNextPage && (
|
||||
<Button onClick={() => fetchNextPage()} variant="ghost">
|
||||
Загрузить еще
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -54,7 +64,12 @@ export function OrdersList() {
|
||||
const selectedDate = useDateTimeStore((store) => store.date);
|
||||
const { endOfDay, startOfDay } = getDateUTCRange(selectedDate).day();
|
||||
|
||||
const { data: { orders } = {}, isLoading } = useOrdersQuery(
|
||||
const {
|
||||
data: { pages } = {},
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isLoading,
|
||||
} = useOrdersInfiniteQuery(
|
||||
{
|
||||
filters: {
|
||||
client: {
|
||||
@ -71,13 +86,12 @@ export function OrdersList() {
|
||||
: undefined,
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
limit: selectedDate ? undefined : 10,
|
||||
},
|
||||
},
|
||||
Boolean(customer?.documentId),
|
||||
{ enabled: Boolean(customer?.documentId) },
|
||||
);
|
||||
|
||||
const orders = pages?.flatMap((page) => page.orders) ?? [];
|
||||
|
||||
if (!orders?.length || isLoading) return null;
|
||||
|
||||
return (
|
||||
@ -87,6 +101,11 @@ export function OrdersList() {
|
||||
(order) =>
|
||||
order && <OrderCard avatarSource="master" key={order.documentId} showDate {...order} />,
|
||||
)}
|
||||
{hasNextPage && (
|
||||
<Button onClick={() => fetchNextPage()} variant="ghost">
|
||||
Загрузить еще
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -3,7 +3,8 @@
|
||||
import { OrderCard } from '../shared/order-card';
|
||||
import { type ProfileProps } from './types';
|
||||
import { useCustomerQuery, useIsMaster } from '@/hooks/api/customers';
|
||||
import { useOrdersQuery } from '@/hooks/api/orders';
|
||||
import { useOrdersInfiniteQuery } from '@/hooks/api/orders';
|
||||
import { Button } from '@repo/ui/components/ui/button';
|
||||
|
||||
export function ProfileOrdersList({ telegramId }: Readonly<ProfileProps>) {
|
||||
const { data: { customer } = {} } = useCustomerQuery();
|
||||
@ -11,7 +12,12 @@ export function ProfileOrdersList({ telegramId }: Readonly<ProfileProps>) {
|
||||
|
||||
const { data: { customer: profile } = {} } = useCustomerQuery({ telegramId });
|
||||
|
||||
const { data: { orders } = {}, isLoading } = useOrdersQuery(
|
||||
const {
|
||||
data: { pages } = {},
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isLoading,
|
||||
} = useOrdersInfiniteQuery(
|
||||
{
|
||||
filters: {
|
||||
client: {
|
||||
@ -27,13 +33,12 @@ export function ProfileOrdersList({ telegramId }: Readonly<ProfileProps>) {
|
||||
},
|
||||
},
|
||||
},
|
||||
pagination: {
|
||||
limit: 5,
|
||||
},
|
||||
},
|
||||
Boolean(profile?.documentId) && Boolean(customer?.documentId),
|
||||
{ enabled: Boolean(profile?.documentId) && Boolean(customer?.documentId) },
|
||||
);
|
||||
|
||||
const orders = pages?.flatMap((page) => page.orders) ?? [];
|
||||
|
||||
if (!orders?.length || isLoading) return null;
|
||||
|
||||
return (
|
||||
@ -50,6 +55,11 @@ export function ProfileOrdersList({ telegramId }: Readonly<ProfileProps>) {
|
||||
/>
|
||||
),
|
||||
)}
|
||||
{hasNextPage && (
|
||||
<Button onClick={() => fetchNextPage()} variant="ghost">
|
||||
Загрузить еще
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
/* eslint-disable unicorn/prevent-abbreviations */
|
||||
'use client';
|
||||
|
||||
import { createOrder, getOrder, getOrders, updateOrder } from '@/actions/api/orders';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
export const useOrderQuery = ({ documentId }: Parameters<typeof getOrder>[0]) =>
|
||||
useQuery({
|
||||
@ -37,6 +38,33 @@ export const useOrdersQuery = (variables: Parameters<typeof getOrders>[0], enabl
|
||||
staleTime: 60 * 1_000,
|
||||
});
|
||||
|
||||
export const useOrdersInfiniteQuery = (
|
||||
variables: Omit<Parameters<typeof getOrders>[0], 'pagination'>,
|
||||
{ enabled = true, pageSize = 5 },
|
||||
) => {
|
||||
const queryFn = ({ pageParam: page = 1 }) =>
|
||||
getOrders({
|
||||
...variables,
|
||||
pagination: {
|
||||
page,
|
||||
pageSize,
|
||||
},
|
||||
});
|
||||
|
||||
return useInfiniteQuery({
|
||||
enabled,
|
||||
getNextPageParam: (lastPage, _allPages, lastPageParameter) => {
|
||||
if (!lastPage?.orders?.length) return undefined;
|
||||
|
||||
return lastPageParameter + 1;
|
||||
},
|
||||
initialPageParam: 1,
|
||||
queryFn,
|
||||
queryKey: ['orders', variables, 'infinite'],
|
||||
staleTime: 60 * 1_000,
|
||||
});
|
||||
};
|
||||
|
||||
export const useOrderMutation = ({
|
||||
documentId,
|
||||
}: Pick<Parameters<typeof updateOrder>[0], 'documentId'>) => {
|
||||
|
||||
23
apps/web/utils/actions.ts
Normal file
23
apps/web/utils/actions.ts
Normal file
@ -0,0 +1,23 @@
|
||||
/* eslint-disable unicorn/prevent-abbreviations */
|
||||
|
||||
export function wrapClientAction<Args extends unknown[], Return>(
|
||||
fn: (...args: Args) => Promise<{ data?: Return; error?: string; ok: boolean }>,
|
||||
): (...args: Args) => Promise<Return> {
|
||||
return async (...args: Args): Promise<Return> => {
|
||||
const res = await fn(...args);
|
||||
if (!res.ok) throw new Error(res.error ?? 'Неизвестная ошибка');
|
||||
return res.data as Return;
|
||||
};
|
||||
}
|
||||
|
||||
export async function wrapServerAction<T>(
|
||||
action: () => Promise<T>,
|
||||
): Promise<{ data: T; ok: true } | { error: string; ok: false }> {
|
||||
try {
|
||||
const data = await action();
|
||||
return { data, ok: true };
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Неизвестная ошибка сервера';
|
||||
return { error: message, ok: false };
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,11 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
# ports:
|
||||
# - 3000:3000
|
||||
networks:
|
||||
|
||||
@ -1,108 +0,0 @@
|
||||
/* eslint-disable complexity */
|
||||
import * as GQL from '../types';
|
||||
import { notifyByTelegramId } from '../utils/notify';
|
||||
import { BaseService } from './base';
|
||||
import { CustomersService } from './customers';
|
||||
import { OrdersService } from './orders';
|
||||
import { ServicesService } from './services';
|
||||
import { SlotsService } from './slots';
|
||||
import { type VariablesOf } from '@graphql-typed-document-node/core';
|
||||
import { formatDate, formatTime, getMinutes, sumTime } from '@repo/utils/datetime-format';
|
||||
|
||||
const STATE_MAP = {
|
||||
approved: 'Одобрено',
|
||||
cancelled: 'Отменено',
|
||||
cancelling: 'Отменяется',
|
||||
completed: 'Завершено',
|
||||
created: 'Создано',
|
||||
scheduled: 'Запланировано',
|
||||
unknown: 'Неизвестно',
|
||||
};
|
||||
export class NotifyService extends BaseService {
|
||||
async orderCreated(
|
||||
variables: VariablesOf<typeof GQL.CreateOrderDocument>,
|
||||
createdByMaster: boolean,
|
||||
) {
|
||||
const customersService = new CustomersService(this.customer);
|
||||
const slotsService = new SlotsService(this.customer);
|
||||
const servicesService = new ServicesService(this.customer);
|
||||
|
||||
const slotId = String(variables.input.slot ?? '');
|
||||
const serviceId = String(variables.input.services?.[0] ?? '');
|
||||
const clientId = String(variables.input.client ?? '');
|
||||
|
||||
if (!serviceId || !clientId || !slotId) return;
|
||||
|
||||
let emoji = '🆕';
|
||||
let confirmText = '';
|
||||
if (createdByMaster) {
|
||||
emoji = '✅';
|
||||
confirmText = ' и подтверждена';
|
||||
}
|
||||
|
||||
const { slot } = await slotsService.getSlot({ documentId: slotId });
|
||||
const { service } = await servicesService.getService({ documentId: serviceId });
|
||||
const { customer: master } = await customersService.getCustomer({
|
||||
documentId: slot?.master?.documentId ?? '',
|
||||
});
|
||||
const { customer: client } = await customersService.getCustomer({ documentId: clientId });
|
||||
|
||||
if (!slot?.datetime_start || !variables.input.datetime_start || !service?.duration) return;
|
||||
|
||||
const slotDate = formatDate(slot?.datetime_start).user();
|
||||
const timeStartString = formatTime(variables.input.datetime_start).user();
|
||||
const timeEndString = formatTime(
|
||||
sumTime(variables.input.datetime_start, getMinutes(service?.duration)),
|
||||
).user();
|
||||
|
||||
// Мастеру
|
||||
if (master?.telegramId) {
|
||||
const message = `${emoji} <b>Запись создана${confirmText}!</b>\n<b>Дата:</b> ${slotDate}\n<b>Время:</b> ${timeStartString} - ${timeEndString}\n<b>Клиент:</b> ${client?.name ?? '-'}\n<b>Услуга:</b> ${service?.name ?? '-'}`;
|
||||
await notifyByTelegramId(String(master.telegramId), message);
|
||||
}
|
||||
|
||||
// Клиенту
|
||||
if (client?.telegramId) {
|
||||
const message = `${emoji} <b>Запись создана${confirmText}!</b>\n<b>Дата:</b> ${slotDate}\n<b>Время:</b> ${timeStartString} - ${timeEndString}\n<b>Мастер:</b> ${master?.name ?? '-'}\n<b>Услуга:</b> ${service?.name ?? '-'}`;
|
||||
await notifyByTelegramId(String(client.telegramId), message);
|
||||
}
|
||||
}
|
||||
|
||||
async orderUpdated(variables: VariablesOf<typeof GQL.UpdateOrderDocument>) {
|
||||
// Получаем order через OrdersService
|
||||
const ordersService = new OrdersService(this.customer);
|
||||
const { order } = await ordersService.getOrder({ documentId: variables.documentId });
|
||||
if (!order) return;
|
||||
|
||||
const slot = order.slot;
|
||||
if (!slot) return;
|
||||
|
||||
const service = order.services[0];
|
||||
const master = slot?.master;
|
||||
const client = order.client;
|
||||
|
||||
const orderStateString = STATE_MAP[order.state || 'unknown'];
|
||||
const slotDate = formatDate(slot?.datetime_start).user();
|
||||
const timeStartString = formatTime(order.datetime_start ?? '').user();
|
||||
const timeEndString = formatTime(order.datetime_end ?? '').user();
|
||||
|
||||
let emoji = '✏️';
|
||||
if (order.state === GQL.Enum_Order_State.Cancelled) {
|
||||
emoji = '❌';
|
||||
} else if (order.state === GQL.Enum_Order_State.Approved) {
|
||||
emoji = '✅';
|
||||
}
|
||||
|
||||
// Мастеру
|
||||
if (master?.telegramId) {
|
||||
const message = `${emoji} <b>Запись изменена!</b>\n<b>Дата:</b> ${slotDate}\n<b>Время:</b> ${timeStartString} - ${timeEndString}\n<b>Клиент:</b> ${client?.name ?? '-'}\n<b>Услуга:</b> ${service?.name ?? '-'}\n<b>Статус:</b> ${orderStateString}`;
|
||||
await notifyByTelegramId(String(master.telegramId), message);
|
||||
}
|
||||
|
||||
// Клиенту
|
||||
if (client?.telegramId) {
|
||||
const message = `${emoji} <b>Запись изменена!</b>\n<b>Дата:</b> ${slotDate}\n<b>Время:</b> ${timeStartString} - ${timeEndString}\n<b>Мастер:</b> ${master?.name ?? '-'}\n<b>Услуга:</b> ${service?.name ?? '-'}\n<b>Статус:</b> ${orderStateString}`;
|
||||
await notifyByTelegramId(String(client.telegramId), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,6 @@ import { getClientWithToken } from '../apollo/client';
|
||||
import * as GQL from '../types';
|
||||
import { BaseService } from './base';
|
||||
import { CustomersService } from './customers';
|
||||
import { NotifyService } from './notify';
|
||||
import { ServicesService } from './services';
|
||||
import { type VariablesOf } from '@graphql-typed-document-node/core';
|
||||
import { isCustomerMaster } from '@repo/utils/customer';
|
||||
@ -64,10 +63,6 @@ export class OrdersService extends BaseService {
|
||||
const error = mutationResult.errors?.at(0);
|
||||
if (error) throw new Error(error.message);
|
||||
|
||||
// Уведомление об создании заказа
|
||||
const notifyService = new NotifyService(this.customer);
|
||||
notifyService.orderCreated(variables, isCustomerMaster(customer));
|
||||
|
||||
return mutationResult.data;
|
||||
}
|
||||
|
||||
@ -136,10 +131,6 @@ export class OrdersService extends BaseService {
|
||||
const error = mutationResult.errors?.at(0);
|
||||
if (error) throw new Error(error.message);
|
||||
|
||||
// Уведомление об изменении заказа
|
||||
const notifyService = new NotifyService(this.customer);
|
||||
notifyService.orderUpdated(variables);
|
||||
|
||||
return mutationResult.data;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
"dayjs": "catalog:",
|
||||
"jsonwebtoken": "catalog:",
|
||||
"radashi": "catalog:",
|
||||
"telegraf": "catalog:",
|
||||
"vite-tsconfig-paths": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"zod": "catalog:"
|
||||
|
||||
@ -317,9 +317,7 @@ export type OrderFiltersInput = {
|
||||
not?: InputMaybe<OrderFiltersInput>;
|
||||
or?: InputMaybe<Array<InputMaybe<OrderFiltersInput>>>;
|
||||
order_number?: InputMaybe<IntFilterInput>;
|
||||
price?: InputMaybe<IntFilterInput>;
|
||||
publishedAt?: InputMaybe<DateTimeFilterInput>;
|
||||
service_description?: InputMaybe<StringFilterInput>;
|
||||
services?: InputMaybe<ServiceFiltersInput>;
|
||||
slot?: InputMaybe<SlotFiltersInput>;
|
||||
state?: InputMaybe<StringFilterInput>;
|
||||
@ -332,9 +330,7 @@ export type OrderInput = {
|
||||
datetime_end?: InputMaybe<Scalars['DateTime']['input']>;
|
||||
datetime_start?: InputMaybe<Scalars['DateTime']['input']>;
|
||||
order_number?: InputMaybe<Scalars['Int']['input']>;
|
||||
price?: InputMaybe<Scalars['Int']['input']>;
|
||||
publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
||||
service_description?: InputMaybe<Scalars['String']['input']>;
|
||||
services?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>;
|
||||
slot?: InputMaybe<Scalars['ID']['input']>;
|
||||
state?: InputMaybe<Enum_Order_State>;
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
import { bot } from './telegram';
|
||||
|
||||
export async function notifyByTelegramId(telegramId: string | undefined, message: string) {
|
||||
if (!telegramId) return;
|
||||
|
||||
await bot.telegram.sendMessage(telegramId, message, {
|
||||
parse_mode: 'HTML',
|
||||
});
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
import { env as environment } from '../config/env';
|
||||
import { Telegraf } from 'telegraf';
|
||||
|
||||
export const bot = new Telegraf(environment.BOT_TOKEN);
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@ -299,9 +299,6 @@ importers:
|
||||
radashi:
|
||||
specifier: 'catalog:'
|
||||
version: 12.6.0
|
||||
telegraf:
|
||||
specifier: 'catalog:'
|
||||
version: 4.16.3
|
||||
vite-tsconfig-paths:
|
||||
specifier: 'catalog:'
|
||||
version: 5.1.4(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user