diff --git a/apps/web/components/orders/order-form/datetime-select.tsx b/apps/web/components/orders/order-form/datetime-select.tsx
index 8e2befb..7411206 100644
--- a/apps/web/components/orders/order-form/datetime-select.tsx
+++ b/apps/web/components/orders/order-form/datetime-select.tsx
@@ -4,6 +4,7 @@ import { useAvailableTimeSlotsQuery } from '@/hooks/api/slots';
import { useOrderStore } from '@/stores/order';
import { Button } from '@repo/ui/components/ui/button';
import { Calendar } from '@repo/ui/components/ui/calendar';
+import { formatDate } from '@repo/utils/datetime-format';
import dayjs from 'dayjs';
export function DateSelect() {
@@ -47,7 +48,7 @@ export function TimeSelect() {
{
filters: {
date: {
- eq: date,
+ eq: formatDate(date).db(),
},
master: {
documentId: {
diff --git a/apps/web/components/orders/order-form/submit-button.tsx b/apps/web/components/orders/order-form/submit-button.tsx
index 464fafb..94b399b 100644
--- a/apps/web/components/orders/order-form/submit-button.tsx
+++ b/apps/web/components/orders/order-form/submit-button.tsx
@@ -4,6 +4,7 @@ import { useOrderCreate } from '@/hooks/api/orders';
import { useOrderStore } from '@/stores/order';
import { Button } from '@repo/ui/components/ui/button';
import { LoadingSpinner } from '@repo/ui/components/ui/spinner';
+import { formatDate, formatTime } from '@repo/utils/datetime-format';
import { useEffect } from 'react';
export function SubmitButton() {
@@ -18,10 +19,10 @@ export function SubmitButton() {
createOrder({
input: {
client: clientId,
- date,
+ date: formatDate(date).db(),
services: [serviceId],
slot: slotId,
- time_start: time,
+ time_start: formatTime(time).db(),
},
});
};
diff --git a/apps/web/components/orders/orders-list/index.tsx b/apps/web/components/orders/orders-list/index.tsx
index 27c66e6..305c861 100644
--- a/apps/web/components/orders/orders-list/index.tsx
+++ b/apps/web/components/orders/orders-list/index.tsx
@@ -6,6 +6,7 @@ import { useCustomerQuery } from '@/hooks/api/customers';
import { useOrdersQuery } from '@/hooks/api/orders';
import { useDateTimeStore } from '@/stores/datetime';
import { Enum_Customer_Role } from '@repo/graphql/types';
+import { formatDate } from '@repo/utils/datetime-format';
export function ClientsOrdersList() {
const { data: { customer } = {} } = useCustomerQuery();
@@ -19,7 +20,7 @@ export function ClientsOrdersList() {
filters: {
slot: {
date: {
- eq: selectedDate,
+ eq: formatDate(selectedDate).db(),
},
master: {
documentId: {
@@ -57,7 +58,7 @@ export function OrdersList() {
},
slot: {
date: {
- eq: selectedDate,
+ eq: formatDate(selectedDate).db(),
},
},
},
diff --git a/apps/web/components/schedule/day-slots-list/day-slot-add-form.tsx b/apps/web/components/schedule/day-slots-list/day-slot-add-form.tsx
index cb74176..62c0161 100644
--- a/apps/web/components/schedule/day-slots-list/day-slot-add-form.tsx
+++ b/apps/web/components/schedule/day-slots-list/day-slot-add-form.tsx
@@ -8,6 +8,7 @@ import { ScheduleStoreProvider, useScheduleStore } from '@/stores/schedule';
import { withContext } from '@/utils/context';
import { Enum_Slot_State } from '@repo/graphql/types';
import { Button } from '@repo/ui/components/ui/button';
+import { formatDate, formatTime } from '@repo/utils/datetime-format';
import { PlusSquare } from 'lucide-react';
import { type FormEvent } from 'react';
@@ -24,10 +25,10 @@ export const DaySlotAddForm = withContext(ScheduleStoreProvider)(function () {
if (startTime && endTime) {
addSlot({
input: {
- date: selectedDate,
+ date: formatDate(selectedDate).db(),
state: Enum_Slot_State.Open,
- time_end: endTime,
- time_start: startTime,
+ time_end: formatTime(endTime).db(),
+ time_start: formatTime(startTime).db(),
},
});
diff --git a/apps/web/components/schedule/day-slots-list/index.tsx b/apps/web/components/schedule/day-slots-list/index.tsx
index 9e24619..1485cc8 100644
--- a/apps/web/components/schedule/day-slots-list/index.tsx
+++ b/apps/web/components/schedule/day-slots-list/index.tsx
@@ -5,12 +5,13 @@ import { SlotCard } from './slot-card';
import { useSlotsQuery } from '@/hooks/api/slots';
import { useDateTimeStore } from '@/stores/datetime';
import { LoadingSpinner } from '@repo/ui/components/ui/spinner';
+import { formatDate } from '@repo/utils/datetime-format';
export function DaySlotsList() {
const selectedDate = useDateTimeStore((store) => store.date);
const { data: { slots } = {}, isLoading } = useSlotsQuery({
- filters: { date: { eq: selectedDate } },
+ filters: { date: { eq: formatDate(selectedDate).db() } },
});
if (isLoading) return ;
diff --git a/apps/web/components/schedule/slot-datetime/slot-date.tsx b/apps/web/components/schedule/slot-datetime/slot-date.tsx
index d967f69..996e594 100644
--- a/apps/web/components/schedule/slot-datetime/slot-date.tsx
+++ b/apps/web/components/schedule/slot-datetime/slot-date.tsx
@@ -2,7 +2,7 @@
import { type SlotComponentProps } from '../types';
import { useSlotQuery } from '@/hooks/api/slots';
-import { formatDate } from '@repo/graphql/utils/datetime-format';
+import { formatDate } from '@repo/utils/datetime-format';
export function SlotDate({ documentId }: Readonly) {
const { data: { slot } = {} } = useSlotQuery({ documentId });
diff --git a/apps/web/components/schedule/slot-datetime/slot-time.tsx b/apps/web/components/schedule/slot-datetime/slot-time.tsx
index aac8b15..fb585c4 100644
--- a/apps/web/components/schedule/slot-datetime/slot-time.tsx
+++ b/apps/web/components/schedule/slot-datetime/slot-time.tsx
@@ -6,6 +6,7 @@ import { EditableTimeRangeForm, ReadonlyTimeRange } from '@/components/shared/ti
import { useSlotMutation, useSlotQuery } from '@/hooks/api/slots';
import { useScheduleStore } from '@/stores/schedule';
import { Button } from '@repo/ui/components/ui/button';
+import { formatTime } from '@repo/utils/datetime-format';
import { PencilLine } from 'lucide-react';
import { useEffect } from 'react';
@@ -32,7 +33,9 @@ function SlotTimeEditForm({ documentId }: Readonly) {
}, [editMode, setEndTime, setStartTime, slot]);
function handleSubmit() {
- updateSlot({ data: { time_end: endTime, time_start: startTime } });
+ updateSlot({
+ data: { time_end: formatTime(endTime).db(), time_start: formatTime(startTime).db() },
+ });
resetTime();
setEditMode(false);
}
diff --git a/apps/web/components/shared/time-range/readonly.tsx b/apps/web/components/shared/time-range/readonly.tsx
index be11032..4aecf6f 100644
--- a/apps/web/components/shared/time-range/readonly.tsx
+++ b/apps/web/components/shared/time-range/readonly.tsx
@@ -1,5 +1,5 @@
-import { formatTime } from '@repo/graphql/utils/datetime-format';
import { cn } from '@repo/ui/lib/utils';
+import { formatTime } from '@repo/utils/datetime-format';
type TimeRangeProps = {
readonly className?: string;
diff --git a/apps/web/package.json b/apps/web/package.json
index 86df62d..b008e96 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -15,6 +15,7 @@
},
"dependencies": {
"@repo/ui": "workspace:*",
+ "@repo/utils": "workspace:",
"@tanstack/react-query": "^5.64.1",
"@telegram-apps/sdk-react": "^2.0.19",
"dayjs": "catalog:",
diff --git a/packages/graphql/api/orders.ts b/packages/graphql/api/orders.ts
index a6a3a1e..f6fccbb 100644
--- a/packages/graphql/api/orders.ts
+++ b/packages/graphql/api/orders.ts
@@ -2,12 +2,12 @@
import { getClientWithToken } from '../apollo/client';
import * as GQL from '../types';
import { Enum_Customer_Role, Enum_Slot_State } from '../types';
-import { formatDate, formatTime, sumTime } from '../utils/datetime-format';
import { BaseService } from './base';
import { CustomersService } from './customers';
import { ServicesService } from './services';
import { SlotsService } from './slots';
import { type VariablesOf } from '@graphql-typed-document-node/core';
+import { formatTime, sumTime } from '@repo/utils/datetime-format';
const ERRORS = {
INVALID_CLIENT: 'Invalid client',
@@ -48,6 +48,7 @@ export class OrdersService extends BaseService {
}
const masters = await customersService.getMasters(this.customer);
+
const masterId = slot?.master?.documentId;
if (!masters.customers.some((master) => master?.documentId === masterId)) {
throw new Error(ERRORS.INVALID_MASTER);
@@ -68,17 +69,17 @@ export class OrdersService extends BaseService {
if (!service?.duration) throw new Error(ERRORS.INVALID_SERVICE_DURATION);
const endTime = sumTime(variables.input.time_start, service?.duration);
+
const { mutate } = await getClientWithToken();
+
const mutationResult = await mutate({
mutation: GQL.CreateOrderDocument,
variables: {
input: {
client: variables.input.client,
- date: formatDate(variables.input.date).db(),
services: variables.input.services,
slot: variables.input.slot,
time_end: formatTime(endTime).db(),
- time_start: formatTime(variables.input.time_start).db(),
},
},
});
@@ -105,16 +106,7 @@ export class OrdersService extends BaseService {
const result = await query({
query: GQL.GetOrdersDocument,
- variables: {
- filters: {
- ...variables.filters,
- date: { eq: formatDate(variables?.filters?.date?.eq).db() },
- slot: {
- ...variables.filters?.slot,
- date: { eq: formatDate(variables?.filters?.slot?.date?.eq).db() },
- },
- },
- },
+ variables,
});
return result.data;
diff --git a/packages/graphql/api/slots.ts b/packages/graphql/api/slots.ts
index f6a33f4..79369e0 100644
--- a/packages/graphql/api/slots.ts
+++ b/packages/graphql/api/slots.ts
@@ -1,10 +1,10 @@
import { getClientWithToken } from '../apollo/client';
import * as GQL from '../types';
-import { formatDate, formatTime, getMinutes } from '../utils/datetime-format';
import { BaseService } from './base';
import { CustomersService } from './customers';
import { ServicesService } from './services';
import { type VariablesOf } from '@graphql-typed-document-node/core';
+import { getMinutes } from '@repo/utils/datetime-format';
import dayjs from 'dayjs';
export class SlotsService extends BaseService {
@@ -21,10 +21,7 @@ export class SlotsService extends BaseService {
...variables,
input: {
...variables.input,
- date: formatDate(variables.input.date).db(),
master: customer?.documentId,
- time_end: variables.input.time_end && formatTime(variables.input.time_end).db(),
- time_start: variables.input.time_start && formatTime(variables.input.time_start).db(),
},
},
});
@@ -60,7 +57,6 @@ export class SlotsService extends BaseService {
variables: {
filters: {
...variables.filters,
- date: { eq: formatDate(variables?.filters?.date?.eq).db() },
state: { eq: GQL.Enum_Slot_State.Open },
},
},
@@ -127,12 +123,7 @@ export class SlotsService extends BaseService {
const result = await query({
query: GQL.GetSlotsDocument,
- variables: {
- filters: {
- ...variables.filters,
- date: { eq: formatDate(variables?.filters?.date?.eq).db() },
- },
- },
+ variables,
});
return result.data;
@@ -143,17 +134,7 @@ export class SlotsService extends BaseService {
const mutationResult = await mutate({
mutation: GQL.UpdateSlotDocument,
- variables: {
- ...variables,
- data: {
- ...variables.data,
- date: variables.data?.date ? formatDate(variables.data.date).db() : undefined,
- time_end: variables.data?.time_end ? formatTime(variables.data.time_end).db() : undefined,
- time_start: variables.data?.time_start
- ? formatTime(variables.data.time_start).db()
- : undefined,
- },
- },
+ variables,
});
const error = mutationResult.errors?.at(0);
diff --git a/packages/graphql/package.json b/packages/graphql/package.json
index 796dd06..c7708f9 100644
--- a/packages/graphql/package.json
+++ b/packages/graphql/package.json
@@ -9,6 +9,7 @@
},
"dependencies": {
"@apollo/client": "catalog:",
+ "@repo/utils": "workspace:",
"dayjs": "catalog:",
"jsonwebtoken": "catalog:",
"radashi": "catalog:",
diff --git a/packages/utils/eslint.config.js b/packages/utils/eslint.config.js
new file mode 100644
index 0000000..3fb0644
--- /dev/null
+++ b/packages/utils/eslint.config.js
@@ -0,0 +1,9 @@
+import { typescript } from '@repo/eslint-config/typescript';
+
+/** @type {import("eslint").Linter.Config} */
+export default [
+ ...typescript,
+ {
+ ignores: ['**/types/**'],
+ },
+];
diff --git a/packages/utils/package.json b/packages/utils/package.json
new file mode 100644
index 0000000..9c2a5b7
--- /dev/null
+++ b/packages/utils/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "@repo/utils",
+ "version": "0.0.0",
+ "license": "MIT",
+ "type": "module",
+ "scripts": {
+ "test:unit": "vitest"
+ },
+ "exports": {
+ "./datetime-format": "./src/datetime-format.ts"
+ },
+ "dependencies": {
+ "dayjs": "catalog:",
+ "radashi": "catalog:",
+ "zod": "catalog:"
+ },
+ "devDependencies": {
+ "@repo/eslint-config": "workspace:*",
+ "@repo/typescript-config": "workspace:*",
+ "vite-tsconfig-paths": "catalog:",
+ "vitest": "catalog:"
+ }
+}
diff --git a/packages/graphql/utils/datetime-format.ts b/packages/utils/src/datetime-format.ts
similarity index 100%
rename from packages/graphql/utils/datetime-format.ts
rename to packages/utils/src/datetime-format.ts
diff --git a/packages/utils/tsconfig.json b/packages/utils/tsconfig.json
new file mode 100644
index 0000000..c106372
--- /dev/null
+++ b/packages/utils/tsconfig.json
@@ -0,0 +1,13 @@
+{
+ "extends": "@repo/typescript-config/base.json",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./*"]
+ },
+ "moduleResolution": "bundler",
+ "module": "ES2022"
+ },
+ "include": ["."],
+ "exclude": ["dist", "build", "node_modules"]
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 71e50bf..14a8d1e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -147,6 +147,9 @@ importers:
'@repo/ui':
specifier: workspace:*
version: link:../../packages/ui
+ '@repo/utils':
+ specifier: 'workspace:'
+ version: link:../../packages/utils
'@tanstack/react-query':
specifier: ^5.64.1
version: 5.64.1(react@19.1.0)
@@ -274,6 +277,9 @@ importers:
'@apollo/client':
specifier: 'catalog:'
version: 3.12.4(@types/react@19.1.2)(graphql-ws@5.16.0(graphql@16.9.0))(graphql@16.9.0)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@repo/utils':
+ specifier: 'workspace:'
+ version: link:../utils
dayjs:
specifier: 'catalog:'
version: 1.11.13
@@ -413,6 +419,31 @@ importers:
specifier: 'catalog:'
version: 5.7.2
+ packages/utils:
+ dependencies:
+ dayjs:
+ specifier: 'catalog:'
+ version: 1.11.13
+ radashi:
+ specifier: 'catalog:'
+ version: 12.5.1
+ zod:
+ specifier: 'catalog:'
+ version: 3.24.1
+ devDependencies:
+ '@repo/eslint-config':
+ specifier: workspace:*
+ version: link:../eslint-config
+ '@repo/typescript-config':
+ specifier: workspace:*
+ version: link:../typescript-config
+ vite-tsconfig-paths:
+ specifier: 'catalog:'
+ version: 5.1.4(typescript@5.7.2)(vite@5.4.11(@types/node@20.17.8))
+ vitest:
+ specifier: 'catalog:'
+ version: 2.1.8(@types/node@20.17.8)(jsdom@25.0.1)(msw@2.7.0(@types/node@20.17.8)(typescript@5.7.2))
+
packages:
'@alloc/quick-lru@5.2.0':