287 lines
11 KiB
TypeScript
287 lines
11 KiB
TypeScript
'use client';
|
||
|
||
import React, {FC, useEffect, useState} from 'react';
|
||
import classNames from 'classnames';
|
||
import { Modal, Menu, Calendar, Radio, Button, Input, message, Form } from 'antd';
|
||
import type { CalendarProps, MenuProps } from 'antd';
|
||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||
import { CloseOutlined } from '@ant-design/icons';
|
||
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 { getLocale } from '../../utils/locale';
|
||
import { AUTH_TOKEN_KEY, SESSION_DATA } from '../../constants/common';
|
||
import { ExpertScheduler, SignupSessionData } from '../../types/experts';
|
||
import { Tag } from '../../types/tags';
|
||
import { getSchedulerByExpertId, getSchedulerSession } from '../../actions/experts';
|
||
import { StripeElementsForm } from '../stripe/StripeElementsForm';
|
||
import { i18nText } from '../../i18nKeys';
|
||
import { CustomSelect } from '../../components/view/CustomSelect';
|
||
import { Loader } from '../view/Loader';
|
||
import { getStorageValue } from '../../hooks/useLocalStorage';
|
||
|
||
type ScheduleModalProps = {
|
||
open: boolean;
|
||
handleCancel: () => void;
|
||
mode: 'data' | 'time' | 'pay' | 'finish';
|
||
updateMode: (mode: 'data' | 'time' | 'pay' | 'finish') => void;
|
||
sessionCost: number;
|
||
expertId: string;
|
||
locale: string;
|
||
checkSession: (data?: SignupSessionData) => void;
|
||
};
|
||
|
||
type MenuItem = Required<MenuProps>['items'][number];
|
||
|
||
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,
|
||
checkSession,
|
||
}) => {
|
||
const [selectDate, setSelectDate] = useState<Dayjs>(dayjs());
|
||
const [dates, setDates] = useState<Record<string, { startTime: string, endTime: string }[]> | undefined>();
|
||
const [tags, setTags] = useState<Tag[] | undefined>();
|
||
const [rawScheduler, setRawScheduler] = useState<ExpertScheduler | null>(null);
|
||
const [isPayLoading, setIsPayLoading] = useState<boolean>(false);
|
||
const [sessionId, setSessionId] = useState<string>('');
|
||
const [form] = Form.useForm<{ clientComment?: string, startAtUtc?: string, tagId?: number }>();
|
||
|
||
dayjs.locale(locale);
|
||
|
||
const signupSession = () => {
|
||
const data = sessionStorage?.getItem(SESSION_DATA);
|
||
const jwt = getStorageValue(AUTH_TOKEN_KEY, '');
|
||
|
||
if (jwt && data) {
|
||
const parseData = JSON.parse(data);
|
||
setIsPayLoading(true);
|
||
getSchedulerSession(parseData as SignupSessionData, locale || 'en', jwt)
|
||
.then((session) => {
|
||
setSessionId(session?.sessionId);
|
||
console.log(session?.sessionId);
|
||
})
|
||
.catch((err) => {
|
||
console.log(err);
|
||
message.error('Не удалось провести оплату')
|
||
})
|
||
.finally(() => {
|
||
sessionStorage?.removeItem(SESSION_DATA);
|
||
setIsPayLoading(false);
|
||
})
|
||
}
|
||
};
|
||
|
||
useEffect(()=> {
|
||
if (open && mode !== 'pay') {
|
||
getSchedulerByExpertId(expertId as string, locale as string)
|
||
.then((data) => {
|
||
setRawScheduler(data);
|
||
})
|
||
.catch((err) => {
|
||
console.log(err);
|
||
});
|
||
}
|
||
|
||
if (!open) {
|
||
form.resetFields();
|
||
}
|
||
}, [open]);
|
||
|
||
useEffect(() => {
|
||
if (open && mode === 'pay') {
|
||
signupSession();
|
||
}
|
||
}, [mode]);
|
||
|
||
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 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>
|
||
),
|
||
});
|
||
};
|
||
|
||
const onValidate = () => {
|
||
form.validateFields()
|
||
.then((values) => {
|
||
checkSession({ coachId: +expertId, ...values });
|
||
})
|
||
}
|
||
|
||
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>
|
||
<Form form={form}>
|
||
<div className="b-schedule-select-tag">
|
||
{tags && (
|
||
<Form.Item
|
||
name="tagId"
|
||
rules={[{
|
||
required: true,
|
||
message: ''
|
||
}]}
|
||
>
|
||
<CustomSelect
|
||
label={i18nText('selectTopic', locale)}
|
||
options={tags?.map(({id, name}) => ({value: id, label: name}))}
|
||
/>
|
||
</Form.Item>
|
||
)}
|
||
</div>
|
||
<div className="b-schedule-radio-list">
|
||
<Form.Item
|
||
name="startAtUtc"
|
||
rules={[{
|
||
required: true,
|
||
message: ''
|
||
}]}
|
||
>
|
||
<Radio.Group>
|
||
{dates && dates[selectDate.format('YYYY-MM-DD')].map((el: any) => (
|
||
<Radio
|
||
key={el.startTime}
|
||
value={el.startTime}
|
||
>
|
||
{dayjs(el.startTime).format('HH:mm')} - {dayjs(el.endTime).format('HH:mm')}
|
||
</Radio>)
|
||
)}
|
||
</Radio.Group>
|
||
</Form.Item>
|
||
</div>
|
||
<Form.Item name="clientComment">
|
||
<Input.TextArea
|
||
className="b-textarea"
|
||
rows={2}
|
||
placeholder={i18nText('sessionWishes', locale)}
|
||
/>
|
||
</Form.Item>
|
||
</Form>
|
||
<Button
|
||
className="btn-apply"
|
||
onClick={onValidate}
|
||
>
|
||
{i18nText('pay', locale)}
|
||
</Button>
|
||
</div>
|
||
)}
|
||
{mode === 'pay' && (
|
||
<div className="b-schedule-payment">
|
||
<Loader isLoading={isPayLoading}>
|
||
<StripeElementsForm
|
||
amount={sessionCost}
|
||
locale={locale}
|
||
sessionId={sessionId}
|
||
/>
|
||
</Loader>
|
||
</div>
|
||
)}
|
||
</Modal>
|
||
);
|
||
};
|