select time feature & get final order values

This commit is contained in:
vchikalkin 2025-05-07 17:33:35 +03:00
parent 7fcf67eece
commit 3738c4e2a9
7 changed files with 51 additions and 34 deletions

View File

@ -6,6 +6,7 @@ import dayjs from 'dayjs';
export function DateSelect() { export function DateSelect() {
const selectedDate = useOrderStore((store) => store.date); const selectedDate = useOrderStore((store) => store.date);
const setDate = useOrderStore((store) => store.setDate); const setDate = useOrderStore((store) => store.setDate);
const setTime = useOrderStore((store) => store.setTime);
return ( return (
<Calendar <Calendar
@ -16,6 +17,7 @@ export function DateSelect() {
mode="single" mode="single"
onSelect={(date) => { onSelect={(date) => {
if (date) setDate(date); if (date) setDate(date);
setTime(null);
}} }}
selected={selectedDate} selected={selectedDate}
/> />

View File

@ -1,31 +1,34 @@
/* eslint-disable canonical/id-match */
'use client'; 'use client';
import { useSlots } from '@/hooks/slots';
import { useOrderStore } from '@/stores/order';
import { Enum_Slot_State, type SlotFieldsFragment } from '@repo/graphql/types';
import { Button } from '@repo/ui/components/ui/button'; import { Button } from '@repo/ui/components/ui/button';
import dayjs, { type Dayjs } from 'dayjs'; import dayjs, { type Dayjs } from 'dayjs';
import { sift } from 'radash';
type TimeSlotsProps = { const generateTimeSlots = (slots: SlotFieldsFragment[]): Dayjs[] => {
endTime?: string;
interval?: number;
startTime?: string;
};
const generateTimeSlots = (start: string, end: string, interval: number): Dayjs[] => {
const times: Dayjs[] = []; const times: Dayjs[] = [];
let currentTime = dayjs(start, 'HH:mm'); for (const slot of slots) {
const endTime = dayjs(end, 'HH:mm'); let currentTime = dayjs(`${slot.date} ${slot.time_start}`);
const endTime = dayjs(`${slot.date} ${slot.time_end}`);
while (currentTime.isBefore(endTime) || currentTime.isSame(endTime)) { while (currentTime.isBefore(endTime) || currentTime.isSame(endTime)) {
times.push(currentTime); times.push(currentTime);
currentTime = currentTime.add(interval, 'minute'); currentTime = currentTime.add(30, 'minute');
}
} }
return times; return times;
}; };
export function TimeSelect({ export function TimeSelect() {
endTime = '2025-03-10T20:00:00', const masterId = useOrderStore((store) => store.masterId);
interval = 30, const date = useOrderStore((store) => store.date);
startTime = '2025-03-10T09:00:00', const { data } = useSlots({ date, masterId });
}: Readonly<TimeSlotsProps>) {
const timeSlots = generateTimeSlots(startTime, endTime, interval); const openedSlots = data?.data.slots.filter((slot) => slot?.state === Enum_Slot_State.Open);
const timeSlots = generateTimeSlots(openedSlots ? sift(openedSlots) : []);
const morning = timeSlots.filter((time) => time.hour() < 12); const morning = timeSlots.filter((time) => time.hour() < 12);
const afternoon = timeSlots.filter((time) => time.hour() >= 12 && time.hour() < 18); const afternoon = timeSlots.filter((time) => time.hour() >= 12 && time.hour() < 18);
@ -41,6 +44,8 @@ export function TimeSelect({
} }
function TimeSlotsButtons({ times, title }: Readonly<{ times: Dayjs[]; title: string }>) { function TimeSlotsButtons({ times, title }: Readonly<{ times: Dayjs[]; title: string }>) {
const setTime = useOrderStore((store) => store.setTime);
if (!times.length) return null; if (!times.length) return null;
return ( return (
@ -48,7 +53,12 @@ function TimeSlotsButtons({ times, title }: Readonly<{ times: Dayjs[]; title: st
<h2 className="text-lg font-semibold">{title}</h2> <h2 className="text-lg font-semibold">{title}</h2>
<div className="grid grid-cols-3 gap-2"> <div className="grid grid-cols-3 gap-2">
{times.map((time) => ( {times.map((time) => (
<Button className="mb-2" key={time.toString()} variant="outline"> <Button
className="mb-2"
key={time.toString()}
onClick={() => setTime(time.format('HH:mm'))}
variant="outline"
>
{time.format('HH:mm')} {time.format('HH:mm')}
</Button> </Button>
))} ))}

View File

@ -1,5 +1,5 @@
export * from './back-button'; export * from './back-button';
export * from './contacts-grid'; export * from './contacts-grid';
export * from './datetime-select'; export * from './datetime-select';
export * from './next-button';
export * from './service-select'; export * from './service-select';
export * from './submit-button';

View File

@ -2,34 +2,38 @@
import { useOrderStore } from '@/stores/order'; import { useOrderStore } from '@/stores/order';
import { Button } from '@repo/ui/components/ui/button'; import { Button } from '@repo/ui/components/ui/button';
export function SubmitButton() { export function NextButton() {
const step = useOrderStore((store) => store.step); const { clientId, date, masterId, nextStep, serviceId, step, time } = useOrderStore(
const nextStep = useOrderStore((store) => store.nextStep); (store) => store,
);
function handleOnClick() {
if (step !== 'success') {
nextStep();
}
}
const disabled = useButtonDisabled(); const disabled = useButtonDisabled();
function handleOnClick() {
nextStep();
// eslint-disable-next-line no-console
console.log({ clientId, date, masterId, serviceId, step, time });
}
return ( return (
<Button className="w-full" disabled={disabled} onClick={() => handleOnClick()} type="button"> <Button className="w-full" disabled={disabled} onClick={() => handleOnClick()} type="button">
{step === 'success' ? 'Создать' : 'Продолжить'} {step === 'datetime-select' ? 'Завершить' : 'Далее'}
</Button> </Button>
); );
} }
function useButtonDisabled() { function useButtonDisabled() {
const step = useOrderStore((state) => state.step);
const clientId = useOrderStore((state) => state.clientId); const clientId = useOrderStore((state) => state.clientId);
const masterId = useOrderStore((state) => state.masterId); const masterId = useOrderStore((state) => state.masterId);
const serviceId = useOrderStore((state) => state.serviceId); const serviceId = useOrderStore((state) => state.serviceId);
const step = useOrderStore((state) => state.step); const date = useOrderStore((state) => state.date);
const time = useOrderStore((state) => state.time);
return ( return (
(step === 'master-select' && !masterId) || (step === 'master-select' && !masterId) ||
(step === 'client-select' && !clientId) || (step === 'client-select' && !clientId) ||
(step === 'service-select' && !serviceId) (step === 'service-select' && !serviceId) ||
(step === 'datetime-select' && (!date || !time))
); );
} }

View File

@ -5,8 +5,8 @@ import {
ClientsGrid, ClientsGrid,
DateTimeSelect, DateTimeSelect,
MastersGrid, MastersGrid,
NextButton,
ServiceSelect, ServiceSelect,
SubmitButton,
} from './components'; } from './components';
import { OrderStoreProvider, useOrderStore } from '@/stores/order'; import { OrderStoreProvider, useOrderStore } from '@/stores/order';
import { useInitOrderStore } from '@/stores/order/hooks'; import { useInitOrderStore } from '@/stores/order/hooks';
@ -30,7 +30,7 @@ export const OrderForm = withContext(OrderStoreProvider)(function () {
<div className="space-y-4 [&>*]:px-4"> <div className="space-y-4 [&>*]:px-4">
{getStepComponent(step)} {getStepComponent(step)}
<div className="space-y-2"> <div className="space-y-2">
<SubmitButton /> <NextButton />
<BackButton /> <BackButton />
</div> </div>
</div> </div>

View File

@ -18,7 +18,7 @@ type SlotMutationInput = {
type SlotQueryInput = { type SlotQueryInput = {
date: Date; date: Date;
masterId?: string; masterId?: null | string;
}; };
export const useSlots = ({ date, masterId }: SlotQueryInput) => { export const useSlots = ({ date, masterId }: SlotQueryInput) => {

View File

@ -20,6 +20,7 @@ export type OrderStore = {
export type Steps = export type Steps =
| 'client-select' | 'client-select'
| 'datetime-select' | 'datetime-select'
| 'error'
| 'loading' | 'loading'
| 'master-select' | 'master-select'
| 'service-select' | 'service-select'