275 lines
10 KiB
TypeScript
275 lines
10 KiB
TypeScript
'use client';
|
||
|
||
import React, { FC, useEffect, useState } from 'react';
|
||
import classNames from 'classnames';
|
||
import { Modal, Menu, Calendar, Radio, Button, Input, message } from 'antd';
|
||
import type { CalendarProps, RadioChangeEvent, MenuProps } from 'antd';
|
||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||
import { CloseOutlined } from '@ant-design/icons';
|
||
import locale_ru from 'antd/lib/calendar/locale/ru_RU';
|
||
import locale_en from 'antd/lib/calendar/locale/en_GB';
|
||
import locale_de from 'antd/lib/calendar/locale/de_DE';
|
||
import locale_it from 'antd/lib/calendar/locale/it_IT';
|
||
import locale_es from 'antd/lib/calendar/locale/es_ES';
|
||
import locale_fr from 'antd/lib/calendar/locale/fr_FR';
|
||
import { RegisterContent, ResetContent, FinishContent, EnterContent } from './authModalContent';
|
||
import dayjs, { Dayjs } from 'dayjs';
|
||
import 'dayjs/locale/ru';
|
||
import 'dayjs/locale/en';
|
||
import 'dayjs/locale/de';
|
||
import 'dayjs/locale/it';
|
||
import 'dayjs/locale/fr';
|
||
import 'dayjs/locale/es';
|
||
import { ExpertScheduler, SignupSessionData } from "../../types/experts";
|
||
import { Tag } from "../../types/tags";
|
||
import { useLocalStorage } from "../../hooks/useLocalStorage";
|
||
import { AUTH_TOKEN_KEY } from "../../constants/common";
|
||
import { getSchedulerByExpertId, getSchedulerSession } from "../../actions/experts";
|
||
import { ElementsForm } from "../stripe/ElementsForm";
|
||
import { i18nText } from '../../i18nKeys';
|
||
import { CustomSelect } from '../../components/view/CustomSelect';
|
||
|
||
type ScheduleModalProps = {
|
||
open: boolean;
|
||
handleCancel: () => void;
|
||
mode: 'data' | 'time' | 'pay' | 'finish';
|
||
updateMode: (mode: 'data' | 'time' | 'pay' | 'finish') => void;
|
||
sessionCost: number;
|
||
expertId: string;
|
||
locale: string;
|
||
};
|
||
|
||
type MenuItem = Required<MenuProps>['items'][number];
|
||
|
||
const getLocale = (locale: string) => {
|
||
if (locale) {
|
||
switch (locale) {
|
||
case 'ru':
|
||
return locale_ru;
|
||
case 'de':
|
||
return locale_de;
|
||
case 'fr':
|
||
return locale_fr;
|
||
case 'it':
|
||
return locale_it;
|
||
case 'es':
|
||
return locale_es;
|
||
default:
|
||
return locale_en;
|
||
};
|
||
}
|
||
|
||
return locale_en;
|
||
};
|
||
|
||
const getCalendarMenu = (start: Dayjs): MenuItem[] => Array.from({ length: 3 })
|
||
.map((_: unknown, index: number) => {
|
||
const date = index ? start.add(index, 'M') : start.clone();
|
||
return {
|
||
label: <span className="b-calendar-month">{date.format('MMMM')}</span>,
|
||
key: date.format('YYYY-MM-DD')
|
||
}
|
||
});
|
||
|
||
export const ScheduleModal: FC<ScheduleModalProps> = ({
|
||
open,
|
||
handleCancel,
|
||
mode,
|
||
updateMode,
|
||
sessionCost,
|
||
locale,
|
||
expertId,
|
||
}) => {
|
||
const [selectDate, setSelectDate] = useState<Dayjs>(dayjs());
|
||
const [dates, setDates] = useState<any>();
|
||
const [tags, setTags] = useState<Tag[] | undefined>();
|
||
const [sessionData, setSesssionData] = useState<SignupSessionData>({ coachId: +expertId });
|
||
const [sessionId, setSessionId] = useState<number>(-1);
|
||
const [rawScheduler, setRawScheduler] = useState<ExpertScheduler | null>(null);
|
||
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||
const [isPayLoading, setIsPayLoading] = useState<boolean>(false);
|
||
|
||
dayjs.locale(locale);
|
||
|
||
useEffect(()=> {
|
||
if (open) {
|
||
getSchedulerByExpertId(expertId as string, locale as string)
|
||
.then((data) => {
|
||
setRawScheduler(data);
|
||
})
|
||
.catch((err) => {
|
||
console.log(err);
|
||
});
|
||
}
|
||
}, [open]);
|
||
|
||
useEffect(() => {
|
||
const map = {} as any
|
||
rawScheduler?.availableSlots.forEach((el) => {
|
||
const key = dayjs(el.startTime).format('YYYY-MM-DD');
|
||
if (!map[key]){
|
||
map[key] = []
|
||
}
|
||
map[key].push(el);
|
||
})
|
||
setDates(map);
|
||
setTags(rawScheduler?.tags)
|
||
}, [rawScheduler]);
|
||
|
||
const onPanelChange = (value: Dayjs) => setSelectDate(value);
|
||
|
||
const onDateChange: CalendarProps<Dayjs>['onSelect'] = (value, selectInfo) => {
|
||
if (selectInfo.source === 'date') {
|
||
setSelectDate(value);
|
||
updateMode('time');
|
||
}
|
||
};
|
||
|
||
const disabledDate = (currentDate: Dayjs) => !dates || !dates[currentDate.format('YYYY-MM-DD')];
|
||
|
||
const onChangeTimeSlot = (e: RadioChangeEvent) => setSesssionData({ ...sessionData, startAtUtc: e.target.value.startTime });
|
||
|
||
const onChangeTag = (tagId: number) => setSesssionData({ ...sessionData, tagId });
|
||
|
||
const singupSession = () => {
|
||
console.log(sessionData);
|
||
if (sessionData?.startAtUtc && sessionData?.tagId) {
|
||
if (jwt) {
|
||
setIsPayLoading(true);
|
||
getSchedulerSession(sessionData, locale, jwt)
|
||
.then((session) => {
|
||
console.log(session);
|
||
// тут должна быть проверка все ли с регистрацией сессии
|
||
setSessionId(+session?.sessionId);
|
||
updateMode('pay');
|
||
})
|
||
.catch((err) => {
|
||
console.log(err);
|
||
message.error('Не удалось провести оплату')
|
||
})
|
||
.finally(() => {
|
||
setIsPayLoading(false);
|
||
})
|
||
} else {
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
const cellRender: CalendarProps<Dayjs>['fullCellRender'] = (date, info) => {
|
||
const isWeekend = date.day() === 6 || date.day() === 0;
|
||
return React.cloneElement(info.originNode, {
|
||
...info.originNode.props,
|
||
className: classNames('b-calendar-cell', {
|
||
['b-calendar-cell__select']: selectDate.isSame(date, 'date'),
|
||
['b-calendar-cell__today']: date.isSame(dayjs(), 'date'),
|
||
['b-calendar-cell__weekend']: isWeekend,
|
||
}),
|
||
children: (
|
||
<div>
|
||
<span>
|
||
{date.get('date')}
|
||
</span>
|
||
</div>
|
||
),
|
||
});
|
||
};
|
||
|
||
return (
|
||
<Modal
|
||
className="b-modal"
|
||
open={open}
|
||
title={undefined}
|
||
onOk={undefined}
|
||
onCancel={handleCancel}
|
||
footer={false}
|
||
width={498}
|
||
closeIcon={<CloseOutlined style={{ fontSize: 20, color: '#000' }}/>}
|
||
>
|
||
{mode === 'data' && (
|
||
<Calendar
|
||
className="b-calendar"
|
||
fullscreen={false}
|
||
onPanelChange={onPanelChange}
|
||
fullCellRender={cellRender}
|
||
onSelect={onDateChange}
|
||
value={selectDate}
|
||
disabledDate={disabledDate}
|
||
locale={getLocale(locale)}
|
||
validRange={[selectDate.startOf('M'), selectDate.endOf('M')]}
|
||
headerRender={({ onChange }) => {
|
||
const start = dayjs().startOf('M');
|
||
const [activeMonth, setActiveMonth] = useState<string>(start.format('YYYY-MM-DD'));
|
||
|
||
const onClick: MenuProps['onClick'] = (e) => {
|
||
setActiveMonth(e.key);
|
||
onChange(dayjs(e.key));
|
||
};
|
||
|
||
return (
|
||
<Menu
|
||
className="b-calendar-header"
|
||
onClick={onClick}
|
||
selectedKeys={[activeMonth]}
|
||
mode="horizontal"
|
||
items={getCalendarMenu(start)}
|
||
/>
|
||
);
|
||
}}
|
||
/>
|
||
)}
|
||
{mode === 'time' && (
|
||
<div className="b-schedule-time">
|
||
<div className="b-schedule-time-header">
|
||
<Button
|
||
className="b-button-link-big"
|
||
type="link"
|
||
onClick={() => updateMode('data')}
|
||
icon={<ArrowLeftOutlined />}
|
||
iconPosition="start"
|
||
>
|
||
{selectDate.locale(locale).format('DD MMMM YYYY')}
|
||
</Button>
|
||
</div>
|
||
<div className="b-schedule-select-tag">
|
||
{tags && (
|
||
<CustomSelect
|
||
label={i18nText('selectTopic', locale)}
|
||
value={sessionData?.tagId}
|
||
options={tags?.map(({ id, name }) => ({ value: id, label: name }))}
|
||
onChange={onChangeTag}
|
||
/>
|
||
)}
|
||
</div>
|
||
<div className="b-schedule-radio-list">
|
||
<Radio.Group name="radiogroupSlots" onChange={onChangeTimeSlot}>
|
||
{dates[selectDate.format('YYYY-MM-DD')].map((el) => {
|
||
return (<Radio key={dayjs(el.startTime).format()} value={el}>{dayjs(el.startTime).format('HH:mm')} - {dayjs(el.endTime).format('HH:mm')}</Radio>)
|
||
})}
|
||
</Radio.Group>
|
||
</div>
|
||
<div>
|
||
<Input.TextArea
|
||
className="b-textarea"
|
||
rows={1}
|
||
value={sessionData?.clientComment}
|
||
placeholder={i18nText('sessionWishes', locale)}
|
||
onChange={(e) => setSesssionData({ ...sessionData, clientComment: e.target.value })}
|
||
/>
|
||
</div>
|
||
<Button
|
||
className="btn-apply"
|
||
onClick={singupSession}
|
||
loading={isPayLoading}
|
||
>
|
||
{i18nText('pay', locale)}
|
||
</Button>
|
||
</div>
|
||
)}
|
||
{mode === 'pay' && (
|
||
<ElementsForm amount={sessionCost}/>
|
||
)}
|
||
</Modal>
|
||
);
|
||
};
|