Release/rc 1 (#87)

* feat(profile): implement local hooks for profile and service data editing

- Added `useProfileEdit` and `useServiceEdit` hooks to manage pending changes and save functionality for profile and service data cards.
- Updated `ProfileDataCard` and `ServiceDataCard` components to utilize these hooks, enhancing user experience with save and cancel options.
- Introduced buttons for saving and canceling changes, improving the overall interactivity of the forms.
- Refactored input handling to use `updateField` for better state management.

* feat(bot): integrate Redis and update bot configuration

- Added Redis service to both docker-compose files for local development and production environments.
- Updated bot configuration to utilize the Grammy framework, replacing Telegraf.
- Implemented graceful shutdown for the bot, ensuring proper resource management.
- Refactored bot commands and removed deprecated message handling logic.
- Enhanced environment variable management for Redis connection settings.
- Updated dependencies in package.json to include new Grammy-related packages.

* fix(registration): improve error handling for customer creation

- Updated error handling in the registration feature to return a generic error message when documentId is not present, enhancing user experience by providing clearer feedback.

* feat(bot): add unhandled command message and integrate unhandled feature

- Introduced a new message for unhandled commands in Russian localization to improve user feedback.
- Integrated the unhandled feature into the bot's middleware for better command handling.

* feat(locales): update Russian localization with additional contact information

- Enhanced the short description in the Russian localization file to include a contact note for user inquiries, improving user support accessibility.

* feat(help): enhance help command with support information

- Updated the help command to include a support message in the Russian localization, providing users with a contact point for inquiries.
- Improved the command response by combining the list of available commands with the new support information, enhancing user experience.

* fix(orders): update default sorting order for orders

- Changed the default sorting order for orders from 'datetime_start:asc' to 'datetime_start:desc' to ensure the most recent orders are displayed first, improving the user experience in order management.

* refactor(orders): remove ClientsOrdersList and streamline OrdersList component

- Eliminated the ClientsOrdersList component to simplify the orders page structure.
- Updated OrdersList to handle both client and master views, enhancing code reusability.
- Improved order fetching logic and UI rendering for better performance and user experience.

* fix(order-form): hide next button on success & error pages

* refactor(bot): streamline bot middleware and improve key generator function

- Removed unused session middleware and sequentialize function from the bot's error boundary.
- Simplified the key generator function for rate limiting by condensing its implementation.
- Enhanced overall code clarity and maintainability in the bot's configuration.

* feat(customer): implement banned customer check and enhance customer data handling

- Added `isCustomerBanned` function to determine if a customer is banned based on the `bannedUntil` field.
- Updated the `BaseService` to throw an error if a banned customer attempts to access certain functionalities.
- Enhanced the GraphQL operations to include the `bannedUntil` field in customer queries and mutations, improving data integrity and user experience.
- Integrated the `CheckBanned` component in the layout to manage banned customer states effectively.

* feat(ban-system): implement multi-level user ban checks across services

- Added a comprehensive ban checking system to prevent access for banned users at multiple levels, including database, API, and client-side.
- Introduced `bannedUntil` field in the customer model to manage temporary and permanent bans effectively.
- Enhanced `BaseService` and various service classes to include ban checks, ensuring that banned users cannot perform actions or access data.
- Updated error handling to provide consistent feedback for banned users across the application.
- Improved user experience with a dedicated ban check component and a user-friendly ban notification page.

* packages(apps/web): upgrade next@15.5.0
This commit is contained in:
Vlad Chikalkin 2025-08-26 13:23:52 +03:00 committed by GitHub
parent 2e849857f2
commit 81fa32c3d2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
67 changed files with 2606 additions and 1109 deletions

View File

@ -25,6 +25,7 @@ jobs:
echo "EMAIL_GRAPHQL=fake@example.com" >> .env
echo "NEXTAUTH_SECRET=fakesecret" >> .env
echo "BOT_URL=http://localhost:3000" >> .env
echo "REDIS_PASSWORD=fake" > .env
- name: Set image tags
id: vars
@ -83,6 +84,7 @@ jobs:
echo "WEB_IMAGE_TAG=${{ needs.build-and-push.outputs.web_tag }}" >> .env
echo "BOT_IMAGE_TAG=${{ needs.build-and-push.outputs.bot_tag }}" >> .env
echo "DOCKERHUB_USERNAME=${{ secrets.DOCKERHUB_USERNAME }}" >> .env
echo "REDIS_PASSWORD=${{ secrets.REDIS_PASSWORD }}" >> .env
- name: Copy .env to VPS via SCP
uses: appleboy/scp-action@master

View File

@ -5,5 +5,10 @@ export default [
...typescript,
{
ignores: ['**/types/**', '*.config.*'],
rules: {
'@typescript-eslint/naming-convention': 'off',
'unicorn/prevent-abbreviations': 'off',
'canonical/id-match': 'off',
},
},
];

105
apps/bot/locales/ru.ftl Normal file
View File

@ -0,0 +1,105 @@
# Описание бота
short-description =
Запись к мастерам, тренерам и репетиторам на вашем смартфоне 📱📅
По всем вопросам и обратной связи: @vchikalkin
description =
📲 Запишись.онлайн — это бесплатное Telegram-приложение для мастеров и тренеров в вашем смартфоне.
Возможности:
• 📅 Ведение графика и запись клиентов
• 👥 Клиентская база в одном месте
• 🔔 Уведомления о новых и предстоящих записях
• 🧑‍ Работа мастером или тренером прямо в Telegram
• 🚀 Создание записи на услугу в пару кликов
✨ Всё, что нужно — ваш смартфон.
По всем вопросам и обратной связи: @vchikalkin
# Команды
start =
.description = Запуск бота
addcontact =
.description = Добавить контакт клиента
becomemaster =
.description = Стать мастером
sharebot =
.description = Поделиться ботом
help =
.description = Список команд и поддержка
commands-list =
📋 Доступные команды:
• /addcontact — добавить контакт клиента
• /becomemaster — стать мастером
• /sharebot — поделиться ботом
• /help — список команд
Откройте приложение кнопкой "Открыть", чтобы отредактировать свой профиль или создать запись
support =
По всем вопросам и обратной связи: @vchikalkin
# Приветственные сообщения
msg-welcome =
👋 Добро пожаловать!
Пожалуйста, поделитесь своим номером телефона для регистрации
msg-welcome-back = 👋 С возвращением, { $name }!
# Сообщения о статусе мастера
msg-not-master =
⛔️ Только мастер может добавлять контакты
Стать мастером можно на странице профиля в приложении или с помощью команды /becomemaster
msg-already-master = 🎉 Вы уже являетесь мастером!
msg-become-master = 🥳 Поздравляем! Теперь вы мастер
# Сообщения о телефоне
msg-need-phone = 📱 Чтобы добавить контакт, сначала поделитесь своим номером телефона
msg-phone-saved =
✅ Спасибо! Мы сохранили ваш номер телефона
Теперь вы можете открыть приложение или воспользоваться командами бота
msg-already-registered =
✅ Вы уже зарегистрированы в системе
Для смены номера телефона обратитесь в поддержку (Контакты в профиле бота)
msg-invalid-phone = ❌ Некорректный номер телефона
# Сообщения о контактах
msg-send-client-contact =
👤 Отправьте контакт клиента, которого вы хотите добавить.
Для отмены операции используйте команду /cancel
msg-send-contact = Пожалуйста, отправьте контакт клиента через кнопку Telegram
msg-contact-added =
✅ Добавили { $name } в список ваших клиентов
Пригласите клиента в приложение, чтобы вы могли добавлять с ним записи
msg-contact-forward = Перешлите пользователю следующее сообщение, чтобы он мог начать пользоваться ботом ⬇️
# Сообщения для шаринга
msg-share-bot =
📅 Воспользуйтесь этим ботом для записи к вашему мастеру!
Нажмите кнопку ниже, чтобы начать
# Системные сообщения
msg-cancel = ❌ Операция отменена
msg-unhandled = ❓ Неизвестная команда. Попробуйте /start
# Ошибки
err-generic = ⚠️ Что-то пошло не так. Попробуйте еще раз через несколько секунд
err-banned = 🚫 Ваш аккаунт заблокирован
err-with-details = ❌ Произошла ошибка
{ $error }
err-limit-exceeded = 🚫 Слишком много запросов! Подождите немного

View File

@ -12,10 +12,23 @@
"lint-staged": "lint-staged"
},
"dependencies": {
"@grammyjs/auto-chat-action": "^0.1.1",
"@grammyjs/commands": "^1.2.0",
"@grammyjs/conversations": "^2.1.0",
"@grammyjs/hydrate": "^1.6.0",
"@grammyjs/i18n": "^1.1.2",
"@grammyjs/parse-mode": "^2.2.0",
"@grammyjs/ratelimiter": "^1.2.1",
"@grammyjs/runner": "^2.0.3",
"@grammyjs/storage-redis": "^2.5.1",
"@grammyjs/types": "^3.22.1",
"@repo/graphql": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/node": "catalog:",
"telegraf": "catalog:",
"grammy": "^1.38.1",
"ioredis": "^5.7.0",
"pino": "^9.9.0",
"pino-pretty": "^13.1.1",
"tsup": "^8.5.0",
"typescript": "catalog:",
"zod": "catalog:"

View File

@ -0,0 +1,21 @@
import { type logger } from '@/utils/logger';
import { type AutoChatActionFlavor } from '@grammyjs/auto-chat-action';
import { type CommandsFlavor } from '@grammyjs/commands';
import { type ConversationFlavor } from '@grammyjs/conversations';
import { type HydrateFlavor } from '@grammyjs/hydrate';
import { type I18nFlavor } from '@grammyjs/i18n';
import { type Context as DefaultContext, type SessionFlavor } from 'grammy';
export type Context = ConversationFlavor<
HydrateFlavor<
AutoChatActionFlavor &
CommandsFlavor &
DefaultContext &
I18nFlavor &
SessionFlavor<SessionData> & {
logger: typeof logger;
}
>
>;
export type SessionData = {};

View File

@ -0,0 +1,101 @@
/* eslint-disable id-length */
import { type Context } from '@/bot/context';
import { logHandle } from '@/bot/helpers/logging';
import { KEYBOARD_REMOVE, KEYBOARD_SHARE_BOT, KEYBOARD_SHARE_PHONE } from '@/config/keyboards';
import { isCustomerMaster } from '@/utils/customer';
import { isValidPhoneNumber, normalizePhoneNumber } from '@/utils/phone';
import { type Conversation, createConversation } from '@grammyjs/conversations';
import { CustomersService } from '@repo/graphql/api/customers';
import { RegistrationService } from '@repo/graphql/api/registration';
import { Composer } from 'grammy';
const composer = new Composer<Context>();
const feature = composer.chatType('private');
async function addContact(conversation: Conversation<Context, Context>, ctx: Context) {
// Проверяем, что пользователь является мастером
const telegramId = ctx.from?.id;
if (!telegramId) {
return ctx.reply(await conversation.external(({ t }) => t('err-generic')));
}
const customerService = new CustomersService({ telegramId });
const { customer } = await customerService.getCustomer({ telegramId });
if (!customer) {
return ctx.reply(
await conversation.external(({ t }) => t('msg-need-phone')),
KEYBOARD_SHARE_PHONE,
);
}
if (!isCustomerMaster(customer)) {
return ctx.reply(await conversation.external(({ t }) => t('msg-not-master')));
}
// Просим отправить контакт клиента
await ctx.reply(await conversation.external(({ t }) => t('msg-send-client-contact')));
// Ждем любое сообщение от пользователя
const waitCtx = await conversation.wait();
// Проверяем команду отмены
if (waitCtx.message?.text === '/cancel') {
return ctx.reply(await conversation.external(({ t }) => t('msg-cancel')));
}
// Проверяем, что отправлен контакт
if (!waitCtx.message?.contact) {
return ctx.reply(await conversation.external(({ t }) => t('msg-send-contact')));
}
const { contact } = waitCtx.message;
const name = `${contact.first_name || ''} ${contact.last_name || ''}`.trim();
const phone = normalizePhoneNumber(contact.phone_number);
// Проверяем валидность номера телефона
if (!isValidPhoneNumber(phone)) {
return ctx.reply(await conversation.external(({ t }) => t('msg-invalid-phone')));
}
try {
// Проверяем, есть ли клиент с таким номером
const { customer: existingCustomer } = await customerService.getCustomer({ phone });
let documentId = existingCustomer?.documentId;
// Если клиента нет, создаём нового
if (!documentId) {
const registrationService = new RegistrationService();
const createCustomerResult = await registrationService.createCustomer({ name, phone });
documentId = createCustomerResult?.createCustomer?.documentId;
if (!documentId) throw new Error('Клиент не создан');
}
// Добавляем текущего мастера к клиенту
const masters = [customer.documentId];
await customerService.addMasters({ data: { masters }, documentId });
// Отправляем подтверждения и инструкции
await ctx.reply(await conversation.external(({ t }) => t('msg-contact-added', { name })));
await ctx.reply(await conversation.external(({ t }) => t('msg-contact-forward')));
await ctx.reply(await conversation.external(({ t }) => t('msg-share-bot')), KEYBOARD_SHARE_BOT);
} catch (error) {
await ctx.reply(
await conversation.external(({ t }) => t('err-with-details', { error: String(error) })),
);
} finally {
await ctx.reply(await conversation.external(({ t }) => t('commands-list')), KEYBOARD_REMOVE);
}
return ctx.reply(await conversation.external(({ t }) => t('err-generic')), KEYBOARD_REMOVE);
}
feature.use(createConversation(addContact));
feature.command('addcontact', logHandle('command-add-contact'), async (ctx) => {
await ctx.conversation.enter('addContact');
});
export { composer as addContact };

View File

@ -0,0 +1,42 @@
import { type Context } from '@/bot/context';
import { logHandle } from '@/bot/helpers/logging';
import { KEYBOARD_SHARE_PHONE } from '@/config/keyboards';
import { isCustomerMaster } from '@/utils/customer';
import { CustomersService } from '@repo/graphql/api/customers';
import { Enum_Customer_Role } from '@repo/graphql/types';
import { Composer } from 'grammy';
const composer = new Composer<Context>();
const feature = composer.chatType('private');
feature.command('becomemaster', logHandle('command-become-master'), async (ctx) => {
const telegramId = ctx.from.id;
const customerService = new CustomersService({ telegramId });
const { customer } = await customerService.getCustomer({ telegramId });
if (!customer) {
return ctx.reply(ctx.t('msg-need-phone'), { ...KEYBOARD_SHARE_PHONE, parse_mode: 'HTML' });
}
if (isCustomerMaster(customer)) {
return ctx.reply(ctx.t('msg-already-master'), { parse_mode: 'HTML' });
}
// Обновляем роль клиента на мастер
const response = await customerService
.updateCustomer({
data: { role: Enum_Customer_Role.Master },
})
.catch((error) => {
ctx.reply(ctx.t('err-with-details', { error: String(error) }), { parse_mode: 'HTML' });
});
if (response) {
return ctx.reply(ctx.t('msg-become-master'), { parse_mode: 'HTML' });
}
return ctx.reply(ctx.t('err-generic'), { parse_mode: 'HTML' });
});
export { composer as becomeMaster };

View File

@ -0,0 +1,18 @@
import { type Context } from '@/bot/context';
import { logHandle } from '@/bot/helpers/logging';
import { KEYBOARD_REMOVE } from '@/config/keyboards';
import { combine } from '@/utils/messages';
import { Composer } from 'grammy';
const composer = new Composer<Context>();
const feature = composer.chatType('private');
feature.command('help', logHandle('command-help'), async (ctx) => {
return ctx.reply(combine(ctx.t('commands-list'), ctx.t('support')), {
...KEYBOARD_REMOVE,
parse_mode: 'HTML',
});
});
export { composer as help };

View File

@ -0,0 +1,6 @@
export * from './add-contact';
export * from './become-master';
export * from './help';
export * from './registration';
export * from './share-bot';
export * from './welcome';

View File

@ -0,0 +1,79 @@
import { type Context } from '@/bot/context';
import { logHandle } from '@/bot/helpers/logging';
import { KEYBOARD_REMOVE } from '@/config/keyboards';
import { isValidPhoneNumber, normalizePhoneNumber } from '@/utils/phone';
import { CustomersService } from '@repo/graphql/api/customers';
import { RegistrationService } from '@repo/graphql/api/registration';
import { Composer } from 'grammy';
const composer = new Composer<Context>();
const feature = composer.chatType('private');
// Обработка получения контакта от пользователя (регистрация или обновление)
feature.on(':contact', logHandle('contact-registration'), async (ctx) => {
const telegramId = ctx.from.id;
const { contact } = ctx.message;
const name = `${contact.first_name || ''} ${contact.last_name || ''}`.trim();
// Проверяем, не зарегистрирован ли уже пользователь
const customerService = new CustomersService({ telegramId });
const { customer: existingCustomer } = await customerService.getCustomer({ telegramId });
if (existingCustomer) {
return ctx.reply(ctx.t('msg-already-registered'), {
...KEYBOARD_REMOVE,
parse_mode: 'HTML',
});
}
// Проверка наличия номера телефона
if (!contact.phone_number) {
return ctx.reply(ctx.t('msg-invalid-phone'));
}
// Нормализация и валидация номера
const phone = normalizePhoneNumber(contact.phone_number);
if (!isValidPhoneNumber(phone)) {
return ctx.reply(ctx.t('msg-invalid-phone'));
}
const registrationService = new RegistrationService();
try {
const { customer } = await registrationService.getCustomer({ phone });
if (customer && !customer.telegramId) {
// Пользователь добавлен ранее мастером — обновляем данные
await registrationService.updateCustomer({
data: { active: true, name, telegramId },
documentId: customer.documentId,
});
return ctx.reply(ctx.t('msg-phone-saved') + '\n\n' + ctx.t('commands-list'), {
...KEYBOARD_REMOVE,
parse_mode: 'HTML',
});
}
// Новый пользователь — создаём и активируем
const response = await registrationService.createCustomer({ name, phone, telegramId });
const documentId = response?.createCustomer?.documentId;
if (!documentId) return ctx.reply(ctx.t('err-generic'));
await registrationService.updateCustomer({
data: { active: true },
documentId,
});
return ctx.reply(ctx.t('msg-phone-saved') + '\n\n' + ctx.t('commands-list'), {
...KEYBOARD_REMOVE,
parse_mode: 'HTML',
});
} catch (error) {
return ctx.reply(ctx.t('err-with-details', { error: String(error) }));
}
});
export { composer as registration };

View File

@ -0,0 +1,15 @@
import { type Context } from '@/bot/context';
import { logHandle } from '@/bot/helpers/logging';
import { KEYBOARD_SHARE_BOT } from '@/config/keyboards';
import { Composer } from 'grammy';
const composer = new Composer<Context>();
const feature = composer.chatType('private');
feature.command('sharebot', logHandle('command-share-bot'), async (ctx) => {
await ctx.reply(ctx.t('msg-contact-forward'), { parse_mode: 'HTML' });
await ctx.reply(ctx.t('msg-share-bot'), { ...KEYBOARD_SHARE_BOT, parse_mode: 'HTML' });
});
export { composer as shareBot };

View File

@ -0,0 +1,17 @@
import { type Context } from '@/bot/context';
import { logHandle } from '@/bot/helpers/logging';
import { Composer } from 'grammy';
const composer = new Composer<Context>();
const feature = composer.chatType('private');
feature.on('message', logHandle('unhandled-message'), (ctx) => {
return ctx.reply(ctx.t('msg-unhandled'));
});
feature.on('callback_query', logHandle('unhandled-callback-query'), (ctx) => {
return ctx.answerCallbackQuery();
});
export { composer as unhandledFeature };

View File

@ -0,0 +1,33 @@
import { type Context } from '@/bot/context';
import { logHandle } from '@/bot/helpers/logging';
import { KEYBOARD_REMOVE, KEYBOARD_SHARE_PHONE } from '@/config/keyboards';
import { combine } from '@/utils/messages';
import { CustomersService } from '@repo/graphql/api/customers';
import { Composer } from 'grammy';
const composer = new Composer<Context>();
const feature = composer.chatType('private');
feature.command('start', logHandle('command-start'), async (ctx) => {
const telegramId = ctx.from.id;
const customerService = new CustomersService({ telegramId });
const { customer } = await customerService.getCustomer({ telegramId });
if (customer) {
// Пользователь уже зарегистрирован — приветствуем
return ctx.reply(
combine(ctx.t('msg-welcome-back', { name: customer.name }), ctx.t('commands-list')),
{
...KEYBOARD_REMOVE,
parse_mode: 'HTML',
},
);
}
// Новый пользователь — просим поделиться номером
return ctx.reply(ctx.t('msg-welcome'), { ...KEYBOARD_SHARE_PHONE, parse_mode: 'HTML' });
});
export { composer as welcome };

View File

@ -0,0 +1,18 @@
import { type Context } from '../context';
import { getUpdateInfo } from '../helpers/logging';
import { KEYBOARD_REMOVE } from '@/config/keyboards';
import { ERRORS } from '@repo/graphql/constants/errors';
import { type ErrorHandler } from 'grammy';
export const errorHandler: ErrorHandler<Context> = async (error) => {
const { ctx } = error;
const text = error.message.includes(ERRORS.NO_PERMISSION) ? 'err-banned' : 'err-generic';
await ctx.reply(ctx.t(text), { ...KEYBOARD_REMOVE, parse_mode: 'HTML' });
ctx.logger.error({
err: error.error,
update: getUpdateInfo(ctx),
});
};

View File

@ -0,0 +1,21 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { type Context } from '../context';
import { type Update } from '@grammyjs/types';
import { type Middleware } from 'grammy';
export function getUpdateInfo(context: Context): Omit<Update, 'update_id'> {
const { update_id, ...update } = context.update;
return update;
}
export function logHandle(id: string): Middleware<Context> {
return (context, next) => {
context.logger.info({
msg: `Handle "${id}"`,
...(id.startsWith('unhandled') ? { update: getUpdateInfo(context) } : {}),
});
return next();
};
}

14
apps/bot/src/bot/i18n.ts Normal file
View File

@ -0,0 +1,14 @@
import { type Context } from './context';
import { I18n } from '@grammyjs/i18n';
import path from 'node:path';
export const i18n = new I18n<Context>({
defaultLocale: 'en',
directory: path.resolve(process.cwd(), 'locales'),
fluentBundleOptions: {
useIsolating: false,
},
useSession: true,
});
export const isMultipleLocales = i18n.locales.length > 1;

66
apps/bot/src/bot/index.ts Normal file
View File

@ -0,0 +1,66 @@
import { type Context } from './context';
import * as features from './features';
import { unhandledFeature } from './features/unhandled';
import { errorHandler } from './handlers/errors';
import { i18n } from './i18n';
import * as middlewares from './middlewares';
import { setCommands } from './settings/commands';
import { setInfo } from './settings/info';
import { env } from '@/config/env';
import { logger } from '@/utils/logger';
import { getRedisInstance } from '@/utils/redis';
import { autoChatAction } from '@grammyjs/auto-chat-action';
import { conversations } from '@grammyjs/conversations';
import { hydrate } from '@grammyjs/hydrate';
import { limit } from '@grammyjs/ratelimiter';
import { Bot } from 'grammy';
type Parameters_ = {
token: string;
};
const redis = getRedisInstance();
export function createBot({ token }: Parameters_) {
const bot = new Bot<Context>(token);
bot.use(i18n);
bot.use(
limit({
keyGenerator: (ctx) => ctx.from?.id.toString(),
limit: env.RATE_LIMIT,
onLimitExceeded: async (ctx) => {
await ctx.reply(ctx.t('err-limit-exceeded'));
},
storageClient: redis,
timeFrame: env.RATE_LIMIT_TIME,
}),
);
bot.use(async (context, next) => {
context.logger = logger.child({
update_id: context.update.update_id,
});
await next();
});
bot.use(conversations());
setInfo(bot);
setCommands(bot);
const protectedBot = bot.errorBoundary(errorHandler);
protectedBot.use(middlewares.updateLogger());
protectedBot.use(autoChatAction(bot.api));
protectedBot.use(hydrate());
for (const feature of Object.values(features)) {
protectedBot.use(feature);
}
protectedBot.use(unhandledFeature);
return bot;
}

View File

@ -0,0 +1,2 @@
export * from './session';
export * from './update-logger';

View File

@ -0,0 +1,20 @@
import { type Context } from '@/bot/context';
import { TTL_SESSION } from '@/config/redis';
import { getRedisInstance } from '@/utils/redis';
import { getSessionKey } from '@/utils/session';
import { RedisAdapter } from '@grammyjs/storage-redis';
import { session as createSession, type Middleware } from 'grammy';
const storage = new RedisAdapter({
autoParseDates: true,
instance: getRedisInstance(),
ttl: TTL_SESSION,
});
export function session(): Middleware<Context> {
return createSession({
getSessionKey,
initial: () => ({}),
storage,
});
}

View File

@ -0,0 +1,34 @@
import { type Context } from '@/bot/context';
import { getUpdateInfo } from '@/bot/helpers/logging';
import { type Middleware } from 'grammy';
import { performance } from 'node:perf_hooks';
export function updateLogger(): Middleware<Context> {
return async (ctx, next) => {
ctx.api.config.use((previous, method, payload, signal) => {
ctx.logger.debug({
method,
msg: 'Bot API call',
payload,
});
return previous(method, payload, signal);
});
ctx.logger.debug({
msg: 'Update received',
update: getUpdateInfo(ctx),
});
const startTime = performance.now();
try {
return next();
} finally {
const endTime = performance.now();
ctx.logger.debug({
elapsed: endTime - startTime,
msg: 'Update processed',
});
}
};
}

View File

@ -0,0 +1,39 @@
import { type Context } from '@/bot/context';
import { i18n } from '@/bot/i18n';
import { Command, CommandGroup } from '@grammyjs/commands';
import { type LanguageCode } from '@grammyjs/types';
import { type Api, type Bot, type RawApi } from 'grammy';
export async function setCommands({ api }: Bot<Context, Api<RawApi>>) {
const commands = createCommands(['start', 'addcontact', 'becomemaster', 'sharebot', 'help']);
for (const command of commands) {
addLocalizations(command);
}
const commandsGroup = new CommandGroup().add(commands);
await commandsGroup.setCommands({ api });
}
function addLocalizations(command: Command) {
for (const locale of i18n.locales) {
command.localize(
locale as LanguageCode,
command.name,
i18n.t(locale, `${command.name}.description`),
);
}
return command;
}
function createCommand(name: string) {
return new Command(name, i18n.t('en', `${name}.description`)).addToScope({
type: 'all_private_chats',
});
}
function createCommands(names: string[]) {
return names.map((name) => createCommand(name));
}

View File

@ -0,0 +1,2 @@
export * from './commands';
export * from './info';

View File

@ -0,0 +1,10 @@
import { type Context } from '../context';
import { i18n } from '../i18n';
import { type Api, type Bot, type RawApi } from 'grammy';
export async function setInfo({ api }: Bot<Context, Api<RawApi>>) {
for (const locale of i18n.locales) {
await api.setMyDescription(i18n.t(locale, 'description'));
await api.setMyShortDescription(i18n.t(locale, 'short-description'));
}
}

View File

@ -1,9 +1,22 @@
/* eslint-disable unicorn/prevent-abbreviations */
import { z } from 'zod';
export const envSchema = z.object({
BOT_TOKEN: z.string(),
BOT_URL: z.string(),
RATE_LIMIT: z
.string()
.transform((value) => Number.parseInt(value, 10))
.default('2'),
RATE_LIMIT_TIME: z
.string()
.transform((value) => Number.parseInt(value, 10))
.default('3000'),
REDIS_HOST: z.string().default('redis'),
REDIS_PASSWORD: z.string(),
REDIS_PORT: z
.string()
.transform((value) => Number.parseInt(value, 10))
.default('6379'),
});
export const env = envSchema.parse(process.env);

View File

@ -0,0 +1,38 @@
import {
type InlineKeyboardMarkup,
type ReplyKeyboardMarkup,
type ReplyKeyboardRemove,
} from '@grammyjs/types';
export const KEYBOARD_SHARE_PHONE = {
reply_markup: {
keyboard: [
[
{
request_contact: true,
text: ' Отправить номер телефона',
},
],
],
one_time_keyboard: true,
} as ReplyKeyboardMarkup,
};
export const KEYBOARD_REMOVE = {
reply_markup: {
remove_keyboard: true,
} as ReplyKeyboardRemove,
};
export const KEYBOARD_SHARE_BOT = {
reply_markup: {
inline_keyboard: [
[
{
text: ' Воспользоваться ботом',
url: process.env.BOT_URL as string,
},
],
],
} as InlineKeyboardMarkup,
};

View File

@ -0,0 +1 @@
export const TTL_SESSION = 5 * 60; // 5 minutes in seconds

View File

@ -1,276 +1,46 @@
/* eslint-disable canonical/id-match */
/* eslint-disable consistent-return */
import { createBot } from './bot';
import { env as environment } from './config/env';
import {
commandsList,
KEYBOARD_REMOVE,
KEYBOARD_SHARE_BOT,
KEYBOARD_SHARE_PHONE,
MESSAGE_CANCEL,
MESSAGE_INVALID_PHONE,
MESSAGE_NOT_MASTER,
MESSAGE_SHARE_BOT,
MSG_ALREADY_MASTER,
MSG_BECOME_MASTER,
MSG_CONTACT_ADDED,
MSG_CONTACT_FORWARD,
MSG_ERROR,
MSG_NEED_PHONE,
MSG_PHONE_SAVED,
MSG_SEND_CLIENT_CONTACT,
MSG_WELCOME,
MSG_WELCOME_BACK,
} from './message';
import { isCustomerMaster } from './utils/customer';
import { isValidPhoneNumber, normalizePhoneNumber } from './utils/phone';
import { CustomersService } from '@repo/graphql/api/customers';
import { RegistrationService } from '@repo/graphql/api/registration';
import { Enum_Customer_Role } from '@repo/graphql/types';
import { Scenes, session, Telegraf, type Context as TelegrafContext } from 'telegraf';
import { message } from 'telegraf/filters';
import {
type SceneContextScene,
type SceneSession,
type WizardContextWizard,
type WizardSessionData,
} from 'telegraf/typings/scenes';
import { logger } from './utils/logger';
import { getRedisInstance } from './utils/redis';
import { run } from '@grammyjs/runner';
// Расширяем контекст бота для работы с сценами и сессиями
type BotContext = TelegrafContext & {
scene: SceneContextScene<BotContext, WizardSessionData>;
session: SceneSession<WizardSessionData>;
wizard: WizardContextWizard<BotContext>;
};
// Создаём экземпляр бота с токеном
const bot = new Telegraf<BotContext>(environment.BOT_TOKEN);
// Создаём менеджер сцен и подключаем сессию
const stage = new Scenes.Stage<BotContext>();
bot.use(session({ defaultSession: () => ({ __scenes: { cursor: 0, state: {} } }) }));
bot.use(stage.middleware());
// Сцена добавления контакта клиента мастером
const addContactScene = new Scenes.WizardScene<BotContext>(
'add-contact',
// Шаг 1: Просим отправить контакт клиента
async (context) => {
await context.reply(MSG_SEND_CLIENT_CONTACT, { parse_mode: 'HTML' });
return context.wizard.next();
},
// Шаг 2: Обрабатываем полученный контакт
async (context) => {
if (!context.from) {
await context.reply('Ошибка: не удалось определить пользователя');
return context.scene.leave();
}
// Команда отмены — выход из сцены
if (context.message && 'text' in context.message && context.message.text === '/cancel') {
await context.reply(MESSAGE_CANCEL + commandsList, { parse_mode: 'HTML' });
return context.scene.leave();
}
// Проверяем, что отправлен контакт (через кнопку Telegram)
if (!context.message || !('contact' in context.message)) {
await context.reply('Пожалуйста, отправьте контакт клиента через кнопку Telegram');
return; // остаёмся в сцене, ждем корректный контакт
}
const telegramId = context.from.id;
const customerService = new CustomersService({ telegramId });
// Проверяем, что текущий пользователь — мастер
const { customer } = await customerService.getCustomer({ telegramId });
if (!customer || !isCustomerMaster(customer)) {
await context.reply(MESSAGE_NOT_MASTER, { parse_mode: 'HTML' });
return context.scene.leave();
}
const { contact } = context.message;
const name = `${contact.first_name || ''} ${contact.last_name || ''}`.trim();
const phone = normalizePhoneNumber(contact.phone_number);
// Проверяем валидность номера телефона
if (!isValidPhoneNumber(phone)) {
await context.reply(MESSAGE_INVALID_PHONE, { parse_mode: 'HTML' });
return; // остаёмся в сцене, ждем правильный номер
}
try {
// Проверяем, есть ли клиент с таким номером
const { customer: existingCustomer } = await customerService.getCustomer({ phone });
let documentId = existingCustomer?.documentId;
// Если клиента нет, создаём нового
if (!documentId) {
const registrationService = new RegistrationService();
const createCustomerResult = await registrationService.createCustomer({ name, phone });
documentId = createCustomerResult?.createCustomer?.documentId;
if (!documentId) throw new Error('Клиент не создан');
}
// Добавляем текущего мастера к клиенту
const masters = [customer.documentId];
await customerService.addMasters({ data: { masters }, documentId });
// Отправляем подтверждения и инструкции
await context.reply(MSG_CONTACT_ADDED(name), { parse_mode: 'HTML' });
await context.reply(MSG_CONTACT_FORWARD, { parse_mode: 'HTML' });
await context.reply(MESSAGE_SHARE_BOT, { ...KEYBOARD_SHARE_BOT, parse_mode: 'HTML' });
} catch (error) {
await context.reply(MSG_ERROR(error), { parse_mode: 'HTML' });
} finally {
await context.reply(commandsList, { ...KEYBOARD_REMOVE, parse_mode: 'HTML' });
context.scene.leave();
}
},
);
// Регистрируем сцену
stage.register(addContactScene);
// Команда /start — приветствие и запрос номера, если пользователь новый
bot.start(async (context) => {
const telegramId = context.from.id;
const customerService = new CustomersService({ telegramId });
const { customer } = await customerService.getCustomer({ telegramId });
if (customer) {
// Пользователь уже зарегистрирован — приветствуем
return context.reply(MSG_WELCOME_BACK(customer.name) + commandsList, {
...KEYBOARD_REMOVE,
parse_mode: 'HTML',
});
}
// Новый пользователь — просим поделиться номером
return context.reply(MSG_WELCOME, { ...KEYBOARD_SHARE_PHONE, parse_mode: 'HTML' });
const bot = createBot({
token: environment.BOT_TOKEN,
});
// Команда /help — список команд
bot.command('help', async (context) => {
return context.reply(commandsList, { ...KEYBOARD_REMOVE, parse_mode: 'HTML' });
bot.catch((error) => {
logger.error('Grammy bot error:');
logger.error(`Message: ${error?.message}`);
logger.error(error.error);
});
// Команда /addcontact — начать сцену добавления контакта
bot.command('addcontact', async (context) => {
const telegramId = context.from.id;
const customerService = new CustomersService({ telegramId });
const { customer } = await customerService.getCustomer({ telegramId });
const runner = run(bot);
const redis = getRedisInstance();
if (!customer) {
// Нет номера — просим поделиться
return context.reply(MSG_NEED_PHONE, { ...KEYBOARD_SHARE_PHONE, parse_mode: 'HTML' });
}
if (!isCustomerMaster(customer)) {
// Нет прав мастера
return context.reply(MESSAGE_NOT_MASTER, { parse_mode: 'HTML' });
}
// Входим в сцену
return context.scene.enter('add-contact');
});
// Команда /becomemaster — запрос статуса мастера
bot.command('becomemaster', async (context) => {
const telegramId = context.from.id;
const customerService = new CustomersService({ telegramId });
const { customer } = await customerService.getCustomer({ telegramId });
if (!customer) {
return context.reply(MSG_NEED_PHONE, { ...KEYBOARD_SHARE_PHONE, parse_mode: 'HTML' });
}
if (isCustomerMaster(customer)) {
return context.reply(MSG_ALREADY_MASTER, { parse_mode: 'HTML' });
}
// Обновляем роль клиента на мастер
const response = await customerService
.updateCustomer({
data: { role: Enum_Customer_Role.Master },
})
.catch((error) => {
context.reply(MSG_ERROR(error), { parse_mode: 'HTML' });
});
if (response) {
return context.reply(MSG_BECOME_MASTER, { parse_mode: 'HTML' });
}
});
// Команда /sharebot — прислать ссылку на бота
bot.command('sharebot', async (context) => {
await context.reply(MSG_CONTACT_FORWARD, { parse_mode: 'HTML' });
await context.reply(MESSAGE_SHARE_BOT, { ...KEYBOARD_SHARE_BOT, parse_mode: 'HTML' });
});
// Обработка получения контакта от пользователя (регистрация или обновление)
bot.on(message('contact'), async (context) => {
const telegramId = context.from.id;
const { contact } = context.message;
const name = `${contact.first_name || ''} ${contact.last_name || ''}`.trim();
// Проверка наличия номера телефона
if (!contact.phone_number) {
return context.reply(MESSAGE_INVALID_PHONE, { parse_mode: 'HTML' });
}
// Нормализация и валидация номера
const phone = normalizePhoneNumber(contact.phone_number);
if (!isValidPhoneNumber(phone)) {
return context.reply(MESSAGE_INVALID_PHONE, { parse_mode: 'HTML' });
}
const registrationService = new RegistrationService();
async function gracefulShutdown(signal: string) {
logger.info(`Received ${signal}, starting graceful shutdown...`);
try {
const { customer } = await registrationService.getCustomer({ phone });
await runner.stop();
logger.info('Bot stopped');
if (customer && !customer.telegramId) {
// Пользователь добавлен ранее мастером — обновляем данные
await registrationService.updateCustomer({
data: { active: true, name, telegramId },
documentId: customer.documentId,
});
return context.reply(MSG_PHONE_SAVED + commandsList, {
...KEYBOARD_REMOVE,
parse_mode: 'HTML',
});
}
// Новый пользователь — создаём и активируем
const response = await registrationService.createCustomer({ name, phone, telegramId });
const documentId = response?.createCustomer?.documentId;
if (!documentId) {
throw new Error('Не удалось создать клиента: отсутствует documentId');
}
await registrationService.updateCustomer({
data: { active: true },
documentId,
});
return context.reply(MSG_PHONE_SAVED + commandsList, {
...KEYBOARD_REMOVE,
parse_mode: 'HTML',
});
redis.disconnect();
logger.info('Redis disconnected');
} catch (error) {
return context.reply(MSG_ERROR(error), { parse_mode: 'HTML' });
const err_ = error as Error;
logger.error('Error during graceful shutdown:' + err_.message || '');
}
}
process.once('SIGINT', () => gracefulShutdown('SIGINT'));
process.once('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('unhandledRejection', (reason) => {
logger.error('Unhandled Rejection: ' + reason);
});
// Запуск бота
bot.launch();
process.on('uncaughtException', (error) => {
logger.error('Uncaught Exception: ' + error);
});
// Корректное завершение работы
process.once('SIGINT', () => bot.stop('SIGINT'));
process.once('SIGTERM', () => bot.stop('SIGTERM'));
logger.info('Bot started');

View File

@ -1,83 +0,0 @@
import { env as environment } from './config/env';
import { type ReplyKeyboardRemove } from 'telegraf/types';
export const commandsList = `
\n<b>📋 Доступные команды:</b>
<b>/addcontact</b> добавить контакт клиента
<b>/becomemaster</b> стать мастером
<b>/sharebot</b> поделиться ботом
<b>/help</b> список команд
\n
Откройте приложение кнопкой <b>"Открыть"</b>, чтобы отредактировать свой профиль или создать запись
`;
export const KEYBOARD_SHARE_PHONE = {
reply_markup: {
keyboard: [
[
{
request_contact: true,
text: '📱 Отправить номер телефона',
},
],
],
one_time_keyboard: true,
},
};
export const KEYBOARD_REMOVE = {
reply_markup: {
remove_keyboard: true,
} as ReplyKeyboardRemove,
};
export const KEYBOARD_SHARE_BOT = {
reply_markup: {
inline_keyboard: [
[
{
text: '🤖Воспользоваться ботом',
url: environment.BOT_URL,
},
],
],
},
};
export const MESSAGE_NOT_MASTER =
'⛔️ <b>Только мастер может добавлять контакты</b>\nСтать мастером можно на странице профиля в приложении или с помощью команды <b>/becomemaster</b>';
export const MSG_WELCOME =
'👋 <b>Добро пожаловать!</b>\nПожалуйста, поделитесь своим номером телефона для регистрации';
export const MSG_WELCOME_BACK = (name: string) =>
`👋 <b>С возвращением, ${name}!</b>`;
export const MSG_NEED_PHONE =
'📱 <b>Чтобы добавить контакт, сначала поделитесь своим номером телефона</b>';
export const MSG_SEND_CLIENT_CONTACT =
'👤 <b>Отправьте контакт клиента, которого вы хотите добавить. \n<em>Для отмены операции используйте команду /cancel</em></b>';
export const MSG_ALREADY_MASTER = '🎉 <b>Вы уже являетесь мастером!</b>';
export const MSG_BECOME_MASTER = '🥳 <b>Поздравляем! Теперь вы мастер</b>';
export const MSG_ERROR = (error?: unknown) =>
`❌ <b>Произошла ошибка</b>\n${error ? String(error) : ''}`;
export const MSG_PHONE_SAVED =
'✅ <b>Спасибо! Мы сохранили ваш номер телефона</b>\nТеперь вы можете открыть приложение или воспользоваться командами бота';
export const MSG_CONTACT_ADDED = (name: string) =>
`✅ <b>Добавили <b>${name}</b> в список ваших клиентов</b>\n\ригласите клиента в приложение, чтобы вы могли добавлять с ним записи`;
export const MSG_CONTACT_FORWARD =
'<em>Перешлите пользователю следующее сообщение, чтобы он мог начать пользоваться ботом ⬇️</em>';
export const MESSAGE_SHARE_BOT =
'📅 <b>Воспользуйтесь этим ботом для записи к вашему мастеру!</b>\nНажмите кнопку ниже, чтобы начать';
export const MESSAGE_CANCEL = '<b>❌ Отменена операции</b>';
export const MESSAGE_INVALID_PHONE = '❌ <b>Некорректный номер телефона</b>';

View File

@ -0,0 +1,13 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import pino from 'pino';
export const logger = pino({
transport: {
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
options: {
colorize: true,
translateTime: true,
},
target: 'pino-pretty',
},
});

View File

@ -0,0 +1,3 @@
export function combine(...messages: string[]) {
return messages.join('\n\n');
}

View File

@ -0,0 +1,23 @@
import { env } from '@/config/env';
import { logger } from '@/utils/logger';
import Redis from 'ioredis';
const instance: Redis = createRedisInstance();
export function getRedisInstance() {
if (!instance) return createRedisInstance();
return instance;
}
function createRedisInstance() {
const redis = new Redis({
host: env.REDIS_HOST,
password: env.REDIS_PASSWORD,
port: env.REDIS_PORT,
});
redis.on('error', logger.error);
return redis;
}

View File

@ -0,0 +1,5 @@
import { type Context } from '@/bot/context';
export function getSessionKey(ctx: Omit<Context, 'session'>) {
return ctx.chat?.id.toString();
}

View File

@ -0,0 +1,5 @@
import { TIKTOK_URL_REGEX } from '@/constants/regex';
export function validateTikTokUrl(url: string) {
return TIKTOK_URL_REGEX.test(url);
}

View File

@ -8,7 +8,7 @@
"moduleResolution": "Node",
"module": "CommonJS",
"paths": {
"@/*": ["./*"]
"@/*": ["./src/*"]
}
},
"include": ["."],

View File

@ -0,0 +1,240 @@
# Система проверки бана пользователей
## Обзор
Реализована многоуровневая система проверки бана пользователей, которая предотвращает доступ заблокированных пользователей к функциональности приложения.
## 1. База данных (`bannedUntil` поле)
В Strapi добавлено поле `bannedUntil` типа `datetime` в модель `Customer`:
- `null` = пользователь не забанен
- `дата в будущем` = временный бан до указанной даты
- `дата в далеком будущем` = постоянный бан
## 2. Утилита проверки (`packages/utils/src/customer.ts`)
```typescript
export function isCustomerBanned(customer: { bannedUntil?: string | null }): boolean {
return Boolean(customer.bannedUntil && new Date() < new Date(customer.bannedUntil));
}
```
## 3. Next Auth проверка (`apps/web/config/auth.ts`)
В `authorize` callback добавлена проверка бана:
```typescript
async authorize(credentials) {
const { telegramId } = credentials ?? {};
if (!telegramId) { throw new Error('Invalid Telegram ID'); }
try {
const { query } = await getClientWithToken();
const result = await query({
query: GetCustomerDocument,
variables: { telegramId: Number(telegramId) },
});
const customer = result.data.customers.at(0);
if (!customer || isCustomerBanned(customer)) {
throw new Error('User is banned or not found');
}
return { id: telegramId };
} catch (error) {
throw new Error('Authentication failed');
}
}
```
## 4. Универсальная проверка в BaseService (`packages/graphql/api/base.ts`)
Добавлен метод `checkIsBanned()` в `BaseService`:
```typescript
/**
* Универсальная проверка статуса бана пользователя
* Должна вызываться в начале каждого метода сервиса
*/
protected async checkIsBanned() {
const { query } = await getClientWithToken();
const result = await query({
query: GQL.GetCustomerDocument,
variables: this._user,
});
const customer = result.data.customers.at(0);
if (!customer) {
throw new Error(BASE_ERRORS.NOT_FOUND_CUSTOMER);
}
if (isCustomerBanned(customer)) {
throw new Error(ERRORS.NO_PERMISSION);
}
return { customer };
}
```
**Использование в сервисах:**
```typescript
async someMethod() {
await this.checkIsBanned(); // Проверка бана в начале метода
// ... остальная логика
}
```
**Обновленные сервисы:**
- ✅ `CustomersService` - все методы
- ✅ `ServicesService` - все методы
- ✅ `OrdersService` - все методы
- ✅ `SlotsService` - все методы
- ✅ `RegistrationService` - добавлена собственная проверка
**Преимущества:**
- Автоматическая проверка во всех сервисах, наследующих от BaseService
- Единая точка проверки бана
- Работает как в веб-приложении, так и в боте
- Простота добавления в новые методы
- Защита всех API методов от забаненных пользователей
## 5. Защита от изменения статуса бана (`packages/graphql/api/customers.ts` и `registration.ts`)
Пользователи не могут изменять поле `bannedUntil` самостоятельно:
```typescript
// В CustomersService
async updateCustomer(variables: Omit<VariablesOf<typeof GQL.UpdateCustomerDocument>, 'documentId'>) {
await this.checkBanStatus();
const { customer } = await this._getUser();
// Проверяем, что пользователь не пытается изменить поле bannedUntil
if (variables.data.bannedUntil !== undefined) {
throw new Error(ERRORS.NO_PERMISSION);
}
// ... остальная логика обновления
}
// В RegistrationService
async updateCustomer(variables: VariablesOf<typeof GQL.UpdateCustomerDocument>) {
// Проверяем бан для существующего пользователя
if (variables.documentId) {
const { query } = await getClientWithToken();
const result = await query({
query: GQL.GetCustomerDocument,
variables: { documentId: variables.documentId },
});
const customer = result.data.customers.at(0);
if (customer && isCustomerBanned(customer)) {
throw new Error(ERRORS.NO_PERMISSION);
}
}
if (variables.data.bannedUntil) {
throw new Error(ERRORS.NO_PERMISSION);
}
// ... остальная логика обновления
}
```
**Преимущества:**
- Пользователи не могут снять с себя бан
- Только администраторы могут изменять статус блокировки
- Дополнительный уровень безопасности
- Защита работает во всех сервисах, которые обновляют данные пользователей
- Единая ошибка `NO_PERMISSION` для всех случаев отсутствия доступа
## 6. Client-side Check (`components/auth/ban-check.tsx`)
React компонент для проверки бана на клиенте:
```typescript
export function BanCheck({ children }: Readonly<PropsWithChildren>) {
const { data: session } = useSession();
const router = useRouter();
const isBanned = useIsBanned();
useEffect(() => {
if (session?.user?.telegramId && isBanned) {
router.push('/banned');
}
}, [session?.user?.telegramId, isBanned, router]);
if (session?.user?.telegramId && isBanned) {
return null;
}
return <>{children}</>;
}
```
**Использование в layout:**
```typescript
export default function Layout({ children }: Readonly<PropsWithChildren>) {
return (
<Provider>
<BanCheck>
<UpdateProfile />
<main className="grow">{children}</main>
<BottomNav />
</BanCheck>
</Provider>
);
}
```
## 7. Hook для проверки бана (`hooks/api/customers.ts`)
```typescript
export const useIsBanned = () => {
const { data: { customer } = {} } = useCustomerQuery();
if (!customer) return false;
return isCustomerBanned(customer);
};
```
## 8. Страница для забаненных пользователей (`apps/web/app/(auth)/banned/page.tsx`)
Создана специальная страница с информацией о бане и возможностью выхода из аккаунта.
## 9. Централизованные ошибки (`packages/graphql/constants/errors.ts`)
```typescript
export const ERRORS = {
NO_PERMISSION: 'Нет доступа',
} as const;
```
## Архитектура системы
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Next Auth │ │ BaseService │ │ Client-side │
│ (авторизация) │ │ (API методы) │ │ (UI) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ isCustomerBanned│ │ checkBanStatus()│ │ BanCheck │
│ (утилита) │ │ (метод) │ │ (компонент) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└───────────────────────┼───────────────────────┘
┌─────────────────────────┐
│ bannedUntil (DB) │
│ (Strapi/PostgreSQL) │
└─────────────────────────┘
```
## Преимущества системы
**Многоуровневая защита** - проверка на всех уровнях приложения
**Универсальность** - работает в веб-приложении и боте
**Простота использования** - один вызов `checkBanStatus()` в начале метода
**Безопасность** - пользователи не могут обойти бан
**UX** - понятные сообщения и страница для забаненных
**DRY принцип** - нет дублирования кода
**Легкость расширения** - просто добавить новые проверки

View File

@ -0,0 +1,57 @@
'use client';
import { Container } from '@/components/layout';
import { Button } from '@repo/ui/components/ui/button';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@repo/ui/components/ui/card';
import { AlertTriangle, Ban } from 'lucide-react';
import { signOut } from 'next-auth/react';
const handleSignOut = () => {
signOut({ callbackUrl: '/' });
};
export default function BannedPage() {
return (
<Container>
<div className="flex min-h-screen items-center justify-center">
<Card className="w-full max-w-md">
<CardHeader className="text-center">
<div className="mx-auto mb-4 flex size-16 items-center justify-center rounded-full bg-destructive/10">
<Ban className="size-8 text-destructive" />
</div>
<CardTitle className="text-xl">Аккаунт заблокирован</CardTitle>
<CardDescription>
Ваш аккаунт был заблокирован администратором. Для получения дополнительной информации
обратитесь в поддержку.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="rounded-lg bg-muted p-4">
<div className="flex items-start gap-3">
<AlertTriangle className="mt-0.5 size-5 text-yellow-500" />
<div className="text-sm text-muted-foreground">
<p className="mb-1 font-medium text-foreground">Возможные причины блокировки:</p>
<ul className="list-inside list-disc space-y-1">
<li>Нарушение правил использования сервиса</li>
<li>Спам или нежелательная активность</li>
<li>Множественные жалобы от других пользователей</li>
<li>Технические проблемы с аккаунтом</li>
</ul>
</div>
</div>
</div>
<Button className="w-full" onClick={handleSignOut} variant="outline">
Выйти из аккаунта
</Button>
</CardContent>
</Card>
</div>
</Container>
);
}

View File

@ -1,6 +1,6 @@
'use client';
import { UpdateProfile } from '@/components/auth';
import { CheckBanned, UpdateProfile } from '@/components/auth';
import { BottomNav } from '@/components/navigation';
import { EmptyProvider } from '@/providers/empty';
import { TelegramProvider } from '@/providers/telegram';
@ -13,10 +13,12 @@ export default function Layout({ children }: Readonly<PropsWithChildren>) {
const Provider = isTG ? TelegramProvider : EmptyProvider;
return (
<Provider>
<UpdateProfile />
<main className="grow">{children}</main>
<BottomNav />
</Provider>
<CheckBanned>
<Provider>
<UpdateProfile />
<main className="grow">{children}</main>
<BottomNav />
</Provider>
</CheckBanned>
);
}

View File

@ -1,6 +1,6 @@
'use client';
import { Container } from '@/components/layout';
import { ClientsOrdersList, OrdersList } from '@/components/orders';
import { OrdersList } from '@/components/orders';
import { DateSelect } from '@/components/orders/orders-list/date-select';
import { DateTimeStoreProvider } from '@/stores/datetime';
@ -10,7 +10,6 @@ export default function ProfilePage() {
<DateTimeStoreProvider>
<div />
<DateSelect />
<ClientsOrdersList />
<OrdersList />
</DateTimeStoreProvider>
</Container>

View File

@ -0,0 +1,20 @@
/* eslint-disable sonarjs/function-return-type */
'use client';
import { useIsBanned } from '@/hooks/api/customers';
import { redirect, RedirectType } from 'next/navigation';
import { type PropsWithChildren, useEffect } from 'react';
export function CheckBanned({ children }: Readonly<PropsWithChildren>) {
const isBanned = useIsBanned();
useEffect(() => {
if (isBanned) {
redirect('/banned', RedirectType.replace);
}
}, [isBanned]);
if (isBanned) return null;
return children;
}

View File

@ -1 +1,2 @@
export * from './check-banned';
export * from './update-profile';

View File

@ -27,13 +27,17 @@ function getStepComponent(step: string) {
return STEP_COMPONENTS[step] ?? null;
}
const BUTTON_COMPONENTS: Record<string, JSX.Element> = {
'': <NextButton />,
const BUTTON_COMPONENTS: Record<string, JSX.Element | null> = {
'client-select': <NextButton />,
'datetime-select': <SubmitButton />,
error: null,
'master-select': <NextButton />,
'service-select': <NextButton />,
success: null,
};
function getButtonComponent(step: string) {
return BUTTON_COMPONENTS[step] ?? <NextButton />;
return BUTTON_COMPONENTS[step];
}
export const OrderForm = withContext(OrderStoreProvider)(function () {

View File

@ -1,66 +1,16 @@
'use client';
import { OrderCard } from '@/components/shared/order-card';
import { OrdersList as OrdersListComponent } from './orders-list';
import { useCurrentAndNext } from './utils';
import { useCustomerQuery, useIsMaster } from '@/hooks/api/customers';
import { useOrdersInfiniteQuery } from '@/hooks/api/orders';
import { useDateTimeStore } from '@/stores/datetime';
import { Button } from '@repo/ui/components/ui/button';
import type * as GQL from '@repo/graphql/types';
import { getDateUTCRange } from '@repo/utils/datetime-format';
export function ClientsOrdersList() {
const { data: { customer } = {} } = useCustomerQuery();
const isMaster = useIsMaster();
const selectedDate = useDateTimeStore((store) => store.date);
const { endOfDay, startOfDay } = getDateUTCRange(selectedDate).day();
const {
data: { pages } = {},
fetchNextPage,
hasNextPage,
isLoading,
} = useOrdersInfiniteQuery(
{
filters: {
slot: {
datetime_start: selectedDate
? {
gte: startOfDay,
lt: endOfDay,
}
: undefined,
master: {
documentId: {
eq: isMaster ? customer?.documentId : undefined,
},
},
},
},
},
{ 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>
);
}
export function OrdersList() {
const { data: { customer } = {} } = useCustomerQuery();
const isMaster = useIsMaster();
const selectedDate = useDateTimeStore((store) => store.date);
const { endOfDay, startOfDay } = getDateUTCRange(selectedDate).day();
@ -72,40 +22,32 @@ export function OrdersList() {
} = useOrdersInfiniteQuery(
{
filters: {
client: {
documentId: {
eq: customer?.documentId,
},
},
client: isMaster ? undefined : { documentId: { eq: customer?.documentId } },
slot: {
datetime_start: selectedDate
? {
gte: startOfDay,
lt: endOfDay,
}
: undefined,
datetime_start: selectedDate ? { gte: startOfDay, lt: endOfDay } : undefined,
master: isMaster ? { documentId: { eq: customer?.documentId } } : undefined,
},
},
},
{ enabled: Boolean(customer?.documentId) },
);
const orders = pages?.flatMap((page) => page.orders) ?? [];
const orders =
(pages?.flatMap((page) => page.orders).filter(Boolean) as GQL.OrderFieldsFragment[]) ?? [];
const { current, next } = useCurrentAndNext(orders);
if (!orders?.length || isLoading) return null;
return (
<div className="flex flex-col space-y-2">
<h1 className="font-bold">Ваши записи</h1>
{orders?.map(
(order) =>
order && <OrderCard avatarSource="master" key={order.documentId} showDate {...order} />,
)}
{hasNextPage && (
<Button onClick={() => fetchNextPage()} variant="ghost">
Загрузить еще
</Button>
)}
</div>
<OrdersListComponent
avatarSource={isMaster ? 'client' : 'master'}
current={current}
hasNextPage={hasNextPage}
next={next}
onLoadMore={() => fetchNextPage()}
orders={orders}
title={isMaster ? 'Записи клиентов' : 'Ваши записи'}
/>
);
}

View File

@ -0,0 +1,78 @@
import { getOrderStatus, getStatusText, type OrderStatus } from './utils';
import { OrderCard } from '@/components/shared/order-card';
import type * as GQL from '@repo/graphql/types';
import { Button } from '@repo/ui/components/ui/button';
import { cn } from '@repo/ui/lib/utils';
type Order = GQL.OrderFieldsFragment;
type OrdersListProps = Pick<Parameters<typeof OrderCard>[0], 'avatarSource'> & {
readonly current: null | Order;
readonly hasNextPage?: boolean;
readonly next: null | Order;
readonly onLoadMore?: () => void;
readonly orders: Order[];
readonly title: string;
};
export function OrdersList({
avatarSource,
current,
hasNextPage = false,
next,
onLoadMore,
orders,
title,
}: OrdersListProps) {
return (
<div className="flex flex-col space-y-2">
<h1 className="font-bold">{title}</h1>
{orders?.map((order) => {
if (!order) return null;
const status = getOrderStatus(order, current, next);
return (
<DateStatusWrapper key={order.documentId} status={status}>
<OrderCard avatarSource={avatarSource} showDate {...order} />
</DateStatusWrapper>
);
})}
{hasNextPage && onLoadMore && (
<Button onClick={onLoadMore} variant="ghost">
Загрузить еще
</Button>
)}
</div>
);
}
function DateStatusWrapper({
children,
status,
}: {
readonly children: React.ReactNode;
readonly status: OrderStatus;
}) {
if (!status) return <>{children}</>;
const statusText = getStatusText(status);
return (
<div className="space-y-1">
{statusText && (
<div className="px-2">
<span
className={cn(
'text-xs',
status === 'current' ? 'text-secondary-foreground' : 'text-muted-foreground',
)}
>
{statusText}
</span>
</div>
)}
{children}
</div>
);
}

View File

@ -0,0 +1,65 @@
import * as GQL from '@repo/graphql/types';
import dayjs from 'dayjs';
import { useMemo } from 'react';
export type OrderStatus = 'current' | 'next' | undefined;
type Order = GQL.OrderFieldsFragment;
export function getCurrentAndNext(orders: Order[]) {
const now = dayjs();
let current: null | Order = null;
let next: null | Order = null;
for (let index = orders.length - 1; index >= 0; index--) {
const order = orders[index];
const nextOrder = index > 0 ? orders[index - 1] : null;
if (
order &&
now.isAfter(dayjs(order.datetime_start)) &&
now.isBefore(dayjs(order.datetime_end))
) {
current = order;
}
if (
nextOrder?.state === GQL.Enum_Order_State.Approved &&
now.isSame(dayjs(nextOrder?.datetime_start), 'day')
) {
next = nextOrder;
}
}
return { current, next };
}
export function getOrderStatus(
order: Order,
current: null | Order,
next: null | Order,
): OrderStatus {
if (current && order.documentId === current.documentId) return 'current';
if (next && order.documentId === next.documentId) return 'next';
return undefined;
}
export function getStatusText(status: OrderStatus): string {
switch (status) {
case 'current':
return 'Текущая запись';
case 'next':
return 'Следующая запись';
default:
return '';
}
}
export function useCurrentAndNext(orders: Order[]) {
const { current, next } = useMemo(() => {
return getCurrentAndNext(orders);
}, [orders, orders.length]);
return { current, next };
}

View File

@ -7,6 +7,7 @@ import { useCustomerMutation, useCustomerQuery } from '@/hooks/api/customers';
import { Button } from '@repo/ui/components/ui/button';
import { Card } from '@repo/ui/components/ui/card';
import Link from 'next/link';
import { useState } from 'react';
export function ContactDataCard({ telegramId }: Readonly<ProfileProps>) {
const { data: { customer } = {} } = useCustomerQuery({ telegramId });
@ -31,7 +32,8 @@ export function ContactDataCard({ telegramId }: Readonly<ProfileProps>) {
export function ProfileDataCard() {
const { data: { customer } = {} } = useCustomerQuery();
const { mutate: updateCustomer } = useCustomerMutation();
const { cancelChanges, hasChanges, isPending, resetTrigger, saveChanges, updateField } =
useProfileEdit();
if (!customer) return null;
@ -41,8 +43,9 @@ export function ProfileDataCard() {
<CardSectionHeader title="Ваши данные" />
<TextField
id="name"
key={`name-${resetTrigger}`}
label="Имя"
onChange={(value) => updateCustomer({ data: { name: value } })}
onChange={(value) => updateField('name', value)}
value={customer?.name ?? ''}
/>
<TextField disabled id="phone" label="Телефон" readOnly value={customer?.phone ?? ''} />
@ -50,13 +53,54 @@ export function ProfileDataCard() {
checked={customer.role !== 'client'}
description="Разрешить другим пользователям записываться к вам"
onChange={(checked) =>
updateCustomer({
data: { role: checked ? Role.Master : Role.Client },
})
updateField('role', checked ? Role.Master : Role.Client)
}
text="Быть мастером"
/> */}
{hasChanges && (
<div className="flex justify-end gap-2">
<Button disabled={isPending} onClick={cancelChanges} variant="outline">
Отмена
</Button>
<Button disabled={isPending} onClick={saveChanges}>
{isPending ? 'Сохранение...' : 'Сохранить'}
</Button>
</div>
)}
</div>
</Card>
);
}
function useProfileEdit() {
const { isPending, mutate } = useCustomerMutation();
const [pendingChanges, setPendingChanges] = useState<Record<string, unknown>>({});
const [resetTrigger, setResetTrigger] = useState(0);
const updateField = (field: string, value: unknown) => {
setPendingChanges((previous) => ({ ...previous, [field]: value }));
};
const saveChanges = () => {
if (Object.keys(pendingChanges).length === 0) return;
mutate({ data: pendingChanges });
setPendingChanges({});
};
const cancelChanges = () => {
setPendingChanges({});
setResetTrigger((previous) => previous + 1);
};
const hasChanges = Object.keys(pendingChanges).length > 0;
return {
cancelChanges,
hasChanges,
isPending,
resetTrigger,
saveChanges,
updateField,
};
}

View File

@ -2,8 +2,10 @@
import { NumberField, TextareaField, TextField, TimeField } from '@/components/shared/data-fields';
import { useServiceMutation, useServiceQuery } from '@/hooks/api/services';
import { Button } from '@repo/ui/components/ui/button';
import { Card } from '@repo/ui/components/ui/card';
import { convertTimeString } from '@repo/utils/datetime-format';
import { useState } from 'react';
type Props = {
serviceId: string;
@ -11,7 +13,8 @@ type Props = {
export function ServiceDataCard({ serviceId }: Readonly<Props>) {
const { data: { service } = {} } = useServiceQuery({ documentId: serviceId });
const { mutate } = useServiceMutation({ documentId: serviceId });
const { cancelChanges, hasChanges, isPending, resetTrigger, saveChanges, updateField } =
useServiceEdit(serviceId);
if (!service) return null;
@ -20,37 +23,78 @@ export function ServiceDataCard({ serviceId }: Readonly<Props>) {
<div className="flex flex-col gap-4">
<TextField
id="name"
key={`name-${resetTrigger}`}
label="Название"
onChange={(name) => mutate({ data: { name } })}
onChange={(name) => updateField('name', name)}
value={service?.name ?? ''}
/>
<TimeField
id="duration"
key={`duration-${resetTrigger}`}
label="Длительность"
onChange={(time) =>
mutate({
data: {
duration: convertTimeString(time).db(),
},
})
}
onChange={(time) => updateField('duration', convertTimeString(time).db())}
value={service?.duration ?? ''}
/>
<NumberField
id="price"
key={`price-${resetTrigger}`}
label="Цена (₽)"
min={0}
onChange={(price) => mutate({ data: { price } })}
onChange={(price) => updateField('price', price)}
value={service?.price ?? null}
/>
<TextareaField
id="description"
key={`description-${resetTrigger}`}
label="Описание"
onChange={(description) => mutate({ data: { description } })}
onChange={(description) => updateField('description', description)}
rows={4}
value={service?.description ?? ''}
/>
{hasChanges && (
<div className="flex justify-end gap-2">
<Button disabled={isPending} onClick={cancelChanges} variant="outline">
Отмена
</Button>
<Button disabled={isPending} onClick={saveChanges}>
{isPending ? 'Сохранение...' : 'Сохранить'}
</Button>
</div>
)}
</div>
</Card>
);
}
function useServiceEdit(serviceId: string) {
const { isPending, mutate } = useServiceMutation({ documentId: serviceId });
const [pendingChanges, setPendingChanges] = useState<Record<string, unknown>>({});
const [resetTrigger, setResetTrigger] = useState(0);
const updateField = (field: string, value: unknown) => {
setPendingChanges((previous) => ({ ...previous, [field]: value }));
};
const saveChanges = () => {
if (Object.keys(pendingChanges).length === 0) return;
mutate({ data: pendingChanges });
setPendingChanges({});
};
const cancelChanges = () => {
setPendingChanges({});
setResetTrigger((previous) => previous + 1);
};
const hasChanges = Object.keys(pendingChanges).length > 0;
return {
cancelChanges,
hasChanges,
isPending,
resetTrigger,
saveChanges,
updateField,
};
}

View File

@ -2,7 +2,7 @@
import { Button } from '@repo/ui/components/ui/button';
import { Card } from '@repo/ui/components/ui/card';
import { Ban, Check, Lock, RotateCcw, Trash2, Undo, Unlock } from 'lucide-react';
import { Ban, Check, Lock, RotateCcw, Save, Trash2, Undo, Unlock } from 'lucide-react';
type FloatingActionPanelProps = {
readonly isLoading?: boolean;
@ -13,6 +13,7 @@ type FloatingActionPanelProps = {
readonly onDelete?: () => void;
readonly onRepeat?: () => void;
readonly onReturn?: () => void;
readonly onSave?: () => void;
readonly onToggle?: () => void;
};
@ -25,10 +26,20 @@ export default function FloatingActionPanel({
onDelete,
onRepeat,
onReturn,
onSave,
onToggle,
}: FloatingActionPanelProps) {
// Если не переданы обработчики, скрываем панель
if (!onCancel && !onConfirm && !onDelete && !onComplete && !onRepeat && !onToggle && !onReturn)
if (
!onCancel &&
!onConfirm &&
!onDelete &&
!onComplete &&
!onRepeat &&
!onToggle &&
!onReturn &&
!onSave
)
return null;
return (
@ -140,6 +151,19 @@ export default function FloatingActionPanel({
<span>Вернуть</span>
</Button>
)}
{/* Кнопка сохранить */}
{onSave && (
<Button
className="w-full rounded-2xl bg-green-600 text-sm text-white shadow-lg transition-all duration-200 hover:bg-green-700 dark:bg-green-700 dark:hover:bg-green-600 sm:w-auto"
disabled={isLoading}
onClick={onSave}
size="sm"
>
<Save className="mr-2 size-4" />
<span>Сохранить</span>
</Button>
)}
</div>
</Card>
);

View File

@ -10,7 +10,7 @@ type FieldProps = {
readonly id: string;
readonly label: string;
readonly min?: number;
readonly onChange?: (value: null | number) => Promise<void> | void;
readonly onChange?: (value: null | number) => void;
readonly readOnly?: boolean;
readonly value: null | number;
};

View File

@ -9,7 +9,7 @@ type FieldProps = {
readonly disabled?: boolean;
readonly id: string;
readonly label: string;
readonly onChange?: (value: string) => Promise<void> | void;
readonly onChange?: (value: string) => void;
readonly readOnly?: boolean;
readonly value: string;
};

View File

@ -8,7 +8,7 @@ type FieldProps = {
readonly disabled?: boolean;
readonly id: string;
readonly label: string;
readonly onChange?: (value: string) => Promise<void> | void;
readonly onChange?: (value: string) => void;
readonly readOnly?: boolean;
readonly rows?: number;
readonly value: string;

View File

@ -9,7 +9,7 @@ type FieldProps = {
readonly disabled?: boolean;
readonly id: string;
readonly label: string;
readonly onChange?: (value: string) => Promise<void> | void;
readonly onChange?: (value: string) => void;
readonly readOnly?: boolean;
readonly value: string;
};

View File

@ -1,7 +1,7 @@
'use client';
import { getCustomer, updateCustomer } from '@/actions/api/customers';
import { isCustomerMaster } from '@repo/utils/customer';
import { isCustomerBanned, isCustomerMaster } from '@repo/utils/customer';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useSession } from 'next-auth/react';
@ -24,6 +24,14 @@ export const useIsMaster = () => {
return isCustomerMaster(customer);
};
export const useIsBanned = () => {
const { data: { customer } = {} } = useCustomerQuery();
if (!customer) return false;
return isCustomerBanned(customer);
};
export const useCustomerMutation = () => {
const { data: session } = useSession();
const telegramId = session?.user?.telegramId;

View File

@ -35,7 +35,7 @@
"next-auth": "^4.24.11",
"next-intl": "^3.26.0",
"next-themes": "^0.4.4",
"next": "15.3.4",
"next": "^15.5.0",
"postcss": "catalog:",
"radashi": "catalog:",
"react-dom": "catalog:",

View File

@ -1,6 +1,5 @@
services:
web:
container_name: web
env_file:
- .env
build:
@ -10,10 +9,28 @@ services:
ports:
- 3000:3000
bot:
container_name: bot
env_file:
- .env
build:
context: .
dockerfile: ./apps/bot/Dockerfile
env_file:
- .env
depends_on:
- redis
restart: always
redis:
image: redis:8-alpine
restart: always
env_file:
- .env
command: ['redis-server', '--requirepass', '${REDIS_PASSWORD}']
ports:
- '127.0.0.1:6379:6379'
volumes:
- redis-data:/data
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 30s
timeout: 10s
retries: 3
start_period: 30s

View File

@ -15,11 +15,36 @@ services:
bot:
image: ${DOCKERHUB_USERNAME}/zapishis-bot:${BOT_IMAGE_TAG}
restart: always
env_file:
- .env
restart: always
depends_on:
- redis
networks:
- app
redis:
image: redis:8-alpine
restart: always
env_file:
- .env
command: ['redis-server', '--requirepass', '${REDIS_PASSWORD}']
volumes:
- redis-data:/data
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
networks:
app:

View File

@ -1,11 +1,13 @@
/* eslint-disable canonical/id-match */
import { getClientWithToken } from '../apollo/client';
import { ERRORS } from '../constants/errors';
import * as GQL from '../types';
import { isCustomerBanned } from '@repo/utils/customer';
export const ERRORS = {
const BASE_ERRORS = {
MISSING_TELEGRAM_ID: 'Не указан Telegram ID',
NOT_FOUND_CUSTOMER: 'Пользователь не найден',
};
} as const;
type UserProfile = {
telegramId: number;
@ -16,7 +18,7 @@ export class BaseService {
constructor(user: UserProfile) {
if (!user?.telegramId) {
throw new Error(ERRORS.MISSING_TELEGRAM_ID);
throw new Error(BASE_ERRORS.MISSING_TELEGRAM_ID);
}
this._user = user;
@ -32,7 +34,32 @@ export class BaseService {
const customer = result.data.customers.at(0);
if (!customer) throw new Error(ERRORS.NOT_FOUND_CUSTOMER);
if (!customer) throw new Error(BASE_ERRORS.NOT_FOUND_CUSTOMER);
if (isCustomerBanned(customer)) {
throw new Error(ERRORS.NO_PERMISSION);
}
return { customer };
}
protected async checkIsBanned() {
const { query } = await getClientWithToken();
const result = await query({
query: GQL.GetCustomerDocument,
variables: this._user,
});
const customer = result.data.customers.at(0);
if (!customer) {
throw new Error(BASE_ERRORS.NOT_FOUND_CUSTOMER);
}
if (isCustomerBanned(customer)) {
throw new Error(ERRORS.NO_PERMISSION);
}
return { customer };
}

View File

@ -1,12 +1,20 @@
import { getClientWithToken } from '../apollo/client';
import { ERRORS } from '../constants/errors';
import * as GQL from '../types';
import { BaseService } from './base';
import { type VariablesOf } from '@graphql-typed-document-node/core';
export class CustomersService extends BaseService {
async addMasters(variables: VariablesOf<typeof GQL.UpdateCustomerDocument>) {
await this.checkIsBanned();
const newMasterIds = variables.data.masters;
// Проверяем, что пользователь не пытается изменить поле bannedUntil
if (variables.data.bannedUntil !== undefined) {
throw new Error(ERRORS.NO_PERMISSION);
}
const { mutate, query } = await getClientWithToken();
const getMastersResult = await query({
query: GQL.GetMastersDocument,
@ -34,6 +42,8 @@ export class CustomersService extends BaseService {
}
async getClients(variables?: VariablesOf<typeof GQL.GetClientsDocument>) {
await this.checkIsBanned();
const { query } = await getClientWithToken();
const result = await query({
@ -47,6 +57,8 @@ export class CustomersService extends BaseService {
}
async getCustomer(variables: VariablesOf<typeof GQL.GetCustomerDocument>) {
await this.checkIsBanned();
const { query } = await getClientWithToken();
const result = await query({
@ -60,6 +72,8 @@ export class CustomersService extends BaseService {
}
async getMasters(variables?: VariablesOf<typeof GQL.GetMastersDocument>) {
await this.checkIsBanned();
const { query } = await getClientWithToken();
const result = await query({
@ -75,8 +89,15 @@ export class CustomersService extends BaseService {
async updateCustomer(
variables: Omit<VariablesOf<typeof GQL.UpdateCustomerDocument>, 'documentId'>,
) {
await this.checkIsBanned();
const { customer } = await this._getUser();
// Пров еряем, что пользователь не пытается изменить поле bannedUntil
if (variables.data.bannedUntil !== undefined) {
throw new Error(ERRORS.NO_PERMISSION);
}
const { mutate } = await getClientWithToken();
const mutationResult = await mutate({

View File

@ -1,6 +1,7 @@
/* eslint-disable sonarjs/cognitive-complexity */
/* eslint-disable @typescript-eslint/naming-convention */
import { getClientWithToken } from '../apollo/client';
import { ERRORS as SHARED_ERRORS } from '../constants/errors';
import * as GQL from '../types';
import { BaseService } from './base';
import { CustomersService } from './customers';
@ -30,7 +31,6 @@ export const ERRORS = {
NO_MASTER_SELF_BOOK: 'Нельзя записать к самому себе',
NO_ORDER_IN_PAST: 'Нельзя создать запись на время в прошлом',
NO_ORDER_OUT_OF_SLOT: 'Время заказа выходит за пределы слота',
NO_PERMISSION: 'Нет доступа',
NOT_FOUND_CLIENT: 'Клиент не найден',
NOT_FOUND_MASTER: 'Мастер не найден',
NOT_FOUND_ORDER: 'Заказ не найден',
@ -39,7 +39,7 @@ export const ERRORS = {
SLOT_CLOSED: 'Слот закрыт',
};
const DEFAULT_ORDERS_SORT = ['slot.datetime_start:desc', 'datetime_start:asc'];
const DEFAULT_ORDERS_SORT = ['slot.datetime_start:desc', 'datetime_start:desc'];
export class OrdersService extends BaseService {
async createOrder(variables: VariablesOf<typeof GQL.CreateOrderDocument>) {
@ -151,17 +151,17 @@ export class OrdersService extends BaseService {
const isOrderClient = order.client?.documentId === customer.documentId;
const isOrderMaster = order.slot?.master?.documentId === customer.documentId;
if (!isOrderClient && !isOrderMaster) throw new Error(ERRORS.NO_PERMISSION);
if (!isOrderClient && !isOrderMaster) throw new Error(SHARED_ERRORS.NO_PERMISSION);
if (isOrderClient && Object.keys(variables.data).length > 1)
throw new Error(ERRORS.NO_PERMISSION);
throw new Error(SHARED_ERRORS.NO_PERMISSION);
if (
isOrderClient &&
variables.data.state &&
variables.data.state !== GQL.Enum_Order_State.Cancelling
) {
throw new Error(ERRORS.NO_PERMISSION);
throw new Error(SHARED_ERRORS.NO_PERMISSION);
}
const { mutate } = await getClientWithToken();
@ -307,7 +307,7 @@ export class OrdersService extends BaseService {
private async checkBeforeUpdate(variables: VariablesOf<typeof GQL.UpdateOrderDocument>) {
if (variables.data.client || variables.data.services?.length || variables.data.slot) {
throw new Error(ERRORS.NO_PERMISSION);
throw new Error(SHARED_ERRORS.NO_PERMISSION);
}
const { order: existingOrder } = await this.getOrder({ documentId: variables.documentId });
@ -368,11 +368,11 @@ export class OrdersService extends BaseService {
const isOrderMaster = order.slot?.master?.documentId === customer.documentId;
if (!isOrderClient && !isOrderMaster) {
throw new Error(ERRORS.NO_PERMISSION);
throw new Error(SHARED_ERRORS.NO_PERMISSION);
}
if (isOrderClient && variables?.data && Object.keys(variables.data).length > 1) {
throw new Error(ERRORS.NO_PERMISSION);
throw new Error(SHARED_ERRORS.NO_PERMISSION);
}
if (
@ -380,7 +380,7 @@ export class OrdersService extends BaseService {
variables?.data?.state &&
variables.data.state !== GQL.Enum_Order_State.Cancelling
) {
throw new Error(ERRORS.NO_PERMISSION);
throw new Error(SHARED_ERRORS.NO_PERMISSION);
}
}

View File

@ -1,6 +1,8 @@
import { getClientWithToken } from '../apollo/client';
import { ERRORS } from '../constants/errors';
import * as GQL from '../types';
import { type VariablesOf } from '@graphql-typed-document-node/core';
import { isCustomerBanned } from '@repo/utils/customer';
export class RegistrationService {
async createCustomer(variables: VariablesOf<typeof GQL.CreateCustomerDocument>) {
@ -18,6 +20,10 @@ export class RegistrationService {
}
async getCustomer(variables: VariablesOf<typeof GQL.GetCustomerDocument>) {
if (variables.telegramId) {
await this.checkBanStatus(variables.telegramId);
}
const { query } = await getClientWithToken();
const result = await query({
@ -31,6 +37,23 @@ export class RegistrationService {
}
async updateCustomer(variables: VariablesOf<typeof GQL.UpdateCustomerDocument>) {
// Проверяем бан для существующего пользователя
if (variables.documentId) {
const { query } = await getClientWithToken();
const result = await query({
query: GQL.GetCustomerDocument,
variables: { documentId: variables.documentId },
});
const customer = result.data.customers.at(0);
if (customer && isCustomerBanned(customer)) {
throw new Error(ERRORS.NO_PERMISSION);
}
}
if (variables.data.bannedUntil) {
throw new Error(ERRORS.NO_PERMISSION);
}
const { mutate } = await getClientWithToken();
const mutationResult = await mutate({
@ -43,4 +66,21 @@ export class RegistrationService {
return mutationResult.data;
}
private async checkBanStatus(telegramId: number) {
const { query } = await getClientWithToken();
const result = await query({
query: GQL.GetCustomerDocument,
variables: { telegramId },
});
const customer = result.data.customers.at(0);
if (customer && isCustomerBanned(customer)) {
throw new Error(ERRORS.NO_PERMISSION);
}
return { customer };
}
}

View File

@ -1,14 +1,13 @@
import { getClientWithToken } from '../apollo/client';
import { ERRORS } from '../constants/errors';
import * as GQL from '../types';
import { BaseService } from './base';
import { type VariablesOf } from '@graphql-typed-document-node/core';
const ERRORS = {
NO_PERMISSION: 'No permission',
};
export class ServicesService extends BaseService {
async createService(variables: VariablesOf<typeof GQL.CreateServiceDocument>) {
await this.checkIsBanned();
const { customer } = await this._getUser();
const { mutate } = await getClientWithToken();
@ -31,6 +30,8 @@ export class ServicesService extends BaseService {
}
async getService(variables: VariablesOf<typeof GQL.GetServiceDocument>) {
await this.checkIsBanned();
const { query } = await getClientWithToken();
const result = await query({
@ -42,6 +43,8 @@ export class ServicesService extends BaseService {
}
async getServices(variables: VariablesOf<typeof GQL.GetServicesDocument>) {
await this.checkIsBanned();
const { query } = await getClientWithToken();
const result = await query({
@ -53,6 +56,8 @@ export class ServicesService extends BaseService {
}
async updateService(variables: VariablesOf<typeof GQL.UpdateServiceDocument>) {
await this.checkIsBanned();
await this.checkPermission(variables);
const { mutate } = await getClientWithToken();

View File

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/naming-convention */
import { getClientWithToken } from '../apollo/client';
import { ERRORS as SHARED_ERRORS } from '../constants/errors';
import * as GQL from '../types';
import { BaseService } from './base';
import { ServicesService } from './services';
@ -14,7 +15,6 @@ export const ERRORS = {
MISSING_DATETIME_START: 'Не указана дата начала',
MISSING_SERVICES_IDS: 'Не указаны услуги',
NO_PAST_SLOT: 'Нельзя создать слот в прошлом',
NO_PERMISSION: 'Нет доступа',
NOT_FOUND_MASTER: 'Мастер не найден',
NOT_FOUND_SERVICE: 'Сервис не найден',
NOT_FOUND_SLOT: 'Слот не найден',
@ -24,6 +24,8 @@ export const ERRORS = {
export class SlotsService extends BaseService {
async createSlot(variables: VariablesOf<typeof GQL.CreateSlotDocument>) {
await this.checkIsBanned();
await this.checkBeforeCreate(variables);
const { customer } = await this._getUser();
@ -48,6 +50,8 @@ export class SlotsService extends BaseService {
}
async deleteSlot(variables: VariablesOf<typeof GQL.DeleteSlotDocument>) {
await this.checkIsBanned();
await this.checkPermission(variables);
const { slot } = await this.getSlot({ documentId: variables.documentId });
@ -73,6 +77,8 @@ export class SlotsService extends BaseService {
variables: VariablesOf<typeof GQL.GetSlotsDocument>,
context: { services: string[] },
) {
await this.checkIsBanned();
if (!variables.filters?.datetime_start) throw new Error(ERRORS.MISSING_DATETIME_START);
if (!context?.services?.length) throw new Error(ERRORS.MISSING_SERVICES_IDS);
@ -146,6 +152,8 @@ export class SlotsService extends BaseService {
}
async getSlot(variables: VariablesOf<typeof GQL.GetSlotDocument>) {
await this.checkIsBanned();
const { query } = await getClientWithToken();
const result = await query({
@ -157,6 +165,8 @@ export class SlotsService extends BaseService {
}
async getSlots(variables: VariablesOf<typeof GQL.GetSlotsDocument>) {
await this.checkIsBanned();
const { query } = await getClientWithToken();
const result = await query({
@ -168,6 +178,8 @@ export class SlotsService extends BaseService {
}
async updateSlot(variables: VariablesOf<typeof GQL.UpdateSlotDocument>) {
await this.checkIsBanned();
await this.checkPermission(variables);
await this.checkBeforeUpdateDatetime(variables);
@ -288,6 +300,7 @@ export class SlotsService extends BaseService {
if (!slot) throw new Error(ERRORS.NOT_FOUND_SLOT);
if (slot?.master?.documentId !== customer?.documentId) throw new Error(ERRORS.NO_PERMISSION);
if (slot?.master?.documentId !== customer?.documentId)
throw new Error(SHARED_ERRORS.NO_PERMISSION);
}
}

View File

@ -0,0 +1,3 @@
export const ERRORS = {
NO_PERMISSION: 'Нет доступа',
} as const;

View File

@ -1,5 +1,6 @@
fragment CustomerFields on Customer {
active
bannedUntil
documentId
name
phone

View File

@ -77,6 +77,7 @@ export type BooleanFilterInput = {
export type CustomerFiltersInput = {
active?: InputMaybe<BooleanFilterInput>;
and?: InputMaybe<Array<InputMaybe<CustomerFiltersInput>>>;
bannedUntil?: InputMaybe<DateTimeFilterInput>;
blocks?: InputMaybe<BlockFiltersInput>;
clients?: InputMaybe<CustomerFiltersInput>;
createdAt?: InputMaybe<DateTimeFilterInput>;
@ -98,6 +99,7 @@ export type CustomerFiltersInput = {
export type CustomerInput = {
active?: InputMaybe<Scalars['Boolean']['input']>;
bannedUntil?: InputMaybe<Scalars['DateTime']['input']>;
blocks?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>;
clients?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>;
masters?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>;
@ -624,7 +626,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?: number | null | undefined };
export type CustomerFieldsFragment = { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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'];
@ -642,7 +644,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?: number | null | undefined } | null | undefined> };
export type GetCustomerQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined> };
export type GetMastersQueryVariables = Exact<{
phone?: InputMaybe<Scalars['String']['input']>;
@ -651,7 +653,7 @@ export type GetMastersQueryVariables = Exact<{
}>;
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 GetMastersQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', documentId: string, masters: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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 GetClientsQueryVariables = Exact<{
phone?: InputMaybe<Scalars['String']['input']>;
@ -659,7 +661,7 @@ export type GetClientsQueryVariables = Exact<{
}>;
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 GetClientsQuery = { __typename?: 'Query', customers: Array<{ __typename?: 'Customer', documentId: string, clients: Array<{ __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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'];
@ -667,9 +669,9 @@ 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?: number | null | undefined } | null | undefined };
export type UpdateCustomerMutation = { __typename?: 'Mutation', updateCustomer?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __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>, client?: { __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, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __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 OrderFieldsFragment = { __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined>, client?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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 GetOrdersQueryVariables = Exact<{
filters?: InputMaybe<OrderFiltersInput>;
@ -678,21 +680,21 @@ export type GetOrdersQueryVariables = Exact<{
}>;
export type GetOrdersQuery = { __typename?: 'Query', orders: Array<{ __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __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>, client?: { __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, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __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 } | null | undefined> };
export type GetOrdersQuery = { __typename?: 'Query', orders: Array<{ __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined>, client?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined } | null | undefined> };
export type GetOrderQueryVariables = Exact<{
documentId: Scalars['ID']['input'];
}>;
export type GetOrderQuery = { __typename?: 'Query', order?: { __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __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>, client?: { __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, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __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 } | null | undefined };
export type GetOrderQuery = { __typename?: 'Query', order?: { __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined>, client?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined } | null | undefined };
export type CreateOrderMutationVariables = Exact<{
input: OrderInput;
}>;
export type CreateOrderMutation = { __typename?: 'Mutation', createOrder?: { __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __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>, client?: { __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, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __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 } | null | undefined };
export type CreateOrderMutation = { __typename?: 'Mutation', createOrder?: { __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined>, client?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined } | null | undefined };
export type UpdateOrderMutationVariables = Exact<{
documentId: Scalars['ID']['input'];
@ -700,30 +702,30 @@ export type UpdateOrderMutationVariables = Exact<{
}>;
export type UpdateOrderMutation = { __typename?: 'Mutation', updateOrder?: { __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __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>, client?: { __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, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __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 } | null | undefined };
export type UpdateOrderMutation = { __typename?: 'Mutation', updateOrder?: { __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined>, client?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined } | null | undefined };
export type ServiceFieldsFragment = { __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __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 ServiceFieldsFragment = { __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined };
export type GetServicesQueryVariables = Exact<{
filters?: InputMaybe<ServiceFiltersInput>;
}>;
export type GetServicesQuery = { __typename?: 'Query', services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __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 GetServicesQuery = { __typename?: 'Query', services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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 GetServiceQueryVariables = Exact<{
documentId: Scalars['ID']['input'];
}>;
export type GetServiceQuery = { __typename?: 'Query', service?: { __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __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 GetServiceQuery = { __typename?: 'Query', service?: { __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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 CreateServiceMutationVariables = Exact<{
data: ServiceInput;
}>;
export type CreateServiceMutation = { __typename?: 'Mutation', createService?: { __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __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 CreateServiceMutation = { __typename?: 'Mutation', createService?: { __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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 UpdateServiceMutationVariables = Exact<{
documentId: Scalars['ID']['input'];
@ -731,37 +733,37 @@ export type UpdateServiceMutationVariables = Exact<{
}>;
export type UpdateServiceMutation = { __typename?: 'Mutation', updateService?: { __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __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 UpdateServiceMutation = { __typename?: 'Mutation', updateService?: { __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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 SlotFieldsFragment = { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __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 SlotFieldsFragment = { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined };
export type CreateSlotMutationVariables = Exact<{
input: SlotInput;
}>;
export type CreateSlotMutation = { __typename?: 'Mutation', createSlot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __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 CreateSlotMutation = { __typename?: 'Mutation', createSlot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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 GetSlotsQueryVariables = Exact<{
filters?: InputMaybe<SlotFiltersInput>;
}>;
export type GetSlotsQuery = { __typename?: 'Query', slots: Array<{ __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __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 GetSlotsQuery = { __typename?: 'Query', slots: Array<{ __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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 GetSlotsOrdersQueryVariables = Exact<{
filters?: InputMaybe<SlotFiltersInput>;
}>;
export type GetSlotsOrdersQuery = { __typename?: 'Query', slots: Array<{ __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, orders: Array<{ __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __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>, client?: { __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, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __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 } | null | undefined>, master?: { __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 GetSlotsOrdersQuery = { __typename?: 'Query', slots: Array<{ __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, orders: Array<{ __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined>, client?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined } | null | undefined>, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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 GetSlotQueryVariables = Exact<{
documentId: Scalars['ID']['input'];
}>;
export type GetSlotQuery = { __typename?: 'Query', slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, orders: Array<{ __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __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>, client?: { __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, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __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 } | null | undefined>, master?: { __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 GetSlotQuery = { __typename?: 'Query', slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, orders: Array<{ __typename?: 'Order', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name: string, description?: string | null | undefined, price?: number | null | undefined, active?: boolean | null | undefined, duration: string, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined>, client?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined, slot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: number | null | undefined } | null | undefined } | null | undefined } | null | undefined>, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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 UpdateSlotMutationVariables = Exact<{
documentId: Scalars['ID']['input'];
@ -769,7 +771,7 @@ export type UpdateSlotMutationVariables = Exact<{
}>;
export type UpdateSlotMutation = { __typename?: 'Mutation', updateSlot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __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 UpdateSlotMutation = { __typename?: 'Mutation', updateSlot?: { __typename?: 'Slot', documentId: string, datetime_start: string, datetime_end: string, state?: Enum_Slot_State | null | undefined, master?: { __typename?: 'Customer', active?: boolean | null | undefined, bannedUntil?: string | 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 DeleteSlotMutationVariables = Exact<{
documentId: Scalars['ID']['input'];
@ -778,28 +780,28 @@ export type DeleteSlotMutationVariables = Exact<{
export type DeleteSlotMutation = { __typename?: 'Mutation', deleteSlot?: { __typename?: 'DeleteMutationResponse', documentId: string } | null | undefined };
export const CustomerFieldsFragmentDoc = {"kind":"Document","definitions":[{"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<CustomerFieldsFragment, unknown>;
export const ServiceFieldsFragmentDoc = {"kind":"Document","definitions":[{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"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<ServiceFieldsFragment, unknown>;
export const SlotFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"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<SlotFieldsFragment, unknown>;
export const OrderFieldsFragmentDoc = {"kind":"Document","definitions":[{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<OrderFieldsFragment, unknown>;
export const CustomerFieldsFragmentDoc = {"kind":"Document","definitions":[{"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":"bannedUntil"}},{"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<CustomerFieldsFragment, unknown>;
export const ServiceFieldsFragmentDoc = {"kind":"Document","definitions":[{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"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":"bannedUntil"}},{"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<ServiceFieldsFragment, unknown>;
export const SlotFieldsFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"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":"bannedUntil"}},{"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<SlotFieldsFragment, unknown>;
export const OrderFieldsFragmentDoc = {"kind":"Document","definitions":[{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}},{"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":"bannedUntil"}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<OrderFieldsFragment, unknown>;
export const RegisterDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"Register"},"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"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"register"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"username"},"value":{"kind":"Variable","name":{"kind":"Name","value":"identifier"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"password"},"value":{"kind":"Variable","name":{"kind":"Name","value":"password"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"email"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"jwt"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"username"}}]}}]}}]}}]} as unknown as DocumentNode<RegisterMutation, RegisterMutationVariables>;
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<LoginMutation, LoginMutationVariables>;
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<CreateCustomerMutation, CreateCustomerMutationVariables>;
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"}}},{"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":"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<GetCustomerQuery, GetCustomerQueryVariables>;
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<GetMastersQuery, GetMastersQueryVariables>;
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<GetClientsQuery, GetClientsQueryVariables>;
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<UpdateCustomerMutation, UpdateCustomerMutationVariables>;
export const GetOrdersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrders"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"OrderFiltersInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PaginationArg"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sort"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"orders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sort"}}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}}]}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}}]} as unknown as DocumentNode<GetOrdersQuery, GetOrdersQueryVariables>;
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":"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}}]} as unknown as DocumentNode<GetOrderQuery, GetOrderQueryVariables>;
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":"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}}]} as unknown as DocumentNode<CreateOrderMutation, CreateOrderMutationVariables>;
export const UpdateOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOrder"},"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":"OrderInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOrder"},"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":"OrderFields"}}]}}]}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}}]} as unknown as DocumentNode<UpdateOrderMutation, UpdateOrderMutationVariables>;
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"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"StringValue","value":"name:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}}]}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<GetServicesQuery, GetServicesQueryVariables>;
export const GetServiceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetService"},"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":"service"},"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":"ServiceFields"}}]}}]}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<GetServiceQuery, GetServiceQueryVariables>;
export const CreateServiceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateService"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServiceInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createService"},"arguments":[{"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":"ServiceFields"}}]}}]}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<CreateServiceMutation, CreateServiceMutationVariables>;
export const UpdateServiceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateService"},"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":"ServiceInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateService"},"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":"ServiceFields"}}]}}]}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<UpdateServiceMutation, UpdateServiceMutationVariables>;
export const CreateSlotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSlot"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SlotInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSlot"},"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":"SlotFields"}}]}}]}},{"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"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<CreateSlotMutation, CreateSlotMutationVariables>;
export const GetSlotsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSlots"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"SlotFiltersInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slots"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"StringValue","value":"datetime_start:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}},{"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"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<GetSlotsQuery, GetSlotsQueryVariables>;
export const GetSlotsOrdersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSlotsOrders"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"SlotFiltersInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slots"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"StringValue","value":"datetime_start:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}},{"kind":"Field","name":{"kind":"Name","value":"orders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"StringValue","value":"datetime_start:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}}]}}]}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}}]} as unknown as DocumentNode<GetSlotsOrdersQuery, GetSlotsOrdersQueryVariables>;
export const GetSlotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSlot"},"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":"slot"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"orders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"StringValue","value":"datetime_start:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}}]} as unknown as DocumentNode<GetSlotQuery, GetSlotQueryVariables>;
export const UpdateSlotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSlot"},"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":"SlotInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateSlot"},"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":"SlotFields"}}]}}]}},{"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"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<UpdateSlotMutation, UpdateSlotMutationVariables>;
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"}}},{"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":"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":"bannedUntil"}},{"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<GetCustomerQuery, GetCustomerQueryVariables>;
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":"bannedUntil"}},{"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<GetMastersQuery, GetMastersQueryVariables>;
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":"bannedUntil"}},{"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<GetClientsQuery, GetClientsQueryVariables>;
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":"bannedUntil"}},{"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<UpdateCustomerMutation, UpdateCustomerMutationVariables>;
export const GetOrdersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrders"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"OrderFiltersInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PaginationArg"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"sort"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"orders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"Variable","name":{"kind":"Name","value":"sort"}}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}}]}},{"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":"bannedUntil"}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}}]} as unknown as DocumentNode<GetOrdersQuery, GetOrdersQueryVariables>;
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":"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":"bannedUntil"}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}}]} as unknown as DocumentNode<GetOrderQuery, GetOrderQueryVariables>;
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":"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":"bannedUntil"}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}}]} as unknown as DocumentNode<CreateOrderMutation, CreateOrderMutationVariables>;
export const UpdateOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateOrder"},"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":"OrderInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateOrder"},"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":"OrderFields"}}]}}]}},{"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":"bannedUntil"}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}}]} as unknown as DocumentNode<UpdateOrderMutation, UpdateOrderMutationVariables>;
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"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"StringValue","value":"name:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}}]}},{"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":"bannedUntil"}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<GetServicesQuery, GetServicesQueryVariables>;
export const GetServiceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetService"},"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":"service"},"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":"ServiceFields"}}]}}]}},{"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":"bannedUntil"}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<GetServiceQuery, GetServiceQueryVariables>;
export const CreateServiceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateService"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ServiceInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createService"},"arguments":[{"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":"ServiceFields"}}]}}]}},{"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":"bannedUntil"}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<CreateServiceMutation, CreateServiceMutationVariables>;
export const UpdateServiceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateService"},"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":"ServiceInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateService"},"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":"ServiceFields"}}]}}]}},{"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":"bannedUntil"}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<UpdateServiceMutation, UpdateServiceMutationVariables>;
export const CreateSlotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateSlot"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SlotInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createSlot"},"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":"SlotFields"}}]}}]}},{"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":"bannedUntil"}},{"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"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<CreateSlotMutation, CreateSlotMutationVariables>;
export const GetSlotsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSlots"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"SlotFiltersInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slots"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"StringValue","value":"datetime_start:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}},{"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":"bannedUntil"}},{"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"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<GetSlotsQuery, GetSlotsQueryVariables>;
export const GetSlotsOrdersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSlotsOrders"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filters"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"SlotFiltersInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slots"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filters"}}},{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"StringValue","value":"datetime_start:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}},{"kind":"Field","name":{"kind":"Name","value":"orders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"StringValue","value":"datetime_start:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}}]}}]}},{"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":"bannedUntil"}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}}]} as unknown as DocumentNode<GetSlotsOrdersQuery, GetSlotsOrdersQueryVariables>;
export const GetSlotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSlot"},"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":"slot"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"orders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"StringValue","value":"datetime_start:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}},{"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":"bannedUntil"}},{"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"}}]}},{"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":"description"}},{"kind":"Field","name":{"kind":"Name","value":"price"}},{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"duration"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"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":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_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":"FragmentSpread","name":{"kind":"Name","value":"ServiceFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}},{"kind":"Field","name":{"kind":"Name","value":"slot"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}}]} as unknown as DocumentNode<GetSlotQuery, GetSlotQueryVariables>;
export const UpdateSlotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSlot"},"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":"SlotInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateSlot"},"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":"SlotFields"}}]}}]}},{"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":"bannedUntil"}},{"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"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SlotFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Slot"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_start"}},{"kind":"Field","name":{"kind":"Name","value":"datetime_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"master"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]} as unknown as DocumentNode<UpdateSlotMutation, UpdateSlotMutationVariables>;
export const DeleteSlotDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteSlot"},"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":"deleteSlot"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}}]}}]}}]} as unknown as DocumentNode<DeleteSlotMutation, DeleteSlotMutationVariables>;

View File

@ -1,5 +1,9 @@
import * as GQL from '../../graphql/types';
export function isCustomerBanned(customer: GQL.CustomerFieldsFragment): boolean {
return Boolean(customer.bannedUntil && new Date() < new Date(customer.bannedUntil));
}
export function isCustomerMaster(customer: GQL.CustomerFieldsFragment) {
return customer?.role === GQL.Enum_Customer_Role.Master;
}

1497
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -4,8 +4,8 @@ packages:
catalog:
"@apollo/client": ^3.12.4
"@types/node": ^20
"@types/react": ^19
"@types/react-dom": ^19
"@types/react": ^19.1.11
"@types/react-dom": ^19.1.8
"@vchikalkin/eslint-config-awesome": ^2.2.2
autoprefixer: ^10.4.20
dayjs: ^1.11.3
@ -19,12 +19,11 @@ catalog:
postcss: ^8.4.49
postcss-load-config: ^6.0.1
prettier: ^3.2.5
react: ^19
react-dom: ^19
react: ^19.1.1
react-dom: ^19.1.1
radashi: ^12.5.1
rimraf: ^6.0.1
tailwindcss: ^3.4.15
telegraf: ^4.16.3
typescript: ^5.7
vite-tsconfig-paths: ^5.1.4
vitest: ^2.1.8