Compare commits

...

4 Commits

21 changed files with 261 additions and 280 deletions

View File

@ -98,5 +98,6 @@ jobs:
cd /home/${{ secrets.VPS_USER }}/zapishis && \ cd /home/${{ secrets.VPS_USER }}/zapishis && \
docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }} && \ docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }} && \
docker compose pull && \ docker compose pull && \
docker compose down && \
docker compose up -d docker compose up -d
" "

View File

@ -1,36 +1,8 @@
'use server'; import * as customers from './server/customers';
import { wrapClientAction } from '@/utils/actions';
import { useService } from './lib/service'; export const addMasters = wrapClientAction(customers.addMasters);
import { CustomersService } from '@repo/graphql/api/customers'; export const getClients = wrapClientAction(customers.getClients);
export const getCustomer = wrapClientAction(customers.getCustomer);
const getService = useService(CustomersService); export const getMasters = wrapClientAction(customers.getMasters);
export const updateCustomer = wrapClientAction(customers.updateCustomer);
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);
}

View File

@ -1,30 +1,7 @@
'use server'; import * as orders from './server/orders';
import { wrapClientAction } from '@/utils/actions';
import { useService } from './lib/service'; export const createOrder = wrapClientAction(orders.createOrder);
import { OrdersService } from '@repo/graphql/api/orders'; export const getOrder = wrapClientAction(orders.getOrder);
export const getOrders = wrapClientAction(orders.getOrders);
const getServicesService = useService(OrdersService); export const updateOrder = wrapClientAction(orders.updateOrder);
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);
}

View 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));
}

View 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));
}

View 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));
}

View 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));
}

View File

@ -1,18 +1,5 @@
'use server'; import * as services from './server/services';
import { wrapClientAction } from '@/utils/actions';
import { useService } from './lib/service'; export const getServices = wrapClientAction(services.getServices);
import { ServicesService } from '@repo/graphql/api/services'; export const getService = wrapClientAction(services.getService);
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);
}

View File

@ -1,44 +1,9 @@
'use server'; import * as slots from './server/slots';
import { wrapClientAction } from '@/utils/actions';
import { useService } from './lib/service'; export const getSlot = wrapClientAction(slots.getSlot);
import { SlotsService } from '@repo/graphql/api/slots'; export const getSlots = wrapClientAction(slots.getSlots);
export const createSlot = wrapClientAction(slots.createSlot);
const getService = useService(SlotsService); export const updateSlot = wrapClientAction(slots.updateSlot);
export const deleteSlot = wrapClientAction(slots.deleteSlot);
export async function createSlot(...variables: Parameters<SlotsService['createSlot']>) { export const getAvailableTimeSlots = wrapClientAction(slots.getAvailableTimeSlots);
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);
}

View File

@ -2,8 +2,9 @@
import { OrderCard } from '@/components/shared/order-card'; import { OrderCard } from '@/components/shared/order-card';
import { useCustomerQuery, useIsMaster } from '@/hooks/api/customers'; 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 { useDateTimeStore } from '@/stores/datetime';
import { Button } from '@repo/ui/components/ui/button';
import { getDateUTCRange } from '@repo/utils/datetime-format'; import { getDateUTCRange } from '@repo/utils/datetime-format';
export function ClientsOrdersList() { export function ClientsOrdersList() {
@ -14,7 +15,12 @@ export function ClientsOrdersList() {
const selectedDate = useDateTimeStore((store) => store.date); const selectedDate = useDateTimeStore((store) => store.date);
const { endOfDay, startOfDay } = getDateUTCRange(selectedDate).day(); const { endOfDay, startOfDay } = getDateUTCRange(selectedDate).day();
const { data: { orders } = {}, isLoading } = useOrdersQuery( const {
data: { pages } = {},
fetchNextPage,
hasNextPage,
isLoading,
} = useOrdersInfiniteQuery(
{ {
filters: { filters: {
slot: { slot: {
@ -31,19 +37,23 @@ export function ClientsOrdersList() {
}, },
}, },
}, },
pagination: {
limit: selectedDate ? undefined : 10,
}, },
}, { enabled: Boolean(customer?.documentId) && isMaster },
Boolean(customer?.documentId) && isMaster,
); );
const orders = pages?.flatMap((page) => page.orders) ?? [];
if (!orders?.length || isLoading || !isMaster) return null; if (!orders?.length || isLoading || !isMaster) return null;
return ( return (
<div className="flex flex-col space-y-2"> <div className="flex flex-col space-y-2">
<h1 className="font-bold">Записи клиентов</h1> <h1 className="font-bold">Записи клиентов</h1>
{orders?.map((order) => order && <OrderCard key={order.documentId} showDate {...order} />)} {orders?.map((order) => order && <OrderCard key={order.documentId} showDate {...order} />)}
{hasNextPage && (
<Button onClick={() => fetchNextPage()} variant="ghost">
Загрузить еще
</Button>
)}
</div> </div>
); );
} }
@ -54,7 +64,12 @@ export function OrdersList() {
const selectedDate = useDateTimeStore((store) => store.date); const selectedDate = useDateTimeStore((store) => store.date);
const { endOfDay, startOfDay } = getDateUTCRange(selectedDate).day(); const { endOfDay, startOfDay } = getDateUTCRange(selectedDate).day();
const { data: { orders } = {}, isLoading } = useOrdersQuery( const {
data: { pages } = {},
fetchNextPage,
hasNextPage,
isLoading,
} = useOrdersInfiniteQuery(
{ {
filters: { filters: {
client: { client: {
@ -71,13 +86,12 @@ export function OrdersList() {
: undefined, : undefined,
}, },
}, },
pagination: {
limit: selectedDate ? undefined : 10,
}, },
}, { enabled: Boolean(customer?.documentId) },
Boolean(customer?.documentId),
); );
const orders = pages?.flatMap((page) => page.orders) ?? [];
if (!orders?.length || isLoading) return null; if (!orders?.length || isLoading) return null;
return ( return (
@ -87,6 +101,11 @@ export function OrdersList() {
(order) => (order) =>
order && <OrderCard avatarSource="master" key={order.documentId} showDate {...order} />, order && <OrderCard avatarSource="master" key={order.documentId} showDate {...order} />,
)} )}
{hasNextPage && (
<Button onClick={() => fetchNextPage()} variant="ghost">
Загрузить еще
</Button>
)}
</div> </div>
); );
} }

