Feature/records (#22)
* components/profile: rename components files * components/profile: organize files & folders * split DataCard to 2 components * [2] components/profile: organize files & folders * data-card: fix phone field disabled * fix card header color * add schedule button for master * fix navigation & profile background * add basic schedule page * fix bottom navbar overflows content * header: remove bottom margin * replace vanilla calendar with shadcn/ui one * add slot functional * fix forbidden error * add slot operations * show slots * filter by selected day * add hook useSlots fix update slots list after add slot fix initial fetch slots * use slots hooks * split edit-slot-form into files * rename /time-slots -> /components * refactor components & folders structure * add feature: delete slot * hooks/slot: update query keys * add hooks/profile * add hook useProfileMutation * use useProfileMutation hook for update role * rename useProfile -> useProfileQuery * fix useProfileQuery queryKey * add hook useContactsQuery * remove unused ternary operator * header: add backdrop blur * create slot cards * fix elements y center * fix getSlots filters * ui/ux improvements * fix date time types & names * move profile components from sub folder * add basic slot page * fix add slot form padding x * add slot buttons * extend slot card information * fix import type * use Container in pages * change orange -> yellow for dark * use Loading spinner in slots list * refactor \components\schedule dir structure * add orders list * change query & mutation keys * change url /profile/schedule/slot/ -> /slots/ * order: show services * remove prefetchQuery * bring the results of queries and hooks into a single form * react query: globally show error toast * add font inter * fix header: center text * orders: add sorting * order card: add avatar * rename records -> orders * reduced text size * fix slot buttons * fix datetime card ui * fix header: center text (finally) * layout/container: last:mb-4 * fix type * slot-datetime: use ReadonlyTimeRange * rename files & components * remove unnecessary context using * feature: edit slot time * fix: selected day reset after go back to /schedule * rename AddTimeRange -> EditableTimeRangeForm & refactor * fix some elements on page before data loaded * fix text size * slot-card: remove gap * slot-date: remove margin * fix slots & orders layout * toast: show error text in ui
This commit is contained in:
parent
c8a602db05
commit
06be87f0ec
@ -16,7 +16,8 @@ import { message } from 'telegraf/filters';
|
|||||||
const bot = new Telegraf(environment.BOT_TOKEN);
|
const bot = new Telegraf(environment.BOT_TOKEN);
|
||||||
|
|
||||||
bot.start(async (context) => {
|
bot.start(async (context) => {
|
||||||
const customer = await getCustomer({ telegramId: context.from.id });
|
const data = await getCustomer({ telegramId: context.from.id });
|
||||||
|
const customer = data?.data?.customers?.at(0);
|
||||||
|
|
||||||
if (customer) {
|
if (customer) {
|
||||||
return context.reply(
|
return context.reply(
|
||||||
@ -33,7 +34,8 @@ bot.start(async (context) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
bot.command('addcontact', async (context) => {
|
bot.command('addcontact', async (context) => {
|
||||||
const customer = await getCustomer({ telegramId: context.from.id });
|
const data = await getCustomer({ telegramId: context.from.id });
|
||||||
|
const customer = data?.data?.customers?.at(0);
|
||||||
|
|
||||||
if (!customer) {
|
if (!customer) {
|
||||||
return context.reply(
|
return context.reply(
|
||||||
@ -50,7 +52,8 @@ bot.command('addcontact', async (context) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
bot.command('becomemaster', async (context) => {
|
bot.command('becomemaster', async (context) => {
|
||||||
const customer = await getCustomer({ telegramId: context.from.id });
|
const data = await getCustomer({ telegramId: context.from.id });
|
||||||
|
const customer = data?.data?.customers?.at(0);
|
||||||
|
|
||||||
if (!customer) {
|
if (!customer) {
|
||||||
return context.reply('Сначала поделитесь своим номером телефона.', KEYBOARD_SHARE_PHONE);
|
return context.reply('Сначала поделитесь своим номером телефона.', KEYBOARD_SHARE_PHONE);
|
||||||
@ -73,7 +76,9 @@ bot.command('becomemaster', async (context) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
bot.on(message('contact'), async (context) => {
|
bot.on(message('contact'), async (context) => {
|
||||||
const customer = await getCustomer({ telegramId: context.from.id });
|
const data = await getCustomer({ telegramId: context.from.id });
|
||||||
|
const customer = data?.data?.customers?.at(0);
|
||||||
|
|
||||||
const isRegistration = !customer;
|
const isRegistration = !customer;
|
||||||
|
|
||||||
const { contact } = context.message;
|
const { contact } = context.message;
|
||||||
|
|||||||
@ -9,11 +9,9 @@ export async function getClients() {
|
|||||||
|
|
||||||
const { user } = session;
|
const { user } = session;
|
||||||
|
|
||||||
const getCustomerClientsResponse = await getCustomerClients({ telegramId: user?.telegramId });
|
const response = await getCustomerClients({ telegramId: user?.telegramId });
|
||||||
|
|
||||||
return {
|
return response.data?.customers?.at(0);
|
||||||
clients: getCustomerClientsResponse?.clients,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMasters() {
|
export async function getMasters() {
|
||||||
@ -22,9 +20,7 @@ export async function getMasters() {
|
|||||||
|
|
||||||
const { user } = session;
|
const { user } = session;
|
||||||
|
|
||||||
const getCustomerMastersResponse = await getCustomerMasters({ telegramId: user?.telegramId });
|
const response = await getCustomerMasters({ telegramId: user?.telegramId });
|
||||||
|
|
||||||
return {
|
return response.data?.customers?.at(0);
|
||||||
masters: getCustomerMastersResponse?.masters,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|||||||
4
apps/web/actions/orders.ts
Normal file
4
apps/web/actions/orders.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
'use server';
|
||||||
|
import * as api from '@repo/graphql/api';
|
||||||
|
|
||||||
|
export const getOrder = api.getOrder;
|
||||||
@ -3,17 +3,17 @@ import { authOptions } from '@/config/auth';
|
|||||||
import { getCustomer, updateCustomerProfile } from '@repo/graphql/api';
|
import { getCustomer, updateCustomerProfile } from '@repo/graphql/api';
|
||||||
import { type CustomerInput, type GetCustomerQueryVariables } from '@repo/graphql/types';
|
import { type CustomerInput, type GetCustomerQueryVariables } from '@repo/graphql/types';
|
||||||
import { getServerSession } from 'next-auth/next';
|
import { getServerSession } from 'next-auth/next';
|
||||||
import { revalidatePath } from 'next/cache';
|
|
||||||
|
|
||||||
export async function getProfile(input?: GetCustomerQueryVariables) {
|
export async function getProfile(input?: GetCustomerQueryVariables) {
|
||||||
const session = await getServerSession(authOptions);
|
const session = await getServerSession(authOptions);
|
||||||
if (!session) throw new Error('Missing session');
|
if (!session) throw new Error('Missing session');
|
||||||
|
|
||||||
const { user } = session;
|
const { user } = session;
|
||||||
|
|
||||||
const telegramId = input?.telegramId || user?.telegramId;
|
const telegramId = input?.telegramId || user?.telegramId;
|
||||||
|
|
||||||
const customer = await getCustomer({ telegramId });
|
const { data } = await getCustomer({ telegramId });
|
||||||
|
const customer = data?.customers?.at(0);
|
||||||
|
|
||||||
return customer;
|
return customer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -23,13 +23,12 @@ export async function updateProfile(input: CustomerInput) {
|
|||||||
|
|
||||||
const { user } = session;
|
const { user } = session;
|
||||||
|
|
||||||
const customer = await getCustomer({ telegramId: user?.telegramId });
|
const { data } = await getCustomer({ telegramId: user?.telegramId });
|
||||||
|
const customer = data.customers.at(0);
|
||||||
if (!customer) throw new Error('Customer not found');
|
if (!customer) throw new Error('Customer not found');
|
||||||
|
|
||||||
await updateCustomerProfile({
|
await updateCustomerProfile({
|
||||||
data: input,
|
data: input,
|
||||||
documentId: customer.documentId,
|
documentId: customer?.documentId,
|
||||||
});
|
});
|
||||||
|
|
||||||
revalidatePath('/profile');
|
|
||||||
}
|
}
|
||||||
|
|||||||
60
apps/web/actions/slots.ts
Normal file
60
apps/web/actions/slots.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
'use server';
|
||||||
|
// eslint-disable-next-line sonarjs/no-internal-api-use
|
||||||
|
import type * as ApolloTypes from '../../../packages/graphql/node_modules/@apollo/client/core';
|
||||||
|
import { getProfile } from './profile';
|
||||||
|
import { formatDate, formatTime } from '@/utils/date';
|
||||||
|
import * as api from '@repo/graphql/api';
|
||||||
|
import type * as GQL from '@repo/graphql/types';
|
||||||
|
|
||||||
|
type AddSlotInput = Omit<GQL.CreateSlotMutationVariables['input'], 'master'>;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
type FixTypescriptCringe = ApolloTypes.FetchResult;
|
||||||
|
|
||||||
|
export async function addSlot(input: AddSlotInput) {
|
||||||
|
const customer = await getProfile();
|
||||||
|
|
||||||
|
return api.createSlot({
|
||||||
|
...input,
|
||||||
|
date: formatDate(input.date).db(),
|
||||||
|
master: customer?.documentId,
|
||||||
|
time_end: formatTime(input.time_end).db(),
|
||||||
|
time_start: formatTime(input.time_start).db(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSlots(input: GQL.GetSlotsQueryVariables) {
|
||||||
|
const customer = await getProfile();
|
||||||
|
|
||||||
|
if (!customer?.documentId) throw new Error('Customer not found');
|
||||||
|
|
||||||
|
return api.getSlots({
|
||||||
|
filters: {
|
||||||
|
...input.filters,
|
||||||
|
master: {
|
||||||
|
documentId: {
|
||||||
|
eq: customer.documentId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateSlot(input: GQL.UpdateSlotMutationVariables) {
|
||||||
|
const customer = await getProfile();
|
||||||
|
|
||||||
|
if (!customer?.documentId) throw new Error('Customer not found');
|
||||||
|
|
||||||
|
return api.updateSlot({
|
||||||
|
...input,
|
||||||
|
data: {
|
||||||
|
...input.data,
|
||||||
|
date: input.data?.date ? formatDate(input.data.date).db() : undefined,
|
||||||
|
time_end: input.data?.time_end ? formatTime(input.data.time_end).db() : undefined,
|
||||||
|
time_start: input.data?.time_start ? formatTime(input.data.time_start).db() : undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSlot = api.getSlot;
|
||||||
|
export const deleteSlot = api.deleteSlot;
|
||||||
@ -8,7 +8,7 @@ export default function ContactsPage() {
|
|||||||
<ContactsFilterProvider>
|
<ContactsFilterProvider>
|
||||||
<Card>
|
<Card>
|
||||||
<div className="flex flex-row items-center justify-between space-x-4 p-4">
|
<div className="flex flex-row items-center justify-between space-x-4 p-4">
|
||||||
<h1 className="text-2xl font-bold">Контакты</h1>
|
<h1 className="font-bold">Контакты</h1>
|
||||||
<ContactsFilter />
|
<ContactsFilter />
|
||||||
</div>
|
</div>
|
||||||
<div className="p-4 pt-0">
|
<div className="p-4 pt-0">
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import { type PropsWithChildren } from 'react';
|
|||||||
export default async function Layout({ children }: Readonly<PropsWithChildren>) {
|
export default async function Layout({ children }: Readonly<PropsWithChildren>) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<main>{children}</main>
|
<main className="grow">{children}</main>
|
||||||
<BottomNav />
|
<BottomNav />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
3
apps/web/app/(main)/orders/add/page.tsx
Normal file
3
apps/web/app/(main)/orders/add/page.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default function AddOrdersPage() {
|
||||||
|
return 'Add Orders';
|
||||||
|
}
|
||||||
3
apps/web/app/(main)/orders/page.tsx
Normal file
3
apps/web/app/(main)/orders/page.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export default function OrdersPage() {
|
||||||
|
return 'Orders';
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { getProfile } from '@/actions/profile';
|
import { Container } from '@/components/layout';
|
||||||
import { PageHeader } from '@/components/navigation';
|
import { PageHeader } from '@/components/navigation';
|
||||||
import { ProfileCard } from '@/components/profile/profile-card';
|
import { ContactDataCard, PersonCard } from '@/components/profile';
|
||||||
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';
|
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
type Props = { params: Promise<{ telegramId: string }> };
|
type Props = { params: Promise<{ telegramId: string }> };
|
||||||
@ -11,15 +11,13 @@ export default async function ProfilePage(props: Readonly<Props>) {
|
|||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
await queryClient.prefetchQuery({
|
|
||||||
queryFn: () => getProfile({ telegramId }),
|
|
||||||
queryKey: telegramId ? ['profile', 'telegramId', telegramId] : ['profile'],
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HydrationBoundary state={dehydrate(queryClient)}>
|
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||||
<PageHeader title="Профиль контакта" />
|
<PageHeader title="Профиль контакта" />
|
||||||
<ProfileCard telegramId={telegramId} />
|
<Container className="px-0">
|
||||||
|
<PersonCard telegramId={telegramId} />
|
||||||
|
<ContactDataCard telegramId={telegramId} />
|
||||||
|
</Container>
|
||||||
</HydrationBoundary>
|
</HydrationBoundary>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,18 +1,17 @@
|
|||||||
import { getProfile } from '@/actions/profile';
|
import { Container } from '@/components/layout';
|
||||||
import { ProfileCard } from '@/components/profile/profile-card';
|
import { LinksCard, PersonCard, ProfileDataCard } from '@/components/profile';
|
||||||
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';
|
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
export default async function ProfilePage() {
|
export default async function ProfilePage() {
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
await queryClient.prefetchQuery({
|
|
||||||
queryFn: () => getProfile(),
|
|
||||||
queryKey: ['profile'],
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HydrationBoundary state={dehydrate(queryClient)}>
|
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||||
<ProfileCard />
|
<Container className="px-0">
|
||||||
|
<PersonCard />
|
||||||
|
<ProfileDataCard />
|
||||||
|
<LinksCard />
|
||||||
|
</Container>
|
||||||
</HydrationBoundary>
|
</HydrationBoundary>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
6
apps/web/app/(main)/profile/schedule/layout.tsx
Normal file
6
apps/web/app/(main)/profile/schedule/layout.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { ScheduleContextProvider } from '@/context/schedule';
|
||||||
|
import { type PropsWithChildren } from 'react';
|
||||||
|
|
||||||
|
export default async function Layout({ children }: Readonly<PropsWithChildren>) {
|
||||||
|
return <ScheduleContextProvider>{children}</ScheduleContextProvider>;
|
||||||
|
}
|
||||||
15
apps/web/app/(main)/profile/schedule/page.tsx
Normal file
15
apps/web/app/(main)/profile/schedule/page.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Container } from '@/components/layout';
|
||||||
|
import { PageHeader } from '@/components/navigation';
|
||||||
|
import { DaySlotsList, ScheduleCalendar } from '@/components/schedule';
|
||||||
|
|
||||||
|
export default function SchedulePage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<PageHeader title="График работы" />
|
||||||
|
<Container className="px-0">
|
||||||
|
<ScheduleCalendar />
|
||||||
|
<DaySlotsList />
|
||||||
|
</Container>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { Container } from '@/components/layout';
|
||||||
|
import { PageHeader } from '@/components/navigation';
|
||||||
|
import { SlotButtons, SlotDateTime, SlotOrdersList } from '@/components/schedule';
|
||||||
|
import { dehydrate, HydrationBoundary, QueryClient } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
type Props = { params: Promise<{ documentId: string }> };
|
||||||
|
|
||||||
|
export default async function ProfilePage(props: Readonly<Props>) {
|
||||||
|
const parameters = await props.params;
|
||||||
|
|
||||||
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HydrationBoundary state={dehydrate(queryClient)}>
|
||||||
|
<PageHeader title={undefined} />
|
||||||
|
<Container>
|
||||||
|
<SlotDateTime {...parameters} />
|
||||||
|
<SlotButtons {...parameters} />
|
||||||
|
<SlotOrdersList {...parameters} />
|
||||||
|
</Container>
|
||||||
|
</HydrationBoundary>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,3 +0,0 @@
|
|||||||
export default function AddRecordsPage() {
|
|
||||||
return 'Add Records';
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
export default function RecordsPage() {
|
|
||||||
return 'Records';
|
|
||||||
}
|
|
||||||
@ -1,12 +1,17 @@
|
|||||||
import { AuthProvider } from '@/providers/auth';
|
import { AuthProvider } from '@/providers/auth';
|
||||||
|
import { ErrorProvider } from '@/providers/error';
|
||||||
import { QueryProvider } from '@/providers/query';
|
import { QueryProvider } from '@/providers/query';
|
||||||
import { ThemeProvider } from '@/providers/theme-provider';
|
import { ThemeProvider } from '@/providers/theme-provider';
|
||||||
import { I18nProvider } from '@/utils/i18n/provider';
|
import { I18nProvider } from '@/utils/i18n/provider';
|
||||||
import '@repo/ui/globals.css';
|
import '@repo/ui/globals.css';
|
||||||
|
import { cn } from '@repo/ui/lib/utils';
|
||||||
import { type Metadata } from 'next';
|
import { type Metadata } from 'next';
|
||||||
import { getLocale } from 'next-intl/server';
|
import { getLocale } from 'next-intl/server';
|
||||||
|
import { Inter } from 'next/font/google';
|
||||||
import { type PropsWithChildren } from 'react';
|
import { type PropsWithChildren } from 'react';
|
||||||
|
|
||||||
|
const inter = Inter({ subsets: ['latin', 'cyrillic'] });
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: 'Запишись.онлайн',
|
title: 'Запишись.онлайн',
|
||||||
};
|
};
|
||||||
@ -16,7 +21,8 @@ export default async function RootLayout({ children }: Readonly<PropsWithChildre
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang={locale}>
|
<html lang={locale}>
|
||||||
<body className="bg-app-background">
|
<body className={cn(inter.className, 'flex min-h-screen flex-col bg-app-background')}>
|
||||||
|
<ErrorProvider>
|
||||||
<I18nProvider>
|
<I18nProvider>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
@ -24,6 +30,7 @@ export default async function RootLayout({ children }: Readonly<PropsWithChildre
|
|||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</I18nProvider>
|
</I18nProvider>
|
||||||
|
</ErrorProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
13
apps/web/components/common/divider.tsx
Normal file
13
apps/web/components/common/divider.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { cn } from '@repo/ui/lib/utils';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
readonly className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function HorizontalDivider({ className }: Props) {
|
||||||
|
return (
|
||||||
|
<div className={cn('', className)}>
|
||||||
|
<div className="h-px w-full bg-muted-foreground/15" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -10,7 +10,7 @@ import {
|
|||||||
import { ChevronDown } from 'lucide-react';
|
import { ChevronDown } from 'lucide-react';
|
||||||
import { use } from 'react';
|
import { use } from 'react';
|
||||||
|
|
||||||
const filterLabels: Record<FilterType, string> = {
|
const filterLabels: Order<FilterType, string> = {
|
||||||
all: 'Все',
|
all: 'Все',
|
||||||
clients: 'Клиенты',
|
clients: 'Клиенты',
|
||||||
masters: 'Мастера',
|
masters: 'Мастера',
|
||||||
|
|||||||
9
apps/web/components/layout/container.tsx
Normal file
9
apps/web/components/layout/container.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { cn } from '@repo/ui/lib/utils';
|
||||||
|
import { type PropsWithChildren } from 'react';
|
||||||
|
|
||||||
|
export function Container({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
}: Readonly<PropsWithChildren> & { readonly className?: string }) {
|
||||||
|
return <div className={cn('space-y-4 px-4 last:mb-4', className)}>{children}</div>;
|
||||||
|
}
|
||||||
1
apps/web/components/layout/index.ts
Normal file
1
apps/web/components/layout/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './container';
|
||||||
@ -1,14 +1,13 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { BackButton } from './back-button';
|
import { BackButton } from './back-button';
|
||||||
import { PageTitle } from './page-title';
|
|
||||||
|
|
||||||
type Props = { title: string | undefined };
|
type Props = { title: string | undefined };
|
||||||
|
|
||||||
export function PageHeader(props: Readonly<Props>) {
|
export function PageHeader(props: Readonly<Props>) {
|
||||||
return (
|
return (
|
||||||
<div className="sticky top-0 z-50 flex items-center bg-background p-2">
|
<div className="sticky top-0 z-50 flex h-12 items-center rounded-b-lg bg-transparent px-2 font-bold tracking-wide backdrop-blur-md">
|
||||||
<BackButton />
|
<BackButton />
|
||||||
<PageTitle title={props.title} />
|
{props.title}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +0,0 @@
|
|||||||
type Props = { readonly title: string | undefined };
|
|
||||||
|
|
||||||
export function PageTitle(props: Readonly<Props>) {
|
|
||||||
return <span className="h-8 text-lg font-bold tracking-wide">{props?.title}</span>;
|
|
||||||
}
|
|
||||||
@ -10,11 +10,11 @@ export function BottomNav() {
|
|||||||
if (!isFirstLevel) return null;
|
if (!isFirstLevel) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="fixed inset-x-0 bottom-0 border-t border-border bg-background">
|
<nav className="sticky inset-x-0 bottom-0 border-t border-border bg-background">
|
||||||
<div className="grid grid-cols-5">
|
<div className="grid grid-cols-5">
|
||||||
<NavButton href="/dashboard" icon={<Newspaper />} label="Главное" />
|
<NavButton href="/dashboard" icon={<Newspaper />} label="Главное" />
|
||||||
<NavButton href="/records" icon={<BookOpen />} label="Записи" />
|
<NavButton href="/orders" icon={<BookOpen />} label="Записи" />
|
||||||
<NavButton href="/records/add" icon={<PlusCircle />} label="Новая запись" />
|
<NavButton href="/orders/add" icon={<PlusCircle />} label="Новая запись" />
|
||||||
<NavButton href="/contacts" icon={<Users />} label="Контакты" />
|
<NavButton href="/contacts" icon={<Users />} label="Контакты" />
|
||||||
<NavButton href="/profile" icon={<User />} label="Профиль" />
|
<NavButton href="/profile" icon={<User />} label="Профиль" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
12
apps/web/components/profile/components/card-header.tsx
Normal file
12
apps/web/components/profile/components/card-header.tsx
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
type Props = {
|
||||||
|
title: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ProfileCardHeader({ title }: Readonly<Props>) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-row justify-between">
|
||||||
|
<h1 className="font-bold text-primary">{title}</h1>
|
||||||
|
<div />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
4
apps/web/components/profile/components/index.ts
Normal file
4
apps/web/components/profile/components/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from './card-header';
|
||||||
|
export * from './checkbox-field';
|
||||||
|
export * from './link-button';
|
||||||
|
export * from './text-field';
|
||||||
21
apps/web/components/profile/components/link-button.tsx
Normal file
21
apps/web/components/profile/components/link-button.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
readonly description?: string;
|
||||||
|
readonly href: string;
|
||||||
|
readonly text: string;
|
||||||
|
readonly visible?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function LinkButton({ description, href, text, visible }: Props) {
|
||||||
|
if (!visible) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link href={href} rel="noopener noreferrer">
|
||||||
|
<div className="flex w-full flex-col rounded-2xl bg-background p-4 px-6 shadow-lg backdrop-blur-2xl dark:bg-primary/5">
|
||||||
|
<h2 className="font-bold text-foreground">{text}</h2>
|
||||||
|
<span className="mt-1 text-sm text-muted-foreground">{description}</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -16,7 +16,7 @@ type ProfileFieldProps = {
|
|||||||
readonly value: string;
|
readonly value: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ProfileField({
|
export function DataField({
|
||||||
disabled = false,
|
disabled = false,
|
||||||
fieldName,
|
fieldName,
|
||||||
id,
|
id,
|
||||||
58
apps/web/components/profile/data-card.tsx
Normal file
58
apps/web/components/profile/data-card.tsx
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
'use client';
|
||||||
|
import { CheckboxWithText, DataField, ProfileCardHeader } from './components';
|
||||||
|
import { type ProfileProps } from './types';
|
||||||
|
import { useProfileMutation, useProfileQuery } from '@/hooks/profile';
|
||||||
|
import { Enum_Customer_Role as Role } from '@repo/graphql/types';
|
||||||
|
import { Button } from '@repo/ui/components/ui/button';
|
||||||
|
import { Card } from '@repo/ui/components/ui/card';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export function ContactDataCard({ telegramId }: Readonly<ProfileProps>) {
|
||||||
|
const { data: customer } = useProfileQuery({ telegramId });
|
||||||
|
|
||||||
|
if (!customer) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="p-4">
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<Link href={customer?.phone ? `tel:${customer?.phone}` : ''}>
|
||||||
|
<DataField id="phone" label="Телефон" readOnly value={customer?.phone ?? ''} />
|
||||||
|
</Link>
|
||||||
|
<Button asChild className="w-full bg-foreground">
|
||||||
|
<Link href={`https://t.me/${customer?.phone}`} rel="noopener noreferrer" target="_blank">
|
||||||
|
Написать в Telegram
|
||||||
|
</Link>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ProfileDataCard() {
|
||||||
|
const { data: customer } = useProfileQuery({});
|
||||||
|
const { mutate: updateProfile } = useProfileMutation({});
|
||||||
|
|
||||||
|
if (!customer) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="p-4">
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<ProfileCardHeader title="Ваши данные" />
|
||||||
|
<DataField
|
||||||
|
fieldName="name"
|
||||||
|
id="name"
|
||||||
|
label="Имя"
|
||||||
|
onChange={updateProfile}
|
||||||
|
value={customer?.name ?? ''}
|
||||||
|
/>
|
||||||
|
<DataField disabled id="phone" label="Телефон" readOnly value={customer?.phone ?? ''} />
|
||||||
|
<CheckboxWithText
|
||||||
|
checked={customer.role !== 'client'}
|
||||||
|
description="Разрешить другим пользователям записываться к вам"
|
||||||
|
onChange={(checked) => updateProfile({ role: checked ? Role.Master : Role.Client })}
|
||||||
|
text="Быть мастером"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
3
apps/web/components/profile/index.ts
Normal file
3
apps/web/components/profile/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './data-card';
|
||||||
|
export * from './links-card';
|
||||||
|
export * from './person-card';
|
||||||
@ -1,9 +0,0 @@
|
|||||||
/* eslint-disable canonical/id-match */
|
|
||||||
import { updateProfile } from '@/actions/profile';
|
|
||||||
import { Enum_Customer_Role } from '@repo/graphql/types';
|
|
||||||
|
|
||||||
export function updateRole(checked: boolean) {
|
|
||||||
return updateProfile({
|
|
||||||
role: checked ? Enum_Customer_Role.Master : Enum_Customer_Role.Client,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
23
apps/web/components/profile/links-card.tsx
Normal file
23
apps/web/components/profile/links-card.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/* eslint-disable canonical/id-match */
|
||||||
|
'use client';
|
||||||
|
import { LinkButton } from './components';
|
||||||
|
import { type ProfileProps } from './types';
|
||||||
|
import { useProfileQuery } from '@/hooks/profile';
|
||||||
|
import { Enum_Customer_Role } from '@repo/graphql/types';
|
||||||
|
|
||||||
|
export function LinksCard({ telegramId }: Readonly<ProfileProps>) {
|
||||||
|
const { data: customer } = useProfileQuery({ telegramId });
|
||||||
|
|
||||||
|
const isMaster = customer?.role === Enum_Customer_Role.Master;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-4 p-4 py-0">
|
||||||
|
<LinkButton
|
||||||
|
description="Указать доступные дни и время для записи клиентов"
|
||||||
|
href="/profile/schedule"
|
||||||
|
text="График работы"
|
||||||
|
visible={isMaster}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
29
apps/web/components/profile/person-card.tsx
Normal file
29
apps/web/components/profile/person-card.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
'use client';
|
||||||
|
import { LoadingSpinner } from '../common/spinner';
|
||||||
|
import { type ProfileProps } from './types';
|
||||||
|
import { useProfileQuery } from '@/hooks/profile';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@repo/ui/components/ui/avatar';
|
||||||
|
import { Card } from '@repo/ui/components/ui/card';
|
||||||
|
|
||||||
|
export function PersonCard({ telegramId }: Readonly<ProfileProps>) {
|
||||||
|
const { data: customer, isLoading } = useProfileQuery({ telegramId });
|
||||||
|
|
||||||
|
if (isLoading || !customer)
|
||||||
|
return (
|
||||||
|
<div className="p-4">
|
||||||
|
<LoadingSpinner />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className="bg-transparent p-4 shadow-none">
|
||||||
|
<div className="flex flex-col items-center space-y-2">
|
||||||
|
<Avatar className="size-20">
|
||||||
|
<AvatarImage alt={customer?.name} src={customer.photoUrl || ''} />
|
||||||
|
<AvatarFallback>{customer?.name.charAt(0)}</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<h2 className="text-2xl font-bold">{customer?.name}</h2>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,113 +0,0 @@
|
|||||||
'use client';
|
|
||||||
import { updateRole } from './lib/actions';
|
|
||||||
import { getProfile, updateProfile } from '@/actions/profile';
|
|
||||||
import { CheckboxWithText } from '@/components/profile/checkbox-with-text';
|
|
||||||
import { ProfileField } from '@/components/profile/profile-field';
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from '@repo/ui/components/ui/avatar';
|
|
||||||
import { Button } from '@repo/ui/components/ui/button';
|
|
||||||
import { Card } from '@repo/ui/components/ui/card';
|
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
type ProfileProps = {
|
|
||||||
readonly telegramId?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function ProfileCard(props: ProfileProps) {
|
|
||||||
return (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<Person {...props} />
|
|
||||||
<ProfileFields {...props} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ProfileFields({ telegramId }: ProfileProps) {
|
|
||||||
const { data: customer } = useQuery({
|
|
||||||
queryFn: () => getProfile({ telegramId }),
|
|
||||||
queryKey: telegramId ? ['profile', 'telegramId', telegramId] : ['profile'],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!customer) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card className="p-4">
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
{telegramId ? false : <ProfileFieldsHeader />}
|
|
||||||
{telegramId ? (
|
|
||||||
false
|
|
||||||
) : (
|
|
||||||
<ProfileField
|
|
||||||
fieldName="name"
|
|
||||||
id="name"
|
|
||||||
label="Имя"
|
|
||||||
onChange={updateProfile}
|
|
||||||
value={customer?.name ?? ''}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<Link href={telegramId && customer?.phone ? `tel:${customer?.phone}` : ''}>
|
|
||||||
<ProfileField
|
|
||||||
disabled={!telegramId}
|
|
||||||
id="phone"
|
|
||||||
label="Телефон"
|
|
||||||
readOnly={Boolean(telegramId)}
|
|
||||||
value={customer?.phone ?? ''}
|
|
||||||
/>
|
|
||||||
</Link>
|
|
||||||
{telegramId ? (
|
|
||||||
false
|
|
||||||
) : (
|
|
||||||
<CheckboxWithText
|
|
||||||
checked={customer.role !== 'client'}
|
|
||||||
description="Разрешить другим пользователям записываться к вам"
|
|
||||||
onChange={updateRole}
|
|
||||||
text="Быть мастером"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{telegramId ? (
|
|
||||||
<Button asChild className="w-full bg-foreground">
|
|
||||||
<Link
|
|
||||||
href={`https://t.me/${customer?.phone}`}
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
Написать в Telegram
|
|
||||||
</Link>
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
false
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Person({ telegramId }: ProfileProps) {
|
|
||||||
const { data: customer } = useQuery({
|
|
||||||
queryFn: () => getProfile({ telegramId }),
|
|
||||||
queryKey: telegramId ? ['profile', 'telegramId', telegramId] : ['profile'],
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!customer) return null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card className="p-4">
|
|
||||||
<div className="flex flex-col items-center space-y-2">
|
|
||||||
<Avatar className="size-20">
|
|
||||||
<AvatarImage alt={customer?.name} src={customer.photoUrl || ''} />
|
|
||||||
<AvatarFallback>{customer?.name.charAt(0)}</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<h2 className="text-2xl font-bold">{customer?.name}</h2>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ProfileFieldsHeader() {
|
|
||||||
return (
|
|
||||||
<div className="flex flex-row justify-between">
|
|
||||||
<h1 className="text-lg font-bold text-muted-foreground">Ваши данные</h1>
|
|
||||||
<div />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
3
apps/web/components/profile/types.tsx
Normal file
3
apps/web/components/profile/types.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export type ProfileProps = {
|
||||||
|
readonly telegramId?: string;
|
||||||
|
};
|
||||||
23
apps/web/components/schedule/calendar.tsx
Normal file
23
apps/web/components/schedule/calendar.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
'use client';
|
||||||
|
import { ScheduleContext } from '@/context/schedule';
|
||||||
|
import { Calendar } from '@repo/ui/components/ui/calendar';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { use } from 'react';
|
||||||
|
|
||||||
|
export function ScheduleCalendar() {
|
||||||
|
const { selectedDate, setSelectedDate } = use(ScheduleContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Calendar
|
||||||
|
className="bg-background"
|
||||||
|
disabled={(date) => {
|
||||||
|
return dayjs().isAfter(dayjs(date), 'day');
|
||||||
|
}}
|
||||||
|
mode="single"
|
||||||
|
onSelect={(date) => {
|
||||||
|
if (date) setSelectedDate(date);
|
||||||
|
}}
|
||||||
|
selected={selectedDate}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
76
apps/web/components/schedule/components/order-card.tsx
Normal file
76
apps/web/components/schedule/components/order-card.tsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/* eslint-disable canonical/id-match */
|
||||||
|
'use client';
|
||||||
|
import { type OrderClient, type OrderComponentProps } from '../types';
|
||||||
|
import { ReadonlyTimeRange } from './time-range';
|
||||||
|
import { useOrderQuery } from '@/hooks/orders';
|
||||||
|
import { Enum_Order_State } from '@repo/graphql/types';
|
||||||
|
import { Avatar, AvatarFallback, AvatarImage } from '@repo/ui/components/ui/avatar';
|
||||||
|
import { Badge } from '@repo/ui/components/ui/badge';
|
||||||
|
import { cn } from '@repo/ui/lib/utils';
|
||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export function OrderCard({ documentId }: Readonly<OrderComponentProps>) {
|
||||||
|
const { data } = useOrderQuery({ documentId });
|
||||||
|
const order = data?.data?.order;
|
||||||
|
|
||||||
|
if (!order) return null;
|
||||||
|
|
||||||
|
const isCompleted = order?.state === Enum_Order_State.Completed;
|
||||||
|
const isCancelled = order?.state === Enum_Order_State.Cancelled;
|
||||||
|
|
||||||
|
const services = order?.services.map((service) => service?.name).join(', ');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link href={`/orders/${documentId}`} rel="noopener noreferrer">
|
||||||
|
<div className="flex items-center justify-between rounded-2xl bg-background p-4 px-6 dark:bg-primary/5">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
<ClientAvatar client={order.client} />
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<ReadonlyTimeRange time_end={order?.time_end} time_start={order?.time_start} />
|
||||||
|
<span className="truncate text-xs text-muted-foreground">{services}</span>
|
||||||
|
</div>
|
||||||
|
{/* <span className="text-xs text-foreground">{clientName}</span> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{order?.state && (
|
||||||
|
<Badge
|
||||||
|
className={cn(
|
||||||
|
isCompleted
|
||||||
|
? 'bg-green-100 text-green-500 dark:bg-green-700 dark:text-green-100'
|
||||||
|
: '',
|
||||||
|
isCancelled ? 'bg-red-100 text-red-500 dark:bg-red-700 dark:text-red-100' : '',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{getBadgeText(order?.state)}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ClientAvatar({ client }: { readonly client: OrderClient }) {
|
||||||
|
if (!client) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Avatar>
|
||||||
|
<AvatarImage alt={client.name} src={client.photoUrl || ''} />
|
||||||
|
<AvatarFallback>{client.name.charAt(0)}</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAP_BADGE_TEXT: Record<Enum_Order_State, string> = {
|
||||||
|
approved: 'Подтверждено',
|
||||||
|
cancelled: 'Отменено',
|
||||||
|
completed: 'Завершено',
|
||||||
|
created: 'Создано',
|
||||||
|
scheduled: 'Запланировано',
|
||||||
|
};
|
||||||
|
|
||||||
|
function getBadgeText(state: Enum_Order_State) {
|
||||||
|
if (!state) return '';
|
||||||
|
|
||||||
|
return MAP_BADGE_TEXT[state];
|
||||||
|
}
|
||||||
66
apps/web/components/schedule/components/slot-card.tsx
Normal file
66
apps/web/components/schedule/components/slot-card.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/* eslint-disable canonical/id-match */
|
||||||
|
'use client';
|
||||||
|
import { type SlotComponentProps } from '../types';
|
||||||
|
import { ReadonlyTimeRange } from './time-range';
|
||||||
|
import { useSlotQuery } from '@/hooks/slots';
|
||||||
|
import { Enum_Slot_State } from '@repo/graphql/types';
|
||||||
|
import { Badge } from '@repo/ui/components/ui/badge';
|
||||||
|
import { cn } from '@repo/ui/lib/utils';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { usePathname } from 'next/navigation';
|
||||||
|
|
||||||
|
const MAP_BADGE_TEXT: Record<Enum_Slot_State, string> = {
|
||||||
|
closed: 'Закрыто',
|
||||||
|
open: 'Открыто',
|
||||||
|
reserved: 'Зарезервировано',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function SlotCard(props: Readonly<SlotComponentProps>) {
|
||||||
|
const pathname = usePathname();
|
||||||
|
const { documentId } = props;
|
||||||
|
|
||||||
|
const { data } = useSlotQuery({ documentId });
|
||||||
|
const slot = data?.data?.slot;
|
||||||
|
|
||||||
|
if (!slot) return null;
|
||||||
|
|
||||||
|
const ordersNumber = slot.orders?.length;
|
||||||
|
const hasOrders = Boolean(ordersNumber);
|
||||||
|
|
||||||
|
const isOpened = slot?.state === Enum_Slot_State.Open;
|
||||||
|
const isClosed = slot?.state === Enum_Slot_State.Closed;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link href={`${pathname}/slots/${documentId}`} rel="noopener noreferrer">
|
||||||
|
<div className="flex items-center justify-between rounded-2xl bg-background p-4 px-6 dark:bg-primary/5">
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<ReadonlyTimeRange {...slot} />
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
'text-xs font-normal',
|
||||||
|
hasOrders ? 'font-bold text-foreground' : 'text-muted-foreground',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{hasOrders ? 'Есть записи' : 'Свободно'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{slot.state && (
|
||||||
|
<Badge
|
||||||
|
className={cn(
|
||||||
|
isOpened ? 'bg-green-100 text-green-500 dark:bg-green-700 dark:text-green-100' : '',
|
||||||
|
isClosed ? 'bg-red-100 text-red-500 dark:bg-red-700 dark:text-red-100' : '',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{getBadgeText(slot.state)}
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBadgeText(state: Enum_Slot_State) {
|
||||||
|
if (!state) return '';
|
||||||
|
|
||||||
|
return MAP_BADGE_TEXT[state];
|
||||||
|
}
|
||||||
17
apps/web/components/schedule/components/slot-date.tsx
Normal file
17
apps/web/components/schedule/components/slot-date.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
'use client';
|
||||||
|
import { type SlotComponentProps } from '../types';
|
||||||
|
import { useSlotQuery } from '@/hooks/slots';
|
||||||
|
import { formatDate } from '@/utils/date';
|
||||||
|
|
||||||
|
export function SlotDate({ documentId }: Readonly<SlotComponentProps>) {
|
||||||
|
const { data } = useSlotQuery({ documentId });
|
||||||
|
const slot = data?.data?.slot;
|
||||||
|
|
||||||
|
if (!slot) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span className="text-sm tracking-wide text-muted-foreground">
|
||||||
|
{formatDate(slot?.date).user()}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
73
apps/web/components/schedule/components/slot-time.tsx
Normal file
73
apps/web/components/schedule/components/slot-time.tsx
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/* eslint-disable react/jsx-no-bind */
|
||||||
|
'use client';
|
||||||
|
import { ScheduleTimeContext } from '../context';
|
||||||
|
import { type SlotComponentProps } from '../types';
|
||||||
|
import { EditableTimeRangeForm, ReadonlyTimeRange } from './time-range';
|
||||||
|
import { useSlotMutation, useSlotQuery } from '@/hooks/slots';
|
||||||
|
import { Button } from '@repo/ui/components/ui/button';
|
||||||
|
import { PencilLine } from 'lucide-react';
|
||||||
|
import { use, useEffect } from 'react';
|
||||||
|
|
||||||
|
export function SlotTime(props: Readonly<SlotComponentProps>) {
|
||||||
|
const { editMode } = use(ScheduleTimeContext);
|
||||||
|
|
||||||
|
return editMode ? <SlotTimeEditForm {...props} /> : <SlotTimeReadonly {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SlotTimeEditForm({ documentId }: Readonly<SlotComponentProps>) {
|
||||||
|
const { editMode, endTime, resetTime, setEditMode, setEndTime, setStartTime, startTime } =
|
||||||
|
use(ScheduleTimeContext);
|
||||||
|
const { isPending: isMutationPending, mutate: updateSlot } = useSlotMutation({ documentId });
|
||||||
|
|
||||||
|
const { data, isPending: isQueryPending } = useSlotQuery({ documentId });
|
||||||
|
const slot = data?.data?.slot;
|
||||||
|
|
||||||
|
const isPending = isMutationPending || isQueryPending;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (editMode) {
|
||||||
|
if (slot?.time_start) setStartTime(slot.time_start);
|
||||||
|
if (slot?.time_end) setEndTime(slot.time_end);
|
||||||
|
}
|
||||||
|
}, [editMode, setEndTime, setStartTime, slot]);
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
updateSlot({ data: { time_end: endTime, time_start: startTime }, documentId });
|
||||||
|
resetTime();
|
||||||
|
setEditMode(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableTimeRangeForm disabled={isPending} onSubmit={handleSubmit}>
|
||||||
|
<Button className="rounded-full text-xs" disabled={isPending} onClick={() => handleSubmit()}>
|
||||||
|
Сохранить
|
||||||
|
</Button>
|
||||||
|
</EditableTimeRangeForm>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SlotTimeReadonly(props: Readonly<SlotComponentProps>) {
|
||||||
|
const { setEditMode } = use(ScheduleTimeContext);
|
||||||
|
|
||||||
|
const { data } = useSlotQuery(props);
|
||||||
|
const slot = data?.data?.slot;
|
||||||
|
|
||||||
|
if (!slot) return null;
|
||||||
|
|
||||||
|
const hasOrders = Boolean(slot?.orders.length);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<ReadonlyTimeRange {...slot} className="text-3xl" />
|
||||||
|
<Button
|
||||||
|
className="rounded-full text-xs"
|
||||||
|
disabled={hasOrders}
|
||||||
|
onClick={() => setEditMode(true)}
|
||||||
|
size="icon"
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
<PencilLine className="size-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
69
apps/web/components/schedule/components/time-range.tsx
Normal file
69
apps/web/components/schedule/components/time-range.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
'use client';
|
||||||
|
import { ScheduleTimeContext } from '../context';
|
||||||
|
import { formatTime } from '@/utils/date';
|
||||||
|
import { Input } from '@repo/ui/components/ui/input';
|
||||||
|
import { cn } from '@repo/ui/lib/utils';
|
||||||
|
import { type FormEvent, type PropsWithChildren, use } from 'react';
|
||||||
|
|
||||||
|
type EditableTimeRangeProps = {
|
||||||
|
readonly disabled?: boolean;
|
||||||
|
readonly onSubmit: (event: FormEvent) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TimeRangeProps = {
|
||||||
|
readonly className?: string;
|
||||||
|
readonly delimiter?: boolean;
|
||||||
|
readonly time_end: string;
|
||||||
|
readonly time_start: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function EditableTimeRangeForm({
|
||||||
|
children,
|
||||||
|
disabled = false,
|
||||||
|
onSubmit,
|
||||||
|
}: PropsWithChildren<EditableTimeRangeProps>) {
|
||||||
|
const { endTime, setEndTime, setStartTime, startTime } = use(ScheduleTimeContext);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form className="flex flex-row items-center gap-2" onSubmit={onSubmit}>
|
||||||
|
<div className="flex-1">
|
||||||
|
<Input
|
||||||
|
className="rounded-xl"
|
||||||
|
disabled={disabled}
|
||||||
|
id="start-time"
|
||||||
|
onChange={(event) => setStartTime?.(event.target.value)}
|
||||||
|
required
|
||||||
|
type="time"
|
||||||
|
value={startTime}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex-1">
|
||||||
|
<Input
|
||||||
|
className="rounded-xl"
|
||||||
|
disabled={disabled}
|
||||||
|
id="end-time"
|
||||||
|
onChange={(event) => setEndTime?.(event.target.value)}
|
||||||
|
required
|
||||||
|
type="time"
|
||||||
|
value={endTime}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{children}
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ReadonlyTimeRange({
|
||||||
|
className,
|
||||||
|
delimiter = true,
|
||||||
|
time_end,
|
||||||
|
time_start,
|
||||||
|
}: Readonly<TimeRangeProps>) {
|
||||||
|
return (
|
||||||
|
<div className={cn('flex flex-row items-center gap-2 text-lg font-bold', className)}>
|
||||||
|
<span className="tracking-wider">{formatTime(time_start).user()}</span>
|
||||||
|
{delimiter && ' - '}
|
||||||
|
<span className="tracking-wider">{formatTime(time_end).user()}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
45
apps/web/components/schedule/context/index.tsx
Normal file
45
apps/web/components/schedule/context/index.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
'use client';
|
||||||
|
import {
|
||||||
|
createContext,
|
||||||
|
type Dispatch,
|
||||||
|
type PropsWithChildren,
|
||||||
|
type SetStateAction,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
export type ContextType = {
|
||||||
|
editMode: boolean;
|
||||||
|
endTime: string;
|
||||||
|
resetTime: () => void;
|
||||||
|
setEditMode: Dispatch<SetStateAction<boolean>>;
|
||||||
|
setEndTime: Dispatch<SetStateAction<string>>;
|
||||||
|
setStartTime: Dispatch<SetStateAction<string>>;
|
||||||
|
startTime: string;
|
||||||
|
};
|
||||||
|
export const ScheduleTimeContext = createContext<ContextType>({} as ContextType);
|
||||||
|
|
||||||
|
export function ScheduleTimeContextProvider({ children }: Readonly<PropsWithChildren>) {
|
||||||
|
const [editMode, setEditMode] = useState(false);
|
||||||
|
const [startTime, setStartTime] = useState('');
|
||||||
|
const [endTime, setEndTime] = useState('');
|
||||||
|
|
||||||
|
function resetTime() {
|
||||||
|
setStartTime('');
|
||||||
|
setEndTime('');
|
||||||
|
}
|
||||||
|
|
||||||
|
const value = useMemo(() => {
|
||||||
|
return {
|
||||||
|
editMode,
|
||||||
|
endTime,
|
||||||
|
resetTime,
|
||||||
|
setEditMode,
|
||||||
|
setEndTime,
|
||||||
|
setStartTime,
|
||||||
|
startTime,
|
||||||
|
};
|
||||||
|
}, [editMode, endTime, setEditMode, startTime]);
|
||||||
|
|
||||||
|
return <ScheduleTimeContext value={value}>{children}</ScheduleTimeContext>;
|
||||||
|
}
|
||||||
42
apps/web/components/schedule/day-slot-add-form.tsx
Normal file
42
apps/web/components/schedule/day-slot-add-form.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* eslint-disable canonical/id-match */
|
||||||
|
'use client';
|
||||||
|
import { EditableTimeRangeForm } from './components/time-range';
|
||||||
|
import { ScheduleTimeContext, ScheduleTimeContextProvider } from './context';
|
||||||
|
import { ScheduleContext } from '@/context/schedule';
|
||||||
|
import { useSlotAdd } from '@/hooks/slots';
|
||||||
|
import { withContext } from '@/utils/context';
|
||||||
|
import { Enum_Slot_State } from '@repo/graphql/types';
|
||||||
|
import { Button } from '@repo/ui/components/ui/button';
|
||||||
|
import { PlusSquare } from 'lucide-react';
|
||||||
|
import { type FormEvent, use } from 'react';
|
||||||
|
|
||||||
|
export const DaySlotAddForm = withContext(ScheduleTimeContextProvider)(function () {
|
||||||
|
const { endTime, resetTime, startTime } = use(ScheduleTimeContext);
|
||||||
|
|
||||||
|
const { selectedDate } = use(ScheduleContext);
|
||||||
|
|
||||||
|
const { isPending, mutate: addSlot } = useSlotAdd();
|
||||||
|
|
||||||
|
const handleSubmit = (event: FormEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
if (startTime && endTime) {
|
||||||
|
addSlot({
|
||||||
|
date: selectedDate,
|
||||||
|
state: Enum_Slot_State.Open,
|
||||||
|
time_end: endTime,
|
||||||
|
time_start: startTime,
|
||||||
|
});
|
||||||
|
|
||||||
|
resetTime();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditableTimeRangeForm disabled={isPending} onSubmit={handleSubmit}>
|
||||||
|
<Button className="rounded-full" disabled={isPending} type="submit">
|
||||||
|
<PlusSquare className="mr-1 size-4" />
|
||||||
|
Добавить
|
||||||
|
</Button>
|
||||||
|
</EditableTimeRangeForm>
|
||||||
|
);
|
||||||
|
});
|
||||||
20
apps/web/components/schedule/day-slots-list.tsx
Normal file
20
apps/web/components/schedule/day-slots-list.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
'use client';
|
||||||
|
import { SlotCard } from './components/slot-card';
|
||||||
|
import { DaySlotAddForm } from './day-slot-add-form';
|
||||||
|
import { LoadingSpinner } from '@/components/common/spinner';
|
||||||
|
import { useSlots } from '@/hooks/slots';
|
||||||
|
|
||||||
|
export function DaySlotsList() {
|
||||||
|
const { data, isLoading } = useSlots();
|
||||||
|
const slots = data?.data.slots;
|
||||||
|
|
||||||
|
if (isLoading) return <LoadingSpinner />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col space-y-2 px-4">
|
||||||
|
<h1 className="font-bold">Слоты</h1>
|
||||||
|
{slots?.map((slot) => slot && <SlotCard key={slot.documentId} {...slot} />)}
|
||||||
|
<DaySlotAddForm />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
6
apps/web/components/schedule/index.ts
Normal file
6
apps/web/components/schedule/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export * from './calendar';
|
||||||
|
export * from './day-slot-add-form';
|
||||||
|
export * from './day-slots-list';
|
||||||
|
export * from './slot-buttons';
|
||||||
|
export * from './slot-datetime';
|
||||||
|
export * from './slot-orders-list';
|
||||||
69
apps/web/components/schedule/slot-buttons.tsx
Normal file
69
apps/web/components/schedule/slot-buttons.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/* eslint-disable react/jsx-no-bind */
|
||||||
|
/* eslint-disable canonical/id-match */
|
||||||
|
'use client';
|
||||||
|
import { type SlotComponentProps } from './types';
|
||||||
|
import { useSlotDelete, useSlotMutation, useSlotQuery } from '@/hooks/slots';
|
||||||
|
import { Enum_Slot_State } from '@repo/graphql/types';
|
||||||
|
import { Button } from '@repo/ui/components/ui/button';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
export function SlotButtons({ documentId }: Readonly<SlotComponentProps>) {
|
||||||
|
const { data } = useSlotQuery({ documentId });
|
||||||
|
const { mutate: updateSlot } = useSlotMutation({ documentId });
|
||||||
|
const { mutate: deleteSlot } = useSlotDelete({ documentId });
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const slot = data?.data?.slot;
|
||||||
|
|
||||||
|
if (!slot) return null;
|
||||||
|
|
||||||
|
const isOpened = slot?.state === Enum_Slot_State.Open;
|
||||||
|
const isClosed = slot?.state === Enum_Slot_State.Closed;
|
||||||
|
|
||||||
|
function handleOpenSlot() {
|
||||||
|
return updateSlot({ data: { state: Enum_Slot_State.Open }, documentId });
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCloseSlot() {
|
||||||
|
return updateSlot({ data: { state: Enum_Slot_State.Closed }, documentId });
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDeleteSlot() {
|
||||||
|
router.back();
|
||||||
|
return deleteSlot();
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasOrders = Boolean(slot?.orders.length);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-[2fr_1fr]">
|
||||||
|
{isOpened && (
|
||||||
|
<Button
|
||||||
|
className="rounded-l-2xl rounded-r-none bg-orange-100 p-6 text-orange-500 dark:bg-yellow-700 dark:text-orange-100"
|
||||||
|
onClick={handleCloseSlot}
|
||||||
|
variant="ghost"
|
||||||
|
>
|
||||||
|
Закрыть
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{isClosed && (
|
||||||
|
<Button
|
||||||
|
className="rounded-l-2xl rounded-r-none bg-green-100 p-6 text-green-500 dark:bg-green-700 dark:text-green-100"
|
||||||
|
onClick={handleOpenSlot}
|
||||||
|
variant="ghost"
|
||||||
|
>
|
||||||
|
Открыть
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
className="rounded-l-none rounded-r-2xl bg-red-100 p-6 text-red-500 dark:bg-red-700 dark:text-red-100"
|
||||||
|
disabled={hasOrders}
|
||||||
|
onClick={handleDeleteSlot}
|
||||||
|
variant="ghost"
|
||||||
|
>
|
||||||
|
Удалить
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
17
apps/web/components/schedule/slot-datetime.tsx
Normal file
17
apps/web/components/schedule/slot-datetime.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
'use client';
|
||||||
|
import { SlotDate } from './components/slot-date';
|
||||||
|
import { SlotTime } from './components/slot-time';
|
||||||
|
import { ScheduleTimeContextProvider } from './context';
|
||||||
|
import { type SlotComponentProps } from './types';
|
||||||
|
import { withContext } from '@/utils/context';
|
||||||
|
|
||||||
|
export const SlotDateTime = withContext(ScheduleTimeContextProvider)(function (
|
||||||
|
props: Readonly<SlotComponentProps>,
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<SlotDate {...props} />
|
||||||
|
<SlotTime {...props} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
18
apps/web/components/schedule/slot-orders-list.tsx
Normal file
18
apps/web/components/schedule/slot-orders-list.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
'use client';
|
||||||
|
import { OrderCard } from './components/order-card';
|
||||||
|
import { type SlotComponentProps } from './types';
|
||||||
|
import { useSlotQuery } from '@/hooks/slots';
|
||||||
|
|
||||||
|
export function SlotOrdersList({ documentId }: Readonly<SlotComponentProps>) {
|
||||||
|
const { data } = useSlotQuery({ documentId });
|
||||||
|
const slot = data?.data?.slot;
|
||||||
|
|
||||||
|
if (!slot) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col space-y-2">
|
||||||
|
<h1 className="font-bold">Записи</h1>
|
||||||
|
{slot?.orders.map((order) => order && <OrderCard key={order.documentId} {...order} />)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
6
apps/web/components/schedule/types/index.tsx
Normal file
6
apps/web/components/schedule/types/index.tsx
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import type * as GQL from '@repo/graphql/types';
|
||||||
|
|
||||||
|
export type OrderClient = NonNullable<GQL.GetOrderQuery['order']>['client'];
|
||||||
|
export type OrderComponentProps = Pick<GQL.OrderFieldsFragment, 'documentId'>;
|
||||||
|
export type Slot = NonNullable<GQL.GetSlotQuery['slot']>;
|
||||||
|
export type SlotComponentProps = Pick<GQL.SlotFieldsFragment, 'documentId'>;
|
||||||
17
apps/web/context/schedule.tsx
Normal file
17
apps/web/context/schedule.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
'use client';
|
||||||
|
import { createContext, useMemo, useState } from 'react';
|
||||||
|
|
||||||
|
type ContextType = {
|
||||||
|
selectedDate: Date;
|
||||||
|
setSelectedDate: (date: Date) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ScheduleContext = createContext<ContextType>({} as ContextType);
|
||||||
|
|
||||||
|
export function ScheduleContextProvider({ children }: { readonly children: React.ReactNode }) {
|
||||||
|
const [selectedDate, setSelectedDate] = useState(new Date());
|
||||||
|
|
||||||
|
const value = useMemo(() => ({ selectedDate, setSelectedDate }), [selectedDate]);
|
||||||
|
|
||||||
|
return <ScheduleContext value={value}>{children}</ScheduleContext>;
|
||||||
|
}
|
||||||
@ -1 +1,2 @@
|
|||||||
|
export * from './query';
|
||||||
export * from './use-customer-contacts';
|
export * from './use-customer-contacts';
|
||||||
|
|||||||
8
apps/web/hooks/contacts/query.ts
Normal file
8
apps/web/hooks/contacts/query.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { getClients, getMasters } from '@/actions/contacts';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
export const useClientsQuery = () =>
|
||||||
|
useQuery({ queryFn: getClients, queryKey: ['contacts', 'clients', 'get'] });
|
||||||
|
|
||||||
|
export const useMastersQuery = () =>
|
||||||
|
useQuery({ queryFn: getMasters, queryKey: ['contacts', 'masters', 'get'] });
|
||||||
@ -1,26 +1,18 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import { getClients, getMasters } from '@/actions/contacts';
|
import { useClientsQuery, useMastersQuery } from './query';
|
||||||
import { ContactsFilterContext } from '@/context/contacts-filter';
|
import { ContactsFilterContext } from '@/context/contacts-filter';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { sift } from 'radash';
|
import { sift } from 'radash';
|
||||||
import { use, useMemo } from 'react';
|
import { use, useMemo } from 'react';
|
||||||
|
|
||||||
export function useCustomerContacts() {
|
export function useCustomerContacts() {
|
||||||
const { filter } = use(ContactsFilterContext);
|
const { filter } = use(ContactsFilterContext);
|
||||||
|
|
||||||
const { data: clientsData, isLoading: isLoadingClients } = useQuery({
|
const { data: clientsData, isLoading: isLoadingClients } = useClientsQuery();
|
||||||
queryFn: getClients,
|
const { data: mastersData, isLoading: isLoadingMasters } = useMastersQuery();
|
||||||
queryKey: ['contacts', 'clients'],
|
|
||||||
});
|
|
||||||
|
|
||||||
const clients = clientsData?.clients;
|
const clients = clientsData?.clients;
|
||||||
|
|
||||||
const { data: mastersData, isLoading: isLoadingMasters } = useQuery({
|
|
||||||
queryFn: getMasters,
|
|
||||||
queryKey: ['contacts', 'masters'],
|
|
||||||
});
|
|
||||||
|
|
||||||
const masters = mastersData?.masters;
|
const masters = mastersData?.masters;
|
||||||
|
const isLoading = isLoadingClients || isLoadingMasters;
|
||||||
|
|
||||||
const contacts = useMemo(() => {
|
const contacts = useMemo(() => {
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
@ -33,5 +25,5 @@ export function useCustomerContacts() {
|
|||||||
}
|
}
|
||||||
}, [clients, masters, filter]);
|
}, [clients, masters, filter]);
|
||||||
|
|
||||||
return { contacts, isLoading: isLoadingClients || isLoadingMasters };
|
return { contacts, isLoading };
|
||||||
}
|
}
|
||||||
|
|||||||
18
apps/web/hooks/orders/index.ts
Normal file
18
apps/web/hooks/orders/index.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
'use client';
|
||||||
|
import { getOrder } from '@/actions/orders';
|
||||||
|
// eslint-disable-next-line sonarjs/no-internal-api-use
|
||||||
|
import type * as ApolloTypes from '@repo/graphql/node_modules/@apollo/client/core';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
type FixTypescriptCringe = ApolloTypes.FetchResult;
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
documentId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useOrderQuery = ({ documentId }: Props) =>
|
||||||
|
useQuery({
|
||||||
|
queryFn: () => getOrder({ documentId }),
|
||||||
|
queryKey: ['orders', 'get', documentId],
|
||||||
|
});
|
||||||
21
apps/web/hooks/profile/index.ts
Normal file
21
apps/web/hooks/profile/index.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
'use client';
|
||||||
|
import { getProfile, updateProfile } from '@/actions/profile';
|
||||||
|
import { type ProfileProps } from '@/components/profile/types';
|
||||||
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
|
|
||||||
|
export const useProfileQuery = ({ telegramId }: ProfileProps) => {
|
||||||
|
return useQuery({
|
||||||
|
queryFn: () => getProfile({ telegramId }),
|
||||||
|
queryKey: telegramId ? ['profile', 'telegramId', telegramId, 'get'] : ['profile', 'get'],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useProfileMutation = ({ telegramId }: ProfileProps) => {
|
||||||
|
const { refetch } = useProfileQuery({ telegramId });
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: updateProfile,
|
||||||
|
mutationKey: ['profile', 'telegramId', telegramId, 'update'],
|
||||||
|
onSuccess: () => refetch(),
|
||||||
|
});
|
||||||
|
};
|
||||||
67
apps/web/hooks/slots/index.ts
Normal file
67
apps/web/hooks/slots/index.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
'use client';
|
||||||
|
import { addSlot, deleteSlot, getSlot, getSlots, updateSlot } from '@/actions/slots';
|
||||||
|
import { ScheduleContext } from '@/context/schedule';
|
||||||
|
import { formatDate } from '@/utils/date';
|
||||||
|
// eslint-disable-next-line sonarjs/no-internal-api-use
|
||||||
|
import type * as ApolloTypes from '@repo/graphql/node_modules/@apollo/client/core';
|
||||||
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
|
import { use } from 'react';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
type FixTypescriptCringe = ApolloTypes.FetchResult;
|
||||||
|
|
||||||
|
export const useSlots = () => {
|
||||||
|
const { selectedDate } = use(ScheduleContext);
|
||||||
|
|
||||||
|
return useQuery({
|
||||||
|
queryFn: () =>
|
||||||
|
getSlots({
|
||||||
|
filters: {
|
||||||
|
date: {
|
||||||
|
eq: formatDate(selectedDate).db(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
queryKey: ['slots', 'list', selectedDate],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
documentId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSlotQuery = ({ documentId }: Props) =>
|
||||||
|
useQuery({
|
||||||
|
queryFn: () => getSlot({ documentId }),
|
||||||
|
queryKey: ['slots', 'get', documentId],
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useSlotMutation = ({ documentId }: Props) => {
|
||||||
|
const { refetch } = useSlotQuery({ documentId });
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: updateSlot,
|
||||||
|
mutationKey: ['slots', 'update', documentId],
|
||||||
|
onSuccess: () => refetch(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSlotAdd = () => {
|
||||||
|
const { refetch } = useSlots();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: addSlot,
|
||||||
|
mutationKey: ['slots', 'add'],
|
||||||
|
onSuccess: () => refetch(),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSlotDelete = ({ documentId }: Props) => {
|
||||||
|
const { refetch } = useSlots();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: () => deleteSlot({ documentId }),
|
||||||
|
mutationKey: ['slots', 'delete', documentId],
|
||||||
|
onSuccess: () => refetch(),
|
||||||
|
});
|
||||||
|
};
|
||||||
@ -17,6 +17,7 @@
|
|||||||
"@repo/ui": "workspace:*",
|
"@repo/ui": "workspace:*",
|
||||||
"@tanstack/react-query": "^5.64.1",
|
"@tanstack/react-query": "^5.64.1",
|
||||||
"@telegram-apps/sdk-react": "^2.0.19",
|
"@telegram-apps/sdk-react": "^2.0.19",
|
||||||
|
"dayjs": "^1.11.13",
|
||||||
"graphql": "catalog:",
|
"graphql": "catalog:",
|
||||||
"lucide-react": "catalog:",
|
"lucide-react": "catalog:",
|
||||||
"next": "^15.1.5",
|
"next": "^15.1.5",
|
||||||
|
|||||||
11
apps/web/providers/error.tsx
Normal file
11
apps/web/providers/error.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Toaster } from '@repo/ui/components/ui/sonner';
|
||||||
|
import { type PropsWithChildren } from 'react';
|
||||||
|
|
||||||
|
export function ErrorProvider({ children }: Readonly<PropsWithChildren>) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Toaster position="top-center" richColors />
|
||||||
|
{children}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,7 +1,14 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { toast } from '@repo/ui/components/ui/sonner';
|
||||||
// Since QueryClientProvider relies on useContext under the hood, we have to put 'use client' on top
|
// Since QueryClientProvider relies on useContext under the hood, we have to put 'use client' on top
|
||||||
import { isServer, QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import {
|
||||||
|
isServer,
|
||||||
|
MutationCache,
|
||||||
|
QueryCache,
|
||||||
|
QueryClient,
|
||||||
|
QueryClientProvider,
|
||||||
|
} from '@tanstack/react-query';
|
||||||
|
|
||||||
function makeQueryClient() {
|
function makeQueryClient() {
|
||||||
return new QueryClient({
|
return new QueryClient({
|
||||||
@ -12,6 +19,18 @@ function makeQueryClient() {
|
|||||||
staleTime: 60 * 1_000,
|
staleTime: 60 * 1_000,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mutationCache: new MutationCache({
|
||||||
|
onError: (error_: unknown) => {
|
||||||
|
const error = error_ as Error;
|
||||||
|
toast.error('Ошибка при отправке данных: ' + error.message);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
queryCache: new QueryCache({
|
||||||
|
onError: (error_: unknown) => {
|
||||||
|
const error = error_ as Error;
|
||||||
|
toast.error('Ошибка при загрузке данных: ' + error.message);
|
||||||
|
},
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
15
apps/web/utils/context.tsx
Normal file
15
apps/web/utils/context.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { type ComponentType, type JSX } from 'react';
|
||||||
|
|
||||||
|
type ContextProvider = <T extends object>(props: T) => JSX.Element;
|
||||||
|
|
||||||
|
export function withContext<T extends object>(ContextProvider: ContextProvider) {
|
||||||
|
return function <P extends T>(Component: ComponentType<P>) {
|
||||||
|
return (props: P) => {
|
||||||
|
return (
|
||||||
|
<ContextProvider {...props}>
|
||||||
|
<Component {...props} />
|
||||||
|
</ContextProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
35
apps/web/utils/date/index.ts
Normal file
35
apps/web/utils/date/index.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* eslint-disable import/no-unassigned-import */
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import 'dayjs/locale/ru';
|
||||||
|
|
||||||
|
export function combineDateTime(date: Date, time: string) {
|
||||||
|
const [hours = '00', minutes = '00'] = time.split(':');
|
||||||
|
|
||||||
|
return new Date(
|
||||||
|
date.getFullYear(),
|
||||||
|
date.getMonth(),
|
||||||
|
date.getDate(),
|
||||||
|
Number.parseInt(hours, 10),
|
||||||
|
Number.parseInt(minutes, 10),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatDate(date: Date | string) {
|
||||||
|
return {
|
||||||
|
db: () => dayjs(date).format('YYYY-MM-DD'),
|
||||||
|
user: () => {
|
||||||
|
const lang = document.documentElement.lang || 'ru';
|
||||||
|
dayjs.locale(lang);
|
||||||
|
return dayjs(date).format('D MMMM YYYY');
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTime(time: string) {
|
||||||
|
const [hours = '00', minutes = '00'] = time.split(':');
|
||||||
|
|
||||||
|
return {
|
||||||
|
db: () => `${hours}:${minutes}:00`,
|
||||||
|
user: () => `${hours}:${minutes}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -5,13 +5,11 @@ import * as GQL from '../types';
|
|||||||
export async function login() {
|
export async function login() {
|
||||||
const { mutate } = createApolloClient();
|
const { mutate } = createApolloClient();
|
||||||
|
|
||||||
const response = await mutate({
|
return mutate({
|
||||||
mutation: GQL.LoginDocument,
|
mutation: GQL.LoginDocument,
|
||||||
variables: {
|
variables: {
|
||||||
identifier: environment.LOGIN_GRAPHQL,
|
identifier: environment.LOGIN_GRAPHQL,
|
||||||
password: environment.PASSWORD_GRAPHQL,
|
password: environment.PASSWORD_GRAPHQL,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return response;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,34 +33,28 @@ export async function createOrUpdateUser(input: GQL.CreateCustomerMutationVariab
|
|||||||
export async function getCustomer(input: GQL.GetCustomerQueryVariables) {
|
export async function getCustomer(input: GQL.GetCustomerQueryVariables) {
|
||||||
const { query } = await getClientWithToken();
|
const { query } = await getClientWithToken();
|
||||||
|
|
||||||
const response = await query({
|
return query({
|
||||||
query: GQL.GetCustomerDocument,
|
query: GQL.GetCustomerDocument,
|
||||||
variables: input,
|
variables: input,
|
||||||
});
|
});
|
||||||
|
|
||||||
return response?.data?.customers?.at(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCustomerMasters(input: GQL.GetCustomerMastersQueryVariables) {
|
export async function getCustomerMasters(input: GQL.GetCustomerMastersQueryVariables) {
|
||||||
const { query } = await getClientWithToken();
|
const { query } = await getClientWithToken();
|
||||||
|
|
||||||
const response = await query({
|
return query({
|
||||||
query: GQL.GetCustomerMastersDocument,
|
query: GQL.GetCustomerMastersDocument,
|
||||||
variables: input,
|
variables: input,
|
||||||
});
|
});
|
||||||
|
|
||||||
return response?.data?.customers?.at(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCustomerClients(input: GQL.GetCustomerClientsQueryVariables) {
|
export async function getCustomerClients(input: GQL.GetCustomerClientsQueryVariables) {
|
||||||
const { query } = await getClientWithToken();
|
const { query } = await getClientWithToken();
|
||||||
|
|
||||||
const response = await query({
|
return query({
|
||||||
query: GQL.GetCustomerClientsDocument,
|
query: GQL.GetCustomerClientsDocument,
|
||||||
variables: input,
|
variables: input,
|
||||||
});
|
});
|
||||||
|
|
||||||
return response?.data?.customers?.at(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AddCustomerMasterInput = Pick<GQL.CreateCustomerMutationVariables, 'phone' | 'telegramId'> & {
|
type AddCustomerMasterInput = Pick<GQL.CreateCustomerMutationVariables, 'phone' | 'telegramId'> & {
|
||||||
|
|||||||
@ -1,2 +1,4 @@
|
|||||||
export * from './auth';
|
export * from './auth';
|
||||||
export * from './customer';
|
export * from './customer';
|
||||||
|
export * from './slot';
|
||||||
|
export * from './order';
|
||||||
|
|||||||
12
packages/graphql/api/order.ts
Normal file
12
packages/graphql/api/order.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
'use server';
|
||||||
|
import { getClientWithToken } from '../apollo/client';
|
||||||
|
import * as GQL from '../types';
|
||||||
|
|
||||||
|
export async function getOrder(input: GQL.GetOrderQueryVariables) {
|
||||||
|
const { query } = await getClientWithToken();
|
||||||
|
|
||||||
|
return query({
|
||||||
|
query: GQL.GetOrderDocument,
|
||||||
|
variables: input,
|
||||||
|
});
|
||||||
|
}
|
||||||
48
packages/graphql/api/slot.ts
Normal file
48
packages/graphql/api/slot.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
'use server';
|
||||||
|
import { getClientWithToken } from '../apollo/client';
|
||||||
|
import * as GQL from '../types';
|
||||||
|
|
||||||
|
export async function createSlot(input: GQL.CreateSlotMutationVariables['input']) {
|
||||||
|
const { mutate } = await getClientWithToken();
|
||||||
|
|
||||||
|
return mutate({
|
||||||
|
mutation: GQL.CreateSlotDocument,
|
||||||
|
variables: { input },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSlots(input: GQL.GetSlotsQueryVariables) {
|
||||||
|
const { query } = await getClientWithToken();
|
||||||
|
|
||||||
|
return query({
|
||||||
|
query: GQL.GetSlotsDocument,
|
||||||
|
variables: input,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSlot(input: GQL.GetSlotQueryVariables) {
|
||||||
|
const { query } = await getClientWithToken();
|
||||||
|
|
||||||
|
return query({
|
||||||
|
query: GQL.GetSlotDocument,
|
||||||
|
variables: input,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateSlot(input: GQL.UpdateSlotMutationVariables) {
|
||||||
|
const { mutate } = await getClientWithToken();
|
||||||
|
|
||||||
|
return mutate({
|
||||||
|
mutation: GQL.UpdateSlotDocument,
|
||||||
|
variables: input,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function deleteSlot(input: GQL.DeleteSlotMutationVariables) {
|
||||||
|
const { mutate } = await getClientWithToken();
|
||||||
|
|
||||||
|
return mutate({
|
||||||
|
mutation: GQL.DeleteSlotDocument,
|
||||||
|
variables: input,
|
||||||
|
});
|
||||||
|
}
|
||||||
22
packages/graphql/operations/order.graphql
Normal file
22
packages/graphql/operations/order.graphql
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
fragment OrderFields on Order {
|
||||||
|
documentId
|
||||||
|
time_start
|
||||||
|
time_end
|
||||||
|
state
|
||||||
|
order_number
|
||||||
|
services {
|
||||||
|
documentId
|
||||||
|
name
|
||||||
|
}
|
||||||
|
client {
|
||||||
|
name
|
||||||
|
documentId
|
||||||
|
photoUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query GetOrder($documentId: ID!) {
|
||||||
|
order(documentId: $documentId) {
|
||||||
|
...OrderFields
|
||||||
|
}
|
||||||
|
}
|
||||||
41
packages/graphql/operations/slot.graphql
Normal file
41
packages/graphql/operations/slot.graphql
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
fragment SlotFields on Slot {
|
||||||
|
documentId
|
||||||
|
date
|
||||||
|
time_start
|
||||||
|
time_end
|
||||||
|
state
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation CreateSlot($input: SlotInput!) {
|
||||||
|
createSlot(data: $input) {
|
||||||
|
...SlotFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query GetSlots($filters: SlotFiltersInput) {
|
||||||
|
slots(filters: $filters, sort: "time_start:asc") {
|
||||||
|
documentId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query GetSlot($documentId: ID!) {
|
||||||
|
slot(documentId: $documentId) {
|
||||||
|
state
|
||||||
|
orders(sort: "time_start:asc") {
|
||||||
|
documentId
|
||||||
|
}
|
||||||
|
...SlotFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation UpdateSlot($documentId: ID!, $data: SlotInput!) {
|
||||||
|
updateSlot(documentId: $documentId, data: $data) {
|
||||||
|
...SlotFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation DeleteSlot($documentId: ID!) {
|
||||||
|
deleteSlot(documentId: $documentId) {
|
||||||
|
documentId
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,38 +13,40 @@ export type Scalars = {
|
|||||||
Boolean: { input: boolean; output: boolean; }
|
Boolean: { input: boolean; output: boolean; }
|
||||||
Int: { input: number; output: number; }
|
Int: { input: number; output: number; }
|
||||||
Float: { input: number; output: number; }
|
Float: { input: number; output: number; }
|
||||||
|
Date: { input: any; output: any; }
|
||||||
DateTime: { input: any; output: any; }
|
DateTime: { input: any; output: any; }
|
||||||
JSON: { input: any; output: any; }
|
JSON: { input: any; output: any; }
|
||||||
Long: { input: any; output: any; }
|
Long: { input: any; output: any; }
|
||||||
|
Time: { input: any; output: any; }
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BlockFiltersInput = {
|
export type BlockFiltersInput = {
|
||||||
and?: InputMaybe<Array<InputMaybe<BlockFiltersInput>>>;
|
and?: InputMaybe<Array<InputMaybe<BlockFiltersInput>>>;
|
||||||
client?: InputMaybe<CustomerFiltersInput>;
|
client?: InputMaybe<CustomerFiltersInput>;
|
||||||
createdAt?: InputMaybe<DateTimeFilterInput>;
|
createdAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
dateend?: InputMaybe<DateTimeFilterInput>;
|
datetime_end?: InputMaybe<DateTimeFilterInput>;
|
||||||
datestart?: InputMaybe<DateTimeFilterInput>;
|
datetime_start?: InputMaybe<DateTimeFilterInput>;
|
||||||
documentId?: InputMaybe<IdFilterInput>;
|
documentId?: InputMaybe<IdFilterInput>;
|
||||||
master?: InputMaybe<CustomerFiltersInput>;
|
master?: InputMaybe<CustomerFiltersInput>;
|
||||||
not?: InputMaybe<BlockFiltersInput>;
|
not?: InputMaybe<BlockFiltersInput>;
|
||||||
or?: InputMaybe<Array<InputMaybe<BlockFiltersInput>>>;
|
or?: InputMaybe<Array<InputMaybe<BlockFiltersInput>>>;
|
||||||
orders?: InputMaybe<OrderFiltersInput>;
|
orders?: InputMaybe<OrderFiltersInput>;
|
||||||
publishedAt?: InputMaybe<DateTimeFilterInput>;
|
publishedAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
sessionsCompleted?: InputMaybe<IntFilterInput>;
|
sessions_completed?: InputMaybe<IntFilterInput>;
|
||||||
sessionsTotal?: InputMaybe<IntFilterInput>;
|
sessions_total?: InputMaybe<IntFilterInput>;
|
||||||
state?: InputMaybe<StringFilterInput>;
|
state?: InputMaybe<StringFilterInput>;
|
||||||
updatedAt?: InputMaybe<DateTimeFilterInput>;
|
updatedAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BlockInput = {
|
export type BlockInput = {
|
||||||
client?: InputMaybe<Scalars['ID']['input']>;
|
client?: InputMaybe<Scalars['ID']['input']>;
|
||||||
dateend?: InputMaybe<Scalars['DateTime']['input']>;
|
datetime_end?: InputMaybe<Scalars['DateTime']['input']>;
|
||||||
datestart?: InputMaybe<Scalars['DateTime']['input']>;
|
datetime_start?: InputMaybe<Scalars['DateTime']['input']>;
|
||||||
master?: InputMaybe<Scalars['ID']['input']>;
|
master?: InputMaybe<Scalars['ID']['input']>;
|
||||||
orders?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>;
|
orders?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>;
|
||||||
publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
||||||
sessionsCompleted?: InputMaybe<Scalars['Int']['input']>;
|
sessions_completed?: InputMaybe<Scalars['Int']['input']>;
|
||||||
sessionsTotal?: InputMaybe<Scalars['Int']['input']>;
|
sessions_total?: InputMaybe<Scalars['Int']['input']>;
|
||||||
state?: InputMaybe<Enum_Block_State>;
|
state?: InputMaybe<Enum_Block_State>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -89,7 +91,6 @@ export type CustomerFiltersInput = {
|
|||||||
photoUrl?: InputMaybe<StringFilterInput>;
|
photoUrl?: InputMaybe<StringFilterInput>;
|
||||||
publishedAt?: InputMaybe<DateTimeFilterInput>;
|
publishedAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
role?: InputMaybe<StringFilterInput>;
|
role?: InputMaybe<StringFilterInput>;
|
||||||
setting?: InputMaybe<SettingFiltersInput>;
|
|
||||||
slots?: InputMaybe<SlotFiltersInput>;
|
slots?: InputMaybe<SlotFiltersInput>;
|
||||||
telegramId?: InputMaybe<LongFilterInput>;
|
telegramId?: InputMaybe<LongFilterInput>;
|
||||||
updatedAt?: InputMaybe<DateTimeFilterInput>;
|
updatedAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
@ -106,11 +107,35 @@ export type CustomerInput = {
|
|||||||
photoUrl?: InputMaybe<Scalars['String']['input']>;
|
photoUrl?: InputMaybe<Scalars['String']['input']>;
|
||||||
publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
||||||
role?: InputMaybe<Enum_Customer_Role>;
|
role?: InputMaybe<Enum_Customer_Role>;
|
||||||
setting?: InputMaybe<Scalars['ID']['input']>;
|
|
||||||
slots?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>;
|
slots?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>;
|
||||||
telegramId?: InputMaybe<Scalars['Long']['input']>;
|
telegramId?: InputMaybe<Scalars['Long']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DateFilterInput = {
|
||||||
|
and?: InputMaybe<Array<InputMaybe<Scalars['Date']['input']>>>;
|
||||||
|
between?: InputMaybe<Array<InputMaybe<Scalars['Date']['input']>>>;
|
||||||
|
contains?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
containsi?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
endsWith?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
eq?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
eqi?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
gt?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
gte?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
in?: InputMaybe<Array<InputMaybe<Scalars['Date']['input']>>>;
|
||||||
|
lt?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
lte?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
ne?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
nei?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
not?: InputMaybe<DateFilterInput>;
|
||||||
|
notContains?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
notContainsi?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
notIn?: InputMaybe<Array<InputMaybe<Scalars['Date']['input']>>>;
|
||||||
|
notNull?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
|
null?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
|
or?: InputMaybe<Array<InputMaybe<Scalars['Date']['input']>>>;
|
||||||
|
startsWith?: InputMaybe<Scalars['Date']['input']>;
|
||||||
|
};
|
||||||
|
|
||||||
export type DateTimeFilterInput = {
|
export type DateTimeFilterInput = {
|
||||||
and?: InputMaybe<Array<InputMaybe<Scalars['DateTime']['input']>>>;
|
and?: InputMaybe<Array<InputMaybe<Scalars['DateTime']['input']>>>;
|
||||||
between?: InputMaybe<Array<InputMaybe<Scalars['DateTime']['input']>>>;
|
between?: InputMaybe<Array<InputMaybe<Scalars['DateTime']['input']>>>;
|
||||||
@ -312,22 +337,30 @@ export type OrderFiltersInput = {
|
|||||||
documentId?: InputMaybe<IdFilterInput>;
|
documentId?: InputMaybe<IdFilterInput>;
|
||||||
not?: InputMaybe<OrderFiltersInput>;
|
not?: InputMaybe<OrderFiltersInput>;
|
||||||
or?: InputMaybe<Array<InputMaybe<OrderFiltersInput>>>;
|
or?: InputMaybe<Array<InputMaybe<OrderFiltersInput>>>;
|
||||||
|
order_number?: InputMaybe<IntFilterInput>;
|
||||||
price?: InputMaybe<IntFilterInput>;
|
price?: InputMaybe<IntFilterInput>;
|
||||||
publishedAt?: InputMaybe<DateTimeFilterInput>;
|
publishedAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
service?: InputMaybe<StringFilterInput>;
|
service_description?: InputMaybe<StringFilterInput>;
|
||||||
|
services?: InputMaybe<ServiceFiltersInput>;
|
||||||
slot?: InputMaybe<SlotFiltersInput>;
|
slot?: InputMaybe<SlotFiltersInput>;
|
||||||
state?: InputMaybe<StringFilterInput>;
|
state?: InputMaybe<StringFilterInput>;
|
||||||
|
time_end?: InputMaybe<TimeFilterInput>;
|
||||||
|
time_start?: InputMaybe<TimeFilterInput>;
|
||||||
updatedAt?: InputMaybe<DateTimeFilterInput>;
|
updatedAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OrderInput = {
|
export type OrderInput = {
|
||||||
block?: InputMaybe<Scalars['ID']['input']>;
|
block?: InputMaybe<Scalars['ID']['input']>;
|
||||||
client?: InputMaybe<Scalars['ID']['input']>;
|
client?: InputMaybe<Scalars['ID']['input']>;
|
||||||
|
order_number?: InputMaybe<Scalars['Int']['input']>;
|
||||||
price?: InputMaybe<Scalars['Int']['input']>;
|
price?: InputMaybe<Scalars['Int']['input']>;
|
||||||
publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
||||||
service?: InputMaybe<Scalars['String']['input']>;
|
service_description?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
services?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>;
|
||||||
slot?: InputMaybe<Scalars['ID']['input']>;
|
slot?: InputMaybe<Scalars['ID']['input']>;
|
||||||
state?: InputMaybe<Enum_Order_State>;
|
state?: InputMaybe<Enum_Order_State>;
|
||||||
|
time_end?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
time_start?: InputMaybe<Scalars['Time']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PaginationArg = {
|
export type PaginationArg = {
|
||||||
@ -384,29 +417,44 @@ export type ReviewWorkflowsWorkflowStageInput = {
|
|||||||
workflow?: InputMaybe<Scalars['ID']['input']>;
|
workflow?: InputMaybe<Scalars['ID']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ServiceFiltersInput = {
|
||||||
|
and?: InputMaybe<Array<InputMaybe<ServiceFiltersInput>>>;
|
||||||
|
createdAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
|
documentId?: InputMaybe<IdFilterInput>;
|
||||||
|
name?: InputMaybe<StringFilterInput>;
|
||||||
|
not?: InputMaybe<ServiceFiltersInput>;
|
||||||
|
or?: InputMaybe<Array<InputMaybe<ServiceFiltersInput>>>;
|
||||||
|
orders?: InputMaybe<OrderFiltersInput>;
|
||||||
|
publishedAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
|
updatedAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ServiceInput = {
|
||||||
|
name?: InputMaybe<Scalars['String']['input']>;
|
||||||
|
orders?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>;
|
||||||
|
publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
||||||
|
};
|
||||||
|
|
||||||
export type SettingFiltersInput = {
|
export type SettingFiltersInput = {
|
||||||
and?: InputMaybe<Array<InputMaybe<SettingFiltersInput>>>;
|
and?: InputMaybe<Array<InputMaybe<SettingFiltersInput>>>;
|
||||||
createdAt?: InputMaybe<DateTimeFilterInput>;
|
createdAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
customer?: InputMaybe<CustomerFiltersInput>;
|
|
||||||
documentId?: InputMaybe<IdFilterInput>;
|
documentId?: InputMaybe<IdFilterInput>;
|
||||||
not?: InputMaybe<SettingFiltersInput>;
|
not?: InputMaybe<SettingFiltersInput>;
|
||||||
or?: InputMaybe<Array<InputMaybe<SettingFiltersInput>>>;
|
or?: InputMaybe<Array<InputMaybe<SettingFiltersInput>>>;
|
||||||
publishedAt?: InputMaybe<DateTimeFilterInput>;
|
publishedAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
recordingByBlocks?: InputMaybe<BooleanFilterInput>;
|
recording_by_blocks?: InputMaybe<BooleanFilterInput>;
|
||||||
updatedAt?: InputMaybe<DateTimeFilterInput>;
|
updatedAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SettingInput = {
|
export type SettingInput = {
|
||||||
customer?: InputMaybe<Scalars['ID']['input']>;
|
|
||||||
publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
||||||
recordingByBlocks?: InputMaybe<Scalars['Boolean']['input']>;
|
recording_by_blocks?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SlotFiltersInput = {
|
export type SlotFiltersInput = {
|
||||||
and?: InputMaybe<Array<InputMaybe<SlotFiltersInput>>>;
|
and?: InputMaybe<Array<InputMaybe<SlotFiltersInput>>>;
|
||||||
createdAt?: InputMaybe<DateTimeFilterInput>;
|
createdAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
dateend?: InputMaybe<DateTimeFilterInput>;
|
date?: InputMaybe<DateFilterInput>;
|
||||||
datestart?: InputMaybe<DateTimeFilterInput>;
|
|
||||||
documentId?: InputMaybe<IdFilterInput>;
|
documentId?: InputMaybe<IdFilterInput>;
|
||||||
master?: InputMaybe<CustomerFiltersInput>;
|
master?: InputMaybe<CustomerFiltersInput>;
|
||||||
not?: InputMaybe<SlotFiltersInput>;
|
not?: InputMaybe<SlotFiltersInput>;
|
||||||
@ -414,16 +462,19 @@ export type SlotFiltersInput = {
|
|||||||
orders?: InputMaybe<OrderFiltersInput>;
|
orders?: InputMaybe<OrderFiltersInput>;
|
||||||
publishedAt?: InputMaybe<DateTimeFilterInput>;
|
publishedAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
state?: InputMaybe<StringFilterInput>;
|
state?: InputMaybe<StringFilterInput>;
|
||||||
|
time_end?: InputMaybe<TimeFilterInput>;
|
||||||
|
time_start?: InputMaybe<TimeFilterInput>;
|
||||||
updatedAt?: InputMaybe<DateTimeFilterInput>;
|
updatedAt?: InputMaybe<DateTimeFilterInput>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SlotInput = {
|
export type SlotInput = {
|
||||||
dateend?: InputMaybe<Scalars['DateTime']['input']>;
|
date?: InputMaybe<Scalars['Date']['input']>;
|
||||||
datestart?: InputMaybe<Scalars['DateTime']['input']>;
|
|
||||||
master?: InputMaybe<Scalars['ID']['input']>;
|
master?: InputMaybe<Scalars['ID']['input']>;
|
||||||
orders?: InputMaybe<Scalars['ID']['input']>;
|
orders?: InputMaybe<Array<InputMaybe<Scalars['ID']['input']>>>;
|
||||||
publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
publishedAt?: InputMaybe<Scalars['DateTime']['input']>;
|
||||||
state?: InputMaybe<Enum_Slot_State>;
|
state?: InputMaybe<Enum_Slot_State>;
|
||||||
|
time_end?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
time_start?: InputMaybe<Scalars['Time']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type StringFilterInput = {
|
export type StringFilterInput = {
|
||||||
@ -451,6 +502,31 @@ export type StringFilterInput = {
|
|||||||
startsWith?: InputMaybe<Scalars['String']['input']>;
|
startsWith?: InputMaybe<Scalars['String']['input']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TimeFilterInput = {
|
||||||
|
and?: InputMaybe<Array<InputMaybe<Scalars['Time']['input']>>>;
|
||||||
|
between?: InputMaybe<Array<InputMaybe<Scalars['Time']['input']>>>;
|
||||||
|
contains?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
containsi?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
endsWith?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
eq?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
eqi?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
gt?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
gte?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
in?: InputMaybe<Array<InputMaybe<Scalars['Time']['input']>>>;
|
||||||
|
lt?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
lte?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
ne?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
nei?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
not?: InputMaybe<TimeFilterInput>;
|
||||||
|
notContains?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
notContainsi?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
notIn?: InputMaybe<Array<InputMaybe<Scalars['Time']['input']>>>;
|
||||||
|
notNull?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
|
null?: InputMaybe<Scalars['Boolean']['input']>;
|
||||||
|
or?: InputMaybe<Array<InputMaybe<Scalars['Time']['input']>>>;
|
||||||
|
startsWith?: InputMaybe<Scalars['Time']['input']>;
|
||||||
|
};
|
||||||
|
|
||||||
export type UploadFileFiltersInput = {
|
export type UploadFileFiltersInput = {
|
||||||
alternativeText?: InputMaybe<StringFilterInput>;
|
alternativeText?: InputMaybe<StringFilterInput>;
|
||||||
and?: InputMaybe<Array<InputMaybe<UploadFileFiltersInput>>>;
|
and?: InputMaybe<Array<InputMaybe<UploadFileFiltersInput>>>;
|
||||||
@ -610,7 +686,56 @@ export type UpdateCustomerProfileMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type UpdateCustomerProfileMutation = { __typename?: 'Mutation', updateCustomer?: { __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: any | null | undefined } | null | undefined };
|
export type UpdateCustomerProfileMutation = { __typename?: 'Mutation', updateCustomer?: { __typename?: 'Customer', active?: boolean | null | undefined, documentId: string, name: string, phone: string, photoUrl?: string | null | undefined, role: Enum_Customer_Role, telegramId?: any | null | undefined } | null | undefined };
|
||||||
|
|
||||||
|
export type OrderFieldsFragment = { __typename?: 'Order', documentId: string, time_start?: any | null | undefined, time_end?: any | null | undefined, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name?: string | null | undefined } | null | undefined>, client?: { __typename?: 'Customer', name: string, documentId: string, photoUrl?: string | null | undefined } | null | undefined };
|
||||||
|
|
||||||
|
export type GetOrderQueryVariables = Exact<{
|
||||||
|
documentId: Scalars['ID']['input'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetOrderQuery = { __typename?: 'Query', order?: { __typename?: 'Order', documentId: string, time_start?: any | null | undefined, time_end?: any | null | undefined, state?: Enum_Order_State | null | undefined, order_number?: number | null | undefined, services: Array<{ __typename?: 'Service', documentId: string, name?: string | null | undefined } | null | undefined>, client?: { __typename?: 'Customer', name: string, documentId: string, photoUrl?: string | null | undefined } | null | undefined } | null | undefined };
|
||||||
|
|
||||||
|
export type SlotFieldsFragment = { __typename?: 'Slot', documentId: string, date?: any | null | undefined, time_start: any, time_end: any, state?: Enum_Slot_State | null | undefined };
|
||||||
|
|
||||||
|
export type CreateSlotMutationVariables = Exact<{
|
||||||
|
input: SlotInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type CreateSlotMutation = { __typename?: 'Mutation', createSlot?: { __typename?: 'Slot', documentId: string, date?: any | null | undefined, time_start: any, time_end: any, state?: Enum_Slot_State | null | undefined } | null | undefined };
|
||||||
|
|
||||||
|
export type GetSlotsQueryVariables = Exact<{
|
||||||
|
filters?: InputMaybe<SlotFiltersInput>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetSlotsQuery = { __typename?: 'Query', slots: Array<{ __typename?: 'Slot', documentId: string } | null | undefined> };
|
||||||
|
|
||||||
|
export type GetSlotQueryVariables = Exact<{
|
||||||
|
documentId: Scalars['ID']['input'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type GetSlotQuery = { __typename?: 'Query', slot?: { __typename?: 'Slot', state?: Enum_Slot_State | null | undefined, documentId: string, date?: any | null | undefined, time_start: any, time_end: any, orders: Array<{ __typename?: 'Order', documentId: string } | null | undefined> } | null | undefined };
|
||||||
|
|
||||||
|
export type UpdateSlotMutationVariables = Exact<{
|
||||||
|
documentId: Scalars['ID']['input'];
|
||||||
|
data: SlotInput;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type UpdateSlotMutation = { __typename?: 'Mutation', updateSlot?: { __typename?: 'Slot', documentId: string, date?: any | null | undefined, time_start: any, time_end: any, state?: Enum_Slot_State | null | undefined } | null | undefined };
|
||||||
|
|
||||||
|
export type DeleteSlotMutationVariables = Exact<{
|
||||||
|
documentId: Scalars['ID']['input'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
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 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 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":"time_start"}},{"kind":"Field","name":{"kind":"Name","value":"time_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"order_number"}},{"kind":"Field","name":{"kind":"Name","value":"services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}}]}}]}}]} as unknown as DocumentNode<OrderFieldsFragment, 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":"date"}},{"kind":"Field","name":{"kind":"Name","value":"time_start"}},{"kind":"Field","name":{"kind":"Name","value":"time_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}}]} as unknown as DocumentNode<SlotFieldsFragment, 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 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 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 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>;
|
||||||
@ -618,3 +743,9 @@ export const GetCustomerDocument = {"kind":"Document","definitions":[{"kind":"Op
|
|||||||
export const GetCustomerMastersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomerMasters"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"and"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"active"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"BooleanValue","value":true}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"masters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode<GetCustomerMastersQuery, GetCustomerMastersQueryVariables>;
|
export const GetCustomerMastersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomerMasters"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"and"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"active"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"BooleanValue","value":true}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"masters"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode<GetCustomerMastersQuery, GetCustomerMastersQueryVariables>;
|
||||||
export const GetCustomerClientsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomerClients"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"and"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"active"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"BooleanValue","value":true}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"clients"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode<GetCustomerClientsQuery, GetCustomerClientsQueryVariables>;
|
export const GetCustomerClientsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCustomerClients"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"phone"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Long"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filters"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"or"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"phone"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"phone"}}}]}}]},{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"telegramId"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"telegramId"}}}]}}]}]}},{"kind":"ObjectField","name":{"kind":"Name","value":"and"},"value":{"kind":"ListValue","values":[{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"active"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"BooleanValue","value":true}}]}}]}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"clients"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode<GetCustomerClientsQuery, GetCustomerClientsQueryVariables>;
|
||||||
export const UpdateCustomerProfileDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateCustomerProfile"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CustomerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode<UpdateCustomerProfileMutation, UpdateCustomerProfileMutationVariables>;
|
export const UpdateCustomerProfileDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateCustomerProfile"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CustomerInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}},{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"CustomerFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"CustomerFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Customer"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"active"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"telegramId"}}]}}]} as unknown as DocumentNode<UpdateCustomerProfileMutation, UpdateCustomerProfileMutationVariables>;
|
||||||
|
export const GetOrderDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOrder"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"order"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"documentId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"documentId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"OrderFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"OrderFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Order"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"time_start"}},{"kind":"Field","name":{"kind":"Name","value":"time_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}},{"kind":"Field","name":{"kind":"Name","value":"order_number"}},{"kind":"Field","name":{"kind":"Name","value":"services"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"client"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"documentId"}},{"kind":"Field","name":{"kind":"Name","value":"photoUrl"}}]}}]}}]} as unknown as DocumentNode<GetOrderQuery, GetOrderQueryVariables>;
|
||||||
|
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":"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":"date"}},{"kind":"Field","name":{"kind":"Name","value":"time_start"}},{"kind":"Field","name":{"kind":"Name","value":"time_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}}]} 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":"time_start:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}}]}}]}}]} as unknown as DocumentNode<GetSlotsQuery, GetSlotsQueryVariables>;
|
||||||
|
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":"state"}},{"kind":"Field","name":{"kind":"Name","value":"orders"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"sort"},"value":{"kind":"StringValue","value":"time_start:asc","block":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"documentId"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SlotFields"}}]}}]}},{"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":"date"}},{"kind":"Field","name":{"kind":"Name","value":"time_start"}},{"kind":"Field","name":{"kind":"Name","value":"time_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}}]} 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":"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":"date"}},{"kind":"Field","name":{"kind":"Name","value":"time_start"}},{"kind":"Field","name":{"kind":"Name","value":"time_end"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}}]} 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>;
|
||||||
@ -26,7 +26,7 @@
|
|||||||
"type-check": "tsc --noEmit"
|
"type-check": "tsc --noEmit"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.1",
|
||||||
"@repo/eslint-config": "workspace:*",
|
"@repo/eslint-config": "workspace:*",
|
||||||
"@repo/lint-staged-config": "workspace:*",
|
"@repo/lint-staged-config": "workspace:*",
|
||||||
"@repo/typescript-config": "workspace:*",
|
"@repo/typescript-config": "workspace:*",
|
||||||
@ -50,7 +50,11 @@
|
|||||||
"@radix-ui/react-label": "^2.1.1",
|
"@radix-ui/react-label": "^2.1.1",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.2",
|
"@radix-ui/react-scroll-area": "^1.2.2",
|
||||||
"@radix-ui/react-select": "^2.1.4",
|
"@radix-ui/react-select": "^2.1.4",
|
||||||
|
"date-fns": "^4.1.0",
|
||||||
|
"next-themes": "^0.4.4",
|
||||||
"react": "catalog:",
|
"react": "catalog:",
|
||||||
"react-dom": "catalog:"
|
"react-day-picker": "8.10.1",
|
||||||
|
"react-dom": "catalog:",
|
||||||
|
"sonner": "^1.7.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
30
packages/ui/src/components/ui/badge.tsx
Normal file
30
packages/ui/src/components/ui/badge.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { cn } from '@repo/ui/lib/utils';
|
||||||
|
import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
const badgeVariants = cva(
|
||||||
|
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
||||||
|
{
|
||||||
|
defaultVariants: {
|
||||||
|
variant: 'default',
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
||||||
|
destructive:
|
||||||
|
'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
||||||
|
outline: 'text-foreground',
|
||||||
|
secondary:
|
||||||
|
'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export type BadgeProps = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof badgeVariants>;
|
||||||
|
|
||||||
|
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||||
|
return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Badge, badgeVariants };
|
||||||
75
packages/ui/src/components/ui/calendar.tsx
Normal file
75
packages/ui/src/components/ui/calendar.tsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/* eslint-disable react/prop-types */
|
||||||
|
/* eslint-disable @typescript-eslint/no-shadow */
|
||||||
|
/* eslint-disable react/no-unstable-nested-components */
|
||||||
|
'use client';
|
||||||
|
import { buttonVariants } from '@repo/ui/components/ui/button';
|
||||||
|
import { cn } from '@repo/ui/lib/utils';
|
||||||
|
import { ru } from 'date-fns/locale';
|
||||||
|
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { DayPicker } from 'react-day-picker';
|
||||||
|
|
||||||
|
export type CalendarProps = React.ComponentProps<typeof DayPicker> & {
|
||||||
|
readonly localeName?: 'ru';
|
||||||
|
};
|
||||||
|
|
||||||
|
function Calendar({
|
||||||
|
className,
|
||||||
|
classNames,
|
||||||
|
localeName = 'ru',
|
||||||
|
showOutsideDays = true,
|
||||||
|
...props
|
||||||
|
}: CalendarProps) {
|
||||||
|
return (
|
||||||
|
<DayPicker
|
||||||
|
className={cn('p-3', className)}
|
||||||
|
classNames={{
|
||||||
|
caption: 'flex justify-center pt-1 relative items-center',
|
||||||
|
caption_label: 'text-sm font-medium',
|
||||||
|
cell: 'h-9 w-full text-center text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20',
|
||||||
|
day: cn(
|
||||||
|
buttonVariants({ variant: 'ghost' }),
|
||||||
|
'h-9 w-full p-0 font-normal aria-selected:opacity-100',
|
||||||
|
),
|
||||||
|
day_disabled: 'text-muted-foreground opacity-50',
|
||||||
|
day_hidden: 'invisible',
|
||||||
|
day_outside:
|
||||||
|
'day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground',
|
||||||
|
day_range_end: 'day-range-end',
|
||||||
|
day_range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',
|
||||||
|
day_selected:
|
||||||
|
'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground',
|
||||||
|
day_today: 'bg-accent text-accent-foreground',
|
||||||
|
head_cell: 'text-muted-foreground rounded-md w-full font-normal text-[0.8rem]',
|
||||||
|
head_row: 'flex',
|
||||||
|
month: 'space-y-4',
|
||||||
|
months: 'flex flex-col flex-row space-y-4 space-x-4 space-y-0',
|
||||||
|
nav: 'space-x-1 flex items-center',
|
||||||
|
nav_button: cn(
|
||||||
|
buttonVariants({ variant: 'outline' }),
|
||||||
|
'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
|
||||||
|
),
|
||||||
|
nav_button_next: 'absolute right-1',
|
||||||
|
nav_button_previous: 'absolute left-1',
|
||||||
|
row: 'flex w-full mt-2',
|
||||||
|
table: 'w-full border-collapse space-y-1',
|
||||||
|
...classNames,
|
||||||
|
}}
|
||||||
|
components={{
|
||||||
|
IconLeft: ({ className, ...props }) => (
|
||||||
|
<ChevronLeft className={cn('h-4 w-4', className)} {...props} />
|
||||||
|
),
|
||||||
|
IconRight: ({ className, ...props }) => (
|
||||||
|
<ChevronRight className={cn('h-4 w-4', className)} {...props} />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
showOutsideDays={showOutsideDays}
|
||||||
|
{...props}
|
||||||
|
locale={localeName === 'ru' ? ru : undefined}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Calendar.displayName = 'Calendar';
|
||||||
|
|
||||||
|
export { Calendar };
|
||||||
31
packages/ui/src/components/ui/sonner.tsx
Normal file
31
packages/ui/src/components/ui/sonner.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useTheme } from 'next-themes';
|
||||||
|
import { Toaster as Sonner } from 'sonner';
|
||||||
|
|
||||||
|
type ToasterProps = React.ComponentProps<typeof Sonner>;
|
||||||
|
|
||||||
|
function Toaster({ ...props }: ToasterProps) {
|
||||||
|
const { theme = 'system' } = useTheme();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Sonner
|
||||||
|
className="toaster group"
|
||||||
|
theme={theme as ToasterProps['theme']}
|
||||||
|
toastOptions={{
|
||||||
|
classNames: {
|
||||||
|
actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
|
||||||
|
cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',
|
||||||
|
description: 'group-[.toast]:text-muted-foreground',
|
||||||
|
toast:
|
||||||
|
'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Toaster };
|
||||||
|
|
||||||
|
export { toast } from 'sonner';
|
||||||
52
pnpm-lock.yaml
generated
52
pnpm-lock.yaml
generated
@ -147,6 +147,9 @@ importers:
|
|||||||
'@telegram-apps/sdk-react':
|
'@telegram-apps/sdk-react':
|
||||||
specifier: ^2.0.19
|
specifier: ^2.0.19
|
||||||
version: 2.0.19(@types/react@19.0.1)(react@19.0.0)
|
version: 2.0.19(@types/react@19.0.1)(react@19.0.0)
|
||||||
|
dayjs:
|
||||||
|
specifier: ^1.11.13
|
||||||
|
version: 1.11.13
|
||||||
graphql:
|
graphql:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 16.9.0
|
version: 16.9.0
|
||||||
@ -324,15 +327,27 @@ importers:
|
|||||||
'@radix-ui/react-select':
|
'@radix-ui/react-select':
|
||||||
specifier: ^2.1.4
|
specifier: ^2.1.4
|
||||||
version: 2.1.4(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
version: 2.1.4(@types/react-dom@19.0.1)(@types/react@19.0.1)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
date-fns:
|
||||||
|
specifier: ^4.1.0
|
||||||
|
version: 4.1.0
|
||||||
|
next-themes:
|
||||||
|
specifier: ^0.4.4
|
||||||
|
version: 0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
react:
|
react:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 19.0.0
|
version: 19.0.0
|
||||||
|
react-day-picker:
|
||||||
|
specifier: 8.10.1
|
||||||
|
version: 8.10.1(date-fns@4.1.0)(react@19.0.0)
|
||||||
react-dom:
|
react-dom:
|
||||||
specifier: 'catalog:'
|
specifier: 'catalog:'
|
||||||
version: 19.0.0(react@19.0.0)
|
version: 19.0.0(react@19.0.0)
|
||||||
|
sonner:
|
||||||
|
specifier: ^1.7.4
|
||||||
|
version: 1.7.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@radix-ui/react-slot':
|
'@radix-ui/react-slot':
|
||||||
specifier: ^1.1.0
|
specifier: ^1.1.1
|
||||||
version: 1.1.1(@types/react@19.0.1)(react@19.0.0)
|
version: 1.1.1(@types/react@19.0.1)(react@19.0.0)
|
||||||
'@repo/eslint-config':
|
'@repo/eslint-config':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
@ -3278,6 +3293,12 @@ packages:
|
|||||||
dataloader@2.2.3:
|
dataloader@2.2.3:
|
||||||
resolution: {integrity: sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==}
|
resolution: {integrity: sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==}
|
||||||
|
|
||||||
|
date-fns@4.1.0:
|
||||||
|
resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==}
|
||||||
|
|
||||||
|
dayjs@1.11.13:
|
||||||
|
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
|
||||||
|
|
||||||
debounce@1.2.1:
|
debounce@1.2.1:
|
||||||
resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
|
resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
|
||||||
|
|
||||||
@ -4600,6 +4621,7 @@ packages:
|
|||||||
|
|
||||||
lodash.get@4.4.2:
|
lodash.get@4.4.2:
|
||||||
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
|
resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
|
||||||
|
deprecated: This package is deprecated. Use the optional chaining (?.) operator instead.
|
||||||
|
|
||||||
lodash.includes@4.3.0:
|
lodash.includes@4.3.0:
|
||||||
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
|
resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==}
|
||||||
@ -5274,6 +5296,12 @@ packages:
|
|||||||
ramda@0.30.1:
|
ramda@0.30.1:
|
||||||
resolution: {integrity: sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw==}
|
resolution: {integrity: sha512-tEF5I22zJnuclswcZMc8bDIrwRHRzf+NqVEmqg50ShAZMP7MWeR/RGDthfM/p+BlqvF2fXAzpn8i+SJcYD3alw==}
|
||||||
|
|
||||||
|
react-day-picker@8.10.1:
|
||||||
|
resolution: {integrity: sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==}
|
||||||
|
peerDependencies:
|
||||||
|
date-fns: ^2.28.0 || ^3.0.0
|
||||||
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
|
||||||
react-dom@19.0.0:
|
react-dom@19.0.0:
|
||||||
resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==}
|
resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -5639,6 +5667,12 @@ packages:
|
|||||||
snake-case@3.0.4:
|
snake-case@3.0.4:
|
||||||
resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
|
resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
|
||||||
|
|
||||||
|
sonner@1.7.4:
|
||||||
|
resolution: {integrity: sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==}
|
||||||
|
peerDependencies:
|
||||||
|
react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
|
||||||
source-map-js@1.2.1:
|
source-map-js@1.2.1:
|
||||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -9779,6 +9813,10 @@ snapshots:
|
|||||||
|
|
||||||
dataloader@2.2.3: {}
|
dataloader@2.2.3: {}
|
||||||
|
|
||||||
|
date-fns@4.1.0: {}
|
||||||
|
|
||||||
|
dayjs@1.11.13: {}
|
||||||
|
|
||||||
debounce@1.2.1: {}
|
debounce@1.2.1: {}
|
||||||
|
|
||||||
debug@3.2.7:
|
debug@3.2.7:
|
||||||
@ -11678,7 +11716,7 @@ snapshots:
|
|||||||
normalize-package-data@2.5.0:
|
normalize-package-data@2.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
hosted-git-info: 2.8.9
|
hosted-git-info: 2.8.9
|
||||||
resolve: 1.22.8
|
resolve: 1.22.10
|
||||||
semver: 5.7.2
|
semver: 5.7.2
|
||||||
validate-npm-package-license: 3.0.4
|
validate-npm-package-license: 3.0.4
|
||||||
|
|
||||||
@ -12041,6 +12079,11 @@ snapshots:
|
|||||||
|
|
||||||
ramda@0.30.1: {}
|
ramda@0.30.1: {}
|
||||||
|
|
||||||
|
react-day-picker@8.10.1(date-fns@4.1.0)(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
date-fns: 4.1.0
|
||||||
|
react: 19.0.0
|
||||||
|
|
||||||
react-dom@19.0.0(react@19.0.0):
|
react-dom@19.0.0(react@19.0.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
react: 19.0.0
|
react: 19.0.0
|
||||||
@ -12452,6 +12495,11 @@ snapshots:
|
|||||||
dot-case: 3.0.4
|
dot-case: 3.0.4
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
|
sonner@1.7.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||||
|
dependencies:
|
||||||
|
react: 19.0.0
|
||||||
|
react-dom: 19.0.0(react@19.0.0)
|
||||||
|
|
||||||
source-map-js@1.2.1: {}
|
source-map-js@1.2.1: {}
|
||||||
|
|
||||||
source-map@0.6.1: {}
|
source-map@0.6.1: {}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user