add basic schedule page
This commit is contained in:
parent
628fb30ce9
commit
7821e39345
@ -1,3 +1,12 @@
|
||||
import { PageHeader } from '@/components/navigation';
|
||||
import { SlotsCalendar, TimeSlots } from '@/components/schedule';
|
||||
|
||||
export default function SchedulePage() {
|
||||
return 'Schedule';
|
||||
return (
|
||||
<>
|
||||
<PageHeader title="График работы" />
|
||||
<SlotsCalendar />
|
||||
<TimeSlots />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
29
apps/web/components/common/vanilla-calendar.tsx
Normal file
29
apps/web/components/common/vanilla-calendar.tsx
Normal file
@ -0,0 +1,29 @@
|
||||
'use client';
|
||||
import { useLocale } from 'next-intl';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import 'vanilla-calendar-pro/styles/index.css';
|
||||
import { Calendar, type Options } from 'vanilla-calendar-pro';
|
||||
|
||||
type CalendarProps = React.HTMLAttributes<HTMLDivElement> & {
|
||||
readonly config?: Options;
|
||||
};
|
||||
|
||||
function VanillaCalendar({ config, ...attributes }: CalendarProps) {
|
||||
const ref = useRef(null);
|
||||
const [calendar, setCalendar] = useState<Calendar | null>(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 <div {...attributes} ref={ref} />;
|
||||
}
|
||||
|
||||
export default VanillaCalendar;
|
||||
14
apps/web/components/schedule/calendar.tsx
Normal file
14
apps/web/components/schedule/calendar.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import VanillaCalendar from '@/components/common/vanilla-calendar';
|
||||
|
||||
export function SlotsCalendar() {
|
||||
return (
|
||||
<div className="p-4">
|
||||
<VanillaCalendar
|
||||
config={{
|
||||
dateMin: new Date(),
|
||||
selectionYearsMode: false,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
2
apps/web/components/schedule/index.ts
Normal file
2
apps/web/components/schedule/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './calendar';
|
||||
export * from './time-slots';
|
||||
48
apps/web/components/schedule/time-slots/add-slot-form.tsx
Normal file
48
apps/web/components/schedule/time-slots/add-slot-form.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
'use client';
|
||||
import { Button } from '@repo/ui/components/ui/button';
|
||||
import { Input } from '@repo/ui/components/ui/input';
|
||||
import { type FormEvent, useState } from 'react';
|
||||
|
||||
type Props = {
|
||||
readonly onAddSlot: (startTime: string, endTime: string) => void;
|
||||
};
|
||||
|
||||
export function AddSlotForm({ onAddSlot }: Props) {
|
||||
const [startTime, setStartTime] = useState('');
|
||||
const [endTime, setEndTime] = useState('');
|
||||
|
||||
const handleSubmit = (event: FormEvent) => {
|
||||
event.preventDefault();
|
||||
if (startTime && endTime) {
|
||||
onAddSlot(startTime, endTime);
|
||||
setStartTime('');
|
||||
setEndTime('');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form className="flex items-center space-x-2" onSubmit={handleSubmit}>
|
||||
<div className="flex flex-1 space-x-2">
|
||||
<div className="flex-1">
|
||||
<Input
|
||||
id="start-time"
|
||||
onChange={(event) => setStartTime(event.target.value)}
|
||||
required
|
||||
type="time"
|
||||
value={startTime}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<Input
|
||||
id="end-time"
|
||||
onChange={(event) => setEndTime(event.target.value)}
|
||||
required
|
||||
type="time"
|
||||
value={endTime}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Button type="submit">Добавить</Button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
10
apps/web/components/schedule/time-slots/index.tsx
Normal file
10
apps/web/components/schedule/time-slots/index.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { AddSlotForm } from './add-slot-form';
|
||||
|
||||
export function TimeSlots() {
|
||||
return (
|
||||
<div className="p-4">
|
||||
<h3 className="text-md mb-2 font-semibold">Добавить новый интервал</h3>
|
||||
<AddSlotForm onAddSlot={undefined} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -27,6 +27,7 @@
|
||||
"react": "catalog:",
|
||||
"react-dom": "catalog:",
|
||||
"use-debounce": "^10.0.4",
|
||||
"vanilla-calendar-pro": "^3.0.3",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
8
pnpm-lock.yaml
generated
8
pnpm-lock.yaml
generated
@ -177,6 +177,9 @@ 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
|
||||
@ -6121,6 +6124,9 @@ 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}
|
||||
@ -12945,6 +12951,8 @@ 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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user