View File

@ -3,7 +3,8 @@
import { OrderCard } from '../shared/order-card'; import { OrderCard } from '../shared/order-card';
import { type ProfileProps } from './types'; import { type ProfileProps } from './types';
import { useCustomerQuery, useIsMaster } from '@/hooks/api/customers'; 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>) { export function ProfileOrdersList({ telegramId }: Readonly<ProfileProps>) {
const { data: { customer } = {} } = useCustomerQuery(); const { data: { customer } = {} } = useCustomerQuery();
@ -11,7 +12,12 @@ export function ProfileOrdersList({ telegramId }: Readonly<ProfileProps>) {
const { data: { customer: profile } = {} } = useCustomerQuery({ telegramId }); const { data: { customer: profile } = {} } = useCustomerQuery({ telegramId });
const { data: { orders } = {}, isLoading } = useOrdersQuery( const {
data: { pages } = {},
fetchNextPage,
hasNextPage,
isLoading,
} = useOrdersInfiniteQuery(
{ {
filters: { filters: {
client: { client: {
@ -27,13 +33,12 @@ export function ProfileOrdersList({ telegramId }: Readonly<ProfileProps>) {
}, },
}, },
}, },
pagination: {
limit: 5,
}, },
}, { enabled: Boolean(profile?.documentId) && Boolean(customer?.documentId) },
Boolean(profile?.documentId) && Boolean(customer?.documentId),
); );
const orders = pages?.flatMap((page) => page.orders) ?? [];
if (!orders?.length || isLoading) return null; if (!orders?.length || isLoading) return null;
return ( return (
@ -50,6 +55,11 @@ export function ProfileOrdersList({ telegramId }: Readonly<ProfileProps>) {
/> />
), ),
)} )}
{hasNextPage && (
<Button onClick={() => fetchNextPage()} variant="ghost">
Загрузить еще
</Button>
)}
</div> </div>
); );
} }

View File

@ -1,7 +1,8 @@
/* eslint-disable unicorn/prevent-abbreviations */
'use client'; 'use client';
import { createOrder, getOrder, getOrders, updateOrder } from '@/actions/api/orders'; 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]) => export const useOrderQuery = ({ documentId }: Parameters<typeof getOrder>[0]) =>
useQuery({ useQuery({
@ -37,6 +38,33 @@ export const useOrdersQuery = (variables: Parameters<typeof getOrders>[0], enabl
staleTime: 60 * 1_000, 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 = ({ export const useOrderMutation = ({
documentId, documentId,
}: Pick<Parameters<typeof updateOrder>[0], 'documentId'>) => { }: Pick<Parameters<typeof updateOrder>[0], 'documentId'>) => {

23
apps/web/utils/actions.ts Normal file
View 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 };
}
}

View File

@ -4,6 +4,11 @@ services:
env_file: env_file:
- .env - .env
restart: always restart: always
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/"]
interval: 10s
timeout: 3s
retries: 5
# ports: # ports:
# - 3000:3000 # - 3000:3000
networks: networks:

View File

@ -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);
}
}
}

View File

@ -2,7 +2,6 @@ import { getClientWithToken } from '../apollo/client';
import * as GQL from '../types'; import * as GQL from '../types';
import { BaseService } from './base'; import { BaseService } from './base';
import { CustomersService } from './customers'; import { CustomersService } from './customers';
import { NotifyService } from './notify';
import { ServicesService } from './services'; import { ServicesService } from './services';
import { type VariablesOf } from '@graphql-typed-document-node/core'; import { type VariablesOf } from '@graphql-typed-document-node/core';
import { isCustomerMaster } from '@repo/utils/customer'; import { isCustomerMaster } from '@repo/utils/customer';
@ -64,10 +63,6 @@ export class OrdersService extends BaseService {
const error = mutationResult.errors?.at(0); const error = mutationResult.errors?.at(0);
if (error) throw new Error(error.message); if (error) throw new Error(error.message);
// Уведомление об создании заказа
const notifyService = new NotifyService(this.customer);
notifyService.orderCreated(variables, isCustomerMaster(customer));
return mutationResult.data; return mutationResult.data;
} }
@ -136,10 +131,6 @@ export class OrdersService extends BaseService {
const error = mutationResult.errors?.at(0); const error = mutationResult.errors?.at(0);
if (error) throw new Error(error.message); if (error) throw new Error(error.message);
// Уведомление об изменении заказа
const notifyService = new NotifyService(this.customer);
notifyService.orderUpdated(variables);
return mutationResult.data; return mutationResult.data;
} }
} }

View File

@ -16,7 +16,6 @@
"dayjs": "catalog:", "dayjs": "catalog:",
"jsonwebtoken": "catalog:", "jsonwebtoken": "catalog:",
"radashi": "catalog:", "radashi": "catalog:",
"telegraf": "catalog:",
"vite-tsconfig-paths": "catalog:", "vite-tsconfig-paths": "catalog:",
"vitest": "catalog:", "vitest": "catalog:",
"zod": "catalog:" "zod": "catalog:"

View File

@ -317,9 +317,7 @@ export type OrderFiltersInput = {
not?: InputMaybe<OrderFiltersInput>; not?: InputMaybe<OrderFiltersInput>;
or?: InputMaybe<Array<InputMaybe<OrderFiltersInput>>>; or?: InputMaybe<Array<InputMaybe<OrderFiltersInput>>>;
order_number?: InputMaybe<IntFilterInput>; order_number?: InputMaybe<IntFilterInput>;
price?: InputMaybe<IntFilterInput>;
publishedAt?: InputMaybe<DateTimeFilterInput>; publishedAt?: InputMaybe<DateTimeFilterInput>;
service_description?: InputMaybe<StringFilterInput>;
services?: InputMaybe<ServiceFiltersInput>; services?: InputMaybe<ServiceFiltersInput>;
slot?: InputMaybe<SlotFiltersInput>; slot?: InputMaybe<SlotFiltersInput>;
state?: InputMaybe<StringFilterInput>; state?: InputMaybe<StringFilterInput>;
@ -332,9 +330,7 @@ export type OrderInput = {
datetime_end?: InputMaybe<Scalars['DateTime']['input']>; datetime_end?: InputMaybe<Scalars['DateTime']['input']>;
datetime_start?: InputMaybe<Scalars['DateTime']['input']>; datetime_start?: InputMaybe<Scalars['DateTime']['input']>;
order_number?: InputMaybe<Scalars['Int']['input']>; order_number?: InputMaybe<Scalars['Int']['input']>;
price?: InputMaybe<Scalars['Int']['input']>;
publishedAt?: InputMaybe<Scalars['DateTime']['input']>; publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
service_description?: InputMaybe<Scalars['String']['input']>;
services?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>; services?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>;
slot?: InputMaybe<Scalars['ID']['input']>; slot?: InputMaybe<Scalars['ID']['input']>;
state?: InputMaybe<Enum_Order_State>; state?: InputMaybe<Enum_Order_State>;

View File

@ -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',
});
}

View File

@ -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
View File

@ -299,9 +299,6 @@ importers:
radashi: radashi:
specifier: 'catalog:' specifier: 'catalog:'
version: 12.6.0 version: 12.6.0
telegraf:
specifier: 'catalog:'
version: 4.16.3
vite-tsconfig-paths: vite-tsconfig-paths:
specifier: 'catalog:' specifier: 'catalog:'
version: 5.1.4(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10)) version: 5.1.4(typescript@5.8.3)(vite@5.4.19(@types/node@24.0.10))