(null);
- const locale = useLocale();
-
- useEffect(() => {
- if (!ref.current) return;
- setCalendar(new Calendar(ref.current, { ...config, locale }));
- }, [config, locale, ref]);
-
- useEffect(() => {
- if (!calendar) return;
- calendar.init();
- }, [calendar]);
-
- return ;
-}
-
-export default VanillaCalendar;
diff --git a/apps/web/components/schedule/calendar.tsx b/apps/web/components/schedule/calendar.tsx
index 7b2cf9b..db7a33c 100644
--- a/apps/web/components/schedule/calendar.tsx
+++ b/apps/web/components/schedule/calendar.tsx
@@ -1,13 +1,24 @@
-import VanillaCalendar from '@/components/common/vanilla-calendar';
+'use client';
+import { ScheduleSlotsContext } from '@/context/schedule-slots';
+import { Calendar } from '@repo/ui/components/ui/calendar';
+import dayjs from 'dayjs';
+import { useContext } from 'react';
export function SlotsCalendar() {
+ const { selectedDate, setSelectedDate } = useContext(ScheduleSlotsContext);
+
return (
-
-
+ {
+ return dayjs().isAfter(dayjs(date), 'day');
}}
+ mode="single"
+ onSelect={(date) => {
+ if (date) setSelectedDate(date);
+ }}
+ selected={selectedDate}
/>
);
diff --git a/apps/web/context/schedule-slots.tsx b/apps/web/context/schedule-slots.tsx
new file mode 100644
index 0000000..61cbd1b
--- /dev/null
+++ b/apps/web/context/schedule-slots.tsx
@@ -0,0 +1,17 @@
+'use client';
+import { createContext, useMemo, useState } from 'react';
+
+type ContextType = {
+ selectedDate: Date;
+ setSelectedDate: (date: Date) => void;
+};
+
+export const ScheduleSlotsContext = createContext({} as ContextType);
+
+export function ScheduleSlotsProvider({ children }: { readonly children: React.ReactNode }) {
+ const [selectedDate, setSelectedDate] = useState(new Date());
+
+ const value = useMemo(() => ({ selectedDate, setSelectedDate }), [selectedDate, setSelectedDate]);
+
+ return {children};
+}
diff --git a/apps/web/package.json b/apps/web/package.json
index 500ebfd..6a73734 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -17,6 +17,7 @@
"@repo/ui": "workspace:*",
"@tanstack/react-query": "^5.64.1",
"@telegram-apps/sdk-react": "^2.0.19",
+ "dayjs": "^1.11.13",
"graphql": "catalog:",
"lucide-react": "catalog:",
"next": "^15.1.5",
@@ -27,7 +28,6 @@
"react": "catalog:",
"react-dom": "catalog:",
"use-debounce": "^10.0.4",
- "vanilla-calendar-pro": "^3.0.3",
"zod": "catalog:"
},
"devDependencies": {
diff --git a/packages/ui/package.json b/packages/ui/package.json
index d22a8fa..a8a4e72 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -26,7 +26,7 @@
"type-check": "tsc --noEmit"
},
"devDependencies": {
- "@radix-ui/react-slot": "^1.1.0",
+ "@radix-ui/react-slot": "^1.1.1",
"@repo/eslint-config": "workspace:*",
"@repo/lint-staged-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
@@ -50,7 +50,9 @@
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-scroll-area": "^1.2.2",
"@radix-ui/react-select": "^2.1.4",
+ "date-fns": "^4.1.0",
"react": "catalog:",
+ "react-day-picker": "8.10.1",
"react-dom": "catalog:"
}
}
diff --git a/packages/ui/src/components/ui/calendar.tsx b/packages/ui/src/components/ui/calendar.tsx
new file mode 100644
index 0000000..8bfb4cd
--- /dev/null
+++ b/packages/ui/src/components/ui/calendar.tsx
@@ -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 & {
+ readonly localeName?: 'ru';
+};
+
+function Calendar({
+ className,
+ classNames,
+ localeName = 'ru',
+ showOutsideDays = true,
+ ...props
+}: CalendarProps) {
+ return (
+ (
+
+ ),
+ IconRight: ({ className, ...props }) => (
+
+ ),
+ }}
+ showOutsideDays={showOutsideDays}
+ {...props}
+ locale={localeName === 'ru' ? ru : undefined}
+ />
+ );
+}
+
+Calendar.displayName = 'Calendar';
+
+export { Calendar };
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a19094c..6984af5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -147,6 +147,9 @@ importers:
'@telegram-apps/sdk-react':
specifier: ^2.0.19
version: 2.0.19(@types/react@19.0.1)(react@19.0.0)
+ dayjs:
+ specifier: ^1.11.13
+ version: 1.11.13
graphql:
specifier: 'catalog:'
version: 16.9.0
@@ -177,9 +180,6 @@ importers:
use-debounce:
specifier: ^10.0.4
version: 10.0.4(react@19.0.0)
- vanilla-calendar-pro:
- specifier: ^3.0.3
- version: 3.0.3
zod:
specifier: 'catalog:'
version: 3.24.1
@@ -327,15 +327,21 @@ importers:
'@radix-ui/react-select':
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)
+ date-fns:
+ specifier: ^4.1.0
+ version: 4.1.0
react:
specifier: 'catalog:'
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:
specifier: 'catalog:'
version: 19.0.0(react@19.0.0)
devDependencies:
'@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)
'@repo/eslint-config':
specifier: workspace:*
@@ -3281,6 +3287,12 @@ packages:
dataloader@2.2.3:
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:
resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==}
@@ -5277,6 +5289,12 @@ packages:
ramda@0.30.1:
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:
resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==}
peerDependencies:
@@ -6124,9 +6142,6 @@ packages:
resolution: {integrity: sha512-Z6Uz+TYwEqE7ZN50gwn+1LCVo9ZVrpxRPOhOLnncYkY1ZzOYtrX8Fwf/rFktZ8R5mJms6EZf5TqNOMeZmnPq9Q==}
engines: {node: '>=12'}
- vanilla-calendar-pro@3.0.3:
- resolution: {integrity: sha512-bfmeGFaDeakk/OrwgM+22qDiRvIx1Zu+GG3+E/sCPjaKwbJR7AsDyRBfJQzVHRw3eeavU8jQQTRSFt8EPIPOGw==}
-
vite-node@2.1.8:
resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==}
engines: {node: ^18.0.0 || >=20.0.0}
@@ -9785,6 +9800,10 @@ snapshots:
dataloader@2.2.3: {}
+ date-fns@4.1.0: {}
+
+ dayjs@1.11.13: {}
+
debounce@1.2.1: {}
debug@3.2.7:
@@ -12047,6 +12066,11 @@ snapshots:
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):
dependencies:
react: 19.0.0
@@ -12951,8 +12975,6 @@ snapshots:
value-or-promise@1.0.12: {}
- vanilla-calendar-pro@3.0.3: {}
-
vite-node@2.1.8(@types/node@20.17.8):
dependencies:
cac: 6.7.14