feat: update views, styles, actions

This commit is contained in:
SD 2024-10-16 21:47:28 +04:00
parent 59de68d611
commit d5808e96db
16 changed files with 467 additions and 414 deletions

View File

@ -1,6 +1,5 @@
import { apiRequest } from './helpers'; import { apiRequest } from './helpers';
import { apiClient } from '../lib/apiClient'; import { GeneralFilter, ExpertsData, ExpertDetails, ExpertScheduler, ExpertSchedulerSession, SignupSessionData } from '../types/experts';
import {GeneralFilter, ExpertsData, ExpertDetails, ExpertScheduler, ExpertSchedulerSession} from '../types/experts';
export const getExpertsList = (locale: string, filter?: GeneralFilter): Promise<ExpertsData> => apiRequest({ export const getExpertsList = (locale: string, filter?: GeneralFilter): Promise<ExpertsData> => apiRequest({
url: '/home/coachsearch1', url: '/home/coachsearch1',
@ -16,32 +15,17 @@ export const getExpertById = (id: string, locale: string): Promise<ExpertDetails
locale locale
}); });
export const getSchedulerByExpertId = async (expertId: string, locale: string, jwt: string) => { export const getSchedulerByExpertId = (id: string, locale: string): Promise<ExpertScheduler> => apiRequest({
const response = await apiClient.post( url: '/home/sessionsignupdata',
'/home/sessionsignupdata', method: 'post',
{ id: expertId }, data: { id },
{ locale
headers: { });
'X-User-Language': locale,
Authorization: `Bearer ${jwt}`
}
}
);
return response.data as ExpertScheduler || null; export const getSchedulerSession = (data: SignupSessionData, locale: string, token: string): Promise<ExpertSchedulerSession> => apiRequest({
}; url: '/home/sessionsignupsubmit',
method: 'post',
export const getSchedulerSession = async (data: { coachId: number, tagId: number, startAtUtc: string, clientComment: string }, locale: string, jwt: string) => {
const response = await apiClient.post(
'/home/sessionsignupsubmit',
data, data,
{ locale,
headers: { token
'X-User-Language': locale, });
Authorization: `Bearer ${jwt}`
}
}
);
return response.data as ExpertSchedulerSession || null;
};

View File

@ -6,13 +6,11 @@ import { getExpertById, getExpertsList } from '../../../../actions/experts';
import { import {
ExpertCard, ExpertCard,
ExpertCertificate, ExpertCertificate,
ExpertInformation,
ExpertPractice ExpertPractice
} from '../../../../components/Experts/ExpertDetails'; } from '../../../../components/Experts/ExpertDetails';
import { Details } from '../../../../types/education'; import { Details } from '../../../../types/education';
import { BackButton } from '../../../../components/view/BackButton'; import { BackButton } from '../../../../components/view/BackButton';
import { i18nText } from '../../../../i18nKeys'; import { i18nText } from '../../../../i18nKeys';
import {SchedulerModal} from "../../../../components/Modals/SchedulerModal";
export const metadata: Metadata = { export const metadata: Metadata = {
title: 'Bbuddy - Experts item', title: 'Bbuddy - Experts item',
@ -84,7 +82,6 @@ export default async function ExpertItem({ params: { expertId = '', locale } }:
</Suspense> </Suspense>
</div> </div>
<ExpertCard expert={expert} locale={locale} expertId={expertId}/> <ExpertCard expert={expert} locale={locale} expertId={expertId}/>
<ExpertInformation expert={expert} locale={locale} />
<h2 className="title-h2">{i18nText('expertBackground', locale)}</h2> <h2 className="title-h2">{i18nText('expertBackground', locale)}</h2>
<p className="base-text"> <p className="base-text">

View File

@ -1,8 +1,8 @@
'use client'; 'use client';
import React, {FC, useEffect, useState} from 'react'; import React, { FC, useState } from 'react';
import Image from 'next/image'; import Image from 'next/image';
import { Tag, Image as AntdImage, Space } from 'antd'; import { Tag, Image as AntdImage, Space, Button } from 'antd';
import { ZoomInOutlined, ZoomOutOutlined, StarFilled } from '@ant-design/icons'; import { ZoomInOutlined, ZoomOutOutlined, StarFilled } from '@ant-design/icons';
import { ExpertScheduler } from '../../types/experts'; import { ExpertScheduler } from '../../types/experts';
import { ExpertDetails, Practice, ThemeGroup } from '../../types/experts'; import { ExpertDetails, Practice, ThemeGroup } from '../../types/experts';
@ -15,7 +15,7 @@ import {getSchedulerByExpertId} from "../../actions/experts";
import {useLocalStorage} from "../../hooks/useLocalStorage"; import {useLocalStorage} from "../../hooks/useLocalStorage";
import {AUTH_TOKEN_KEY} from "../../constants/common"; import {AUTH_TOKEN_KEY} from "../../constants/common";
import dayjs from "dayjs"; import dayjs from "dayjs";
import {SchedulerModal} from "../Modals/SchedulerModal"; import {ScheduleModal} from "../Modals/ScheduleModal";
type ExpertDetailsProps = { type ExpertDetailsProps = {
expert: ExpertDetails; expert: ExpertDetails;
@ -33,16 +33,16 @@ export const ExpertCard: FC<ExpertDetailsProps> = ({ expert, locale, expertId })
const { publicCoachDetails } = expert || {}; const { publicCoachDetails } = expert || {};
const [showSchedulerModal, setShowSchedulerModal] = useState<boolean>(false); const [showSchedulerModal, setShowSchedulerModal] = useState<boolean>(false);
const [mode, setMode] = useState<'data' | 'time' | 'pay' | 'finish'>('data'); const [mode, setMode] = useState<'data' | 'time' | 'pay' | 'finish'>('data');
const { publicCoachDetails: { tags = [], sessionCost = 0, sessionDuration = 0 } } = expert || {}; const { publicCoachDetails: { tags = [], sessionCost = 0, sessionDuration = 0, coachLanguages = [] } } = expert || {};
const isRus = locale === Locale.ru;
const onSchedulerHandle = async () => { const onSchedulerHandle = () => {
console.log('sessionCost', sessionCost);
setMode('data'); setMode('data');
setShowSchedulerModal(true) setShowSchedulerModal(true)
// отмаппим. };
}
return ( return (
<>
<div className="expert-card"> <div className="expert-card">
<div className="expert-card__wrap"> <div className="expert-card__wrap">
<div className="expert-card__avatar"> <div className="expert-card__avatar">
@ -62,10 +62,10 @@ export const ExpertCard: FC<ExpertDetailsProps> = ({ expert, locale, expertId })
</div> </div>
</div> </div>
<div className="expert-card__wrap-btn"> <div className="expert-card__wrap-btn">
<a href="#" className="btn-apply" onClick={onSchedulerHandle}> <Button className="btn-apply" onClick={onSchedulerHandle}>
<img src="/images/calendar-outline.svg" className="" alt="" /> <img src="/images/calendar-outline.svg" className="" alt="" />
{i18nText('schedule', locale)} {i18nText('schedule', locale)}
</a> </Button>
{/* {/*
<a href="#" className="btn-video"> <a href="#" className="btn-video">
<img src="/images/videocam-outline.svg" className="" alt=""/> <img src="/images/videocam-outline.svg" className="" alt=""/>
@ -73,25 +73,7 @@ export const ExpertCard: FC<ExpertDetailsProps> = ({ expert, locale, expertId })
</a> </a>
*/} */}
</div> </div>
<SchedulerModal
open={showSchedulerModal}
handleCancel={() => setShowSchedulerModal(false)}
updateMode={setMode}
mode={mode}
expertId={expertId as string}
locale={locale as string}
sessionCost={sessionCost}
/>
</div> </div>
);
};
export const ExpertInformation: FC<ExpertDetailsProps> = ({ expert, locale }) => {
const { publicCoachDetails: { tags = [], sessionCost = 0, sessionDuration = 0, coachLanguages = [] } } = expert || {};
const isRus = locale === Locale.ru;
return (
<>
<div className="expert-info"> <div className="expert-info">
{/* <h2 className="title-h2">{}</h2> */} {/* <h2 className="title-h2">{}</h2> */}
<div className="skills__list"> <div className="skills__list">
@ -117,11 +99,20 @@ export const ExpertInformation: FC<ExpertDetailsProps> = ({ expert, locale }) =>
{tags?.map((skill) => <Tag key={skill?.id} className="skills__list__item">{skill?.name}</Tag>)} {tags?.map((skill) => <Tag key={skill?.id} className="skills__list__item">{skill?.name}</Tag>)}
</div> </div>
<div className="wrap-btn-prise"> <div className="wrap-btn-prise">
<FilledYellowButton onClick={() => console.log('schedule')}>{i18nText('signUp', locale)}</FilledYellowButton> <FilledYellowButton onClick={onSchedulerHandle}>{i18nText('signUp', locale)}</FilledYellowButton>
<div className="wrap-btn-prise__text"> <div className="wrap-btn-prise__text">
{`${sessionCost}`} <span>/ {`${sessionDuration}${isRus ? 'мин' : 'min'}`}</span> {`${sessionCost}`} <span>/ {`${sessionDuration}${isRus ? 'мин' : 'min'}`}</span>
</div> </div>
</div> </div>
<ScheduleModal
open={showSchedulerModal}
handleCancel={() => setShowSchedulerModal(false)}
updateMode={setMode}
mode={mode}
expertId={expertId as string}
locale={locale as string}
sessionCost={sessionCost}
/>
</> </>
); );
}; };

View File

@ -0,0 +1,274 @@
'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>
);
};

View File

@ -1,290 +0,0 @@
'use client';
import React, {Dispatch, FC, SetStateAction, useEffect, useState} from 'react';
import { usePathname } from 'next/navigation';
import classNames from 'classnames';
import Link from 'next/link';
import {Modal, Form, Calendar, Radio } from 'antd';
import type { CalendarProps, RadioChangeEvent } from 'antd';
import { CloseOutlined } from '@ant-design/icons';
import { RegisterContent, ResetContent, FinishContent, EnterContent } from './authModalContent';
import dayjs, { Dayjs } from 'dayjs';
import {ExpertDetails, ExpertScheduler, Tags} from "../../types/experts";
import { createStyles } from 'antd-style';
import {useLocalStorage} from "../../hooks/useLocalStorage";
import {AUTH_TOKEN_KEY} from "../../constants/common";
import {getSchedulerByExpertId, getSchedulerSession} from "../../actions/experts";
import {ElementsForm} from "../stripe/ElementsForm";
type SchedulerModalProps = {
open: boolean;
handleCancel: () => void;
mode: 'data' | 'time' | 'pay' | 'finish';
updateMode: (mode: 'data' | 'time' | 'pay' | 'finish') => void;
sessionCost: number;
expertId: string;
locale: string;
};
const useStyle = createStyles(({ token, css, cx }) => {
const lunar = css`
color: ${token.colorTextTertiary};
font-size: ${token.fontSizeSM}px;
`;
return {
wrapper: css`
width: 450px;
border: 1px solid ${token.colorBorderSecondary};
border-radius: ${token.borderRadiusOuter};
padding: 5px;
`,
dateCell: css`
position: relative;
&:before {
content: '';
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
max-width: 40px;
max-height: 40px;
background: transparent;
transition: background 300ms;
border-radius: ${token.borderRadiusOuter}px;
border: 1px solid transparent;
box-sizing: border-box;
}
&:hover:before {
background: rgba(0, 0, 0, 0.04);
}
`,
today: css`
&:before {
border: 1px solid ${token.colorPrimary};
}
`,
text: css`
position: relative;
z-index: 1;
`,
lunar,
current: css`
color: ${token.colorTextLightSolid};
&:before {
background: ${token.colorPrimary};
}
&:hover:before {
background: ${token.colorPrimary};
opacity: 0.8;
}
.${cx(lunar)} {
color: ${token.colorTextLightSolid};
opacity: 0.9;
}
`,
monthCell: css`
width: 120px;
color: ${token.colorTextBase};
border-radius: ${token.borderRadiusOuter}px;
padding: 5px 0;
&:hover {
background: rgba(0, 0, 0, 0.04);
}
`,
monthCellCurrent: css`
color: ${token.colorTextLightSolid};
background: ${token.colorPrimary};
&:hover {
background: ${token.colorPrimary};
opacity: 0.8;
}
`,
weekend: css`
color: ${token.colorError};
&.gray {
opacity: 0.4;
}
`,
};
});
export const SchedulerModal: FC<SchedulerModalProps> = ({
open,
handleCancel,
mode,
updateMode,
sessionCost,
locale,
expertId,
}) => {
const { styles } = useStyle({ test: true });
const [selectDate, setSelectDate] = React.useState<Dayjs>(dayjs());
const [dates, setDates] = React.useState<any>();
const [tags, setTags] = React.useState<Tags[]>([]);
const [tag, setTag] = React.useState<number>(-1);
const [slot, setSlot] = React.useState<string>('');
const [sessionId, setSessionId] = React.useState<number>(-1);
const [rawScheduler, setRawScheduler] = useState<ExpertScheduler | null>(null);
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
useEffect( ()=> {
async function loadScheduler(){
const rawScheduler = await getSchedulerByExpertId(expertId as string, locale as string, jwt)
setRawScheduler(rawScheduler)
}
if (open) {
loadScheduler()
}
}, [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)
})
console.log(rawScheduler, map)
setDates(map)
setTags(rawScheduler?.tags)
}, [rawScheduler]);
const onPanelChange = (value: Dayjs, mode: CalendarProps<Dayjs>['mode']) => {
console.log(value.format('YYYY-MM-DD'), mode);
};
const onDateChange: CalendarProps<Dayjs>['onSelect'] = (value, selectInfo) => {
if (selectInfo.source === 'date') {
setSelectDate(value);
updateMode('time')
}
};
const disabledDate = (currentDate: Dayjs) => {
return !dates || !dates[currentDate.format('YYYY-MM-DD')]
}
const handleTimeslot = (e: RadioChangeEvent) => {
setSlot(e.target.value.startTime)
console.log('radio checked', e.target.value);
};
const handleTag = (e: RadioChangeEvent) => {
setTag(e.target.value)
console.log('tag radio checked', e.target.value);
};
const handleSingupSession = async () => {
const data = {coachId: expertId, tagId: tag, startAtUtc: slot, clientComment:''}
console.log(data)
const session = await getSchedulerSession({coachId: expertId, tagId: tag, startAtUtc: slot, clientComment:''}, locale, jwt)
console.log(session);
// тут должна быть проверка все ли с регистрацией сессии
setSessionId(session?.sessionId)
updateMode('pay')
}
const currentDay = dayjs()
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(styles.dateCell, {
[styles.current]: selectDate.isSame(date, 'date'),
[styles.today]: date.isSame(dayjs(), 'date'),
}),
children: (
<div className={styles.text}>
<span
className={classNames({
[styles.weekend]: isWeekend,
})}
>
{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' }}/>}
>
<div>
{tags && (
<Radio.Group name="radiogroupTags" onChange={handleTag}>
{tags?.map((tag)=>(
<Radio key={tag.id} value={tag.id}>{tag.name}</Radio>
)
)}
</Radio.Group>
)
}
</div>
{mode === 'data' && (
<Calendar
fullscreen={false}
onPanelChange={onPanelChange}
fullCellRender={cellRender}
onSelect={onDateChange}
disabledDate={disabledDate}
headerRender={({ value, type, onChange, onTypeChange }) => {
const start = 0;
const end = 12;
const monthOptions = [];
let current = currentDay.clone();
const localeData = value.locale();
const months = [];
for(let i=0; i<6; i++){
const m = current.clone()
months.push(m);
current = current.add(1,'month')
}
return (<>
{months.map((m, i)=>(
<button key={'SchedulerMonth'+i} onClick={()=> onChange(m)}>
{m.month()}
</button>
))}
</>)
}}
/>
)}
{mode === 'time' && (
<>
<div>
<button onClick={()=>{updateMode('data')}}>back</button>
</div>
<Radio.Group name="radiogroupSlots" onChange={handleTimeslot}>
{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>
<button onClick={handleSingupSession}>Записаться</button>
</>
)}
{mode === 'pay' && (
<ElementsForm amount={sessionCost}/>
)}
</Modal>
);
};

View File

@ -42,7 +42,7 @@ export default {
addComment: 'Neuen Kommentar hinzufügen', addComment: 'Neuen Kommentar hinzufügen',
commentPlaceholder: 'Ihr Kommentar', commentPlaceholder: 'Ihr Kommentar',
clientComments: 'Kundenkommentare', clientComments: 'Kundenkommentare',
coachComments: 'Trainerkommentare' coachComments: 'Expertenkommentare'
}, },
room: { room: {
upcoming: 'Zukünftige Räume', upcoming: 'Zukünftige Räume',
@ -110,9 +110,9 @@ export default {
seminars: 'Seminare', seminars: 'Seminare',
courses: 'Kurse', courses: 'Kurse',
mba: 'MBA-Information', mba: 'MBA-Information',
aboutCoach: 'Über Coach', aboutCoach: 'Über den Experten',
education: 'Bildung', education: 'Bildung',
coaching: 'Coaching', coaching: 'Expertenprofil',
experiences: 'Praktische Erfahrung', experiences: 'Praktische Erfahrung',
payInfo: 'Zahlungsdaten', payInfo: 'Zahlungsdaten',
sessionDuration: 'Sitzungsdauer', sessionDuration: 'Sitzungsdauer',
@ -146,6 +146,8 @@ export default {
saturday: 'Sa', saturday: 'Sa',
addNew: 'Neu hinzufügen', addNew: 'Neu hinzufügen',
mExperiences: 'Führungserfahrung', mExperiences: 'Führungserfahrung',
pay: 'Pay',
sessionWishes: 'Write your wishes about the session',
errors: { errors: {
invalidEmail: 'Die E-Mail-Adresse ist ungültig', invalidEmail: 'Die E-Mail-Adresse ist ungültig',
emptyEmail: 'Bitte geben Sie Ihre E-Mail ein', emptyEmail: 'Bitte geben Sie Ihre E-Mail ein',

View File

@ -42,7 +42,7 @@ export default {
addComment: 'Add new', addComment: 'Add new',
commentPlaceholder: 'Your comment', commentPlaceholder: 'Your comment',
clientComments: 'Client Comments', clientComments: 'Client Comments',
coachComments: 'Coach Comments' coachComments: 'Expert Comments'
}, },
room: { room: {
upcoming: 'Upcoming Rooms', upcoming: 'Upcoming Rooms',
@ -109,10 +109,10 @@ export default {
seminars: 'Seminars', seminars: 'Seminars',
courses: 'Courses', courses: 'Courses',
mba: 'MBA Information', mba: 'MBA Information',
aboutCoach: 'About Coach', aboutCoach: 'About Expert',
skillsInfo: 'Skills Info', skillsInfo: 'Skills Info',
education: 'Education', education: 'Education',
coaching: 'Coaching', coaching: 'Expert profile',
experiences: 'Practical experience', experiences: 'Practical experience',
payInfo: 'Payment Info', payInfo: 'Payment Info',
sessionDuration: 'Session duration', sessionDuration: 'Session duration',
@ -146,6 +146,8 @@ export default {
saturday: 'Sa', saturday: 'Sa',
addNew: 'Add New', addNew: 'Add New',
mExperiences: 'Managerial Experience', mExperiences: 'Managerial Experience',
pay: 'Pay',
sessionWishes: 'Write your wishes about the session',
errors: { errors: {
invalidEmail: 'The email address is not valid', invalidEmail: 'The email address is not valid',
emptyEmail: 'Please enter your E-mail', emptyEmail: 'Please enter your E-mail',

View File

@ -42,7 +42,7 @@ export default {
addComment: 'Añadir nuevo comentario', addComment: 'Añadir nuevo comentario',
commentPlaceholder: 'Tu comentario', commentPlaceholder: 'Tu comentario',
clientComments: 'Comentarios del cliente', clientComments: 'Comentarios del cliente',
coachComments: 'Comentarios del entrenador' coachComments: 'Comentarios del experto'
}, },
room: { room: {
upcoming: 'Próximas salas', upcoming: 'Próximas salas',
@ -110,9 +110,9 @@ export default {
seminars: 'Seminarios', seminars: 'Seminarios',
courses: 'Cursos', courses: 'Cursos',
mba: 'Información sobre máster en ADE (MBA)', mba: 'Información sobre máster en ADE (MBA)',
aboutCoach: 'Sobre el coach', aboutCoach: 'Acerca del experto',
education: 'Educación', education: 'Educación',
coaching: 'Coaching', coaching: 'Perfil del experto',
experiences: 'Experiencia práctica', experiences: 'Experiencia práctica',
payInfo: 'Información de pago', payInfo: 'Información de pago',
sessionDuration: 'Duración de la sesión', sessionDuration: 'Duración de la sesión',
@ -146,6 +146,8 @@ export default {
saturday: 'S', saturday: 'S',
addNew: 'Añadir nuevo', addNew: 'Añadir nuevo',
mExperiences: 'Experiencia de dirección', mExperiences: 'Experiencia de dirección',
pay: 'Pay',
sessionWishes: 'Write your wishes about the session',
errors: { errors: {
invalidEmail: 'La dirección de correo electrónico no es válida', invalidEmail: 'La dirección de correo electrónico no es válida',
emptyEmail: 'Introduce tu correo electrónico', emptyEmail: 'Introduce tu correo electrónico',

View File

@ -42,7 +42,7 @@ export default {
addComment: 'Ajouter un nouveau commentaire', addComment: 'Ajouter un nouveau commentaire',
commentPlaceholder: 'Votre commentaire', commentPlaceholder: 'Votre commentaire',
clientComments: 'Commentaires du client', clientComments: 'Commentaires du client',
coachComments: 'Commentaires du coach' coachComments: 'Commentaires de l\'expert'
}, },
room: { room: {
upcoming: 'Salles futures', upcoming: 'Salles futures',
@ -110,9 +110,9 @@ export default {
seminars: 'Séminaires', seminars: 'Séminaires',
courses: 'Cours', courses: 'Cours',
mba: 'Infos Maîtrise en gestion', mba: 'Infos Maîtrise en gestion',
aboutCoach: 'À propos du coach', aboutCoach: 'À propos de l\'expert',
education: 'Éducation', education: 'Éducation',
coaching: 'Coaching', coaching: 'Profil de l\'expert',
experiences: 'Expérience pratique', experiences: 'Expérience pratique',
payInfo: 'Infos sur le paiement', payInfo: 'Infos sur le paiement',
sessionDuration: 'Durée de la session', sessionDuration: 'Durée de la session',
@ -146,6 +146,8 @@ export default {
saturday: 'Sa', saturday: 'Sa',
addNew: 'Ajouter un nouveau', addNew: 'Ajouter un nouveau',
mExperiences: 'Expérience en gestion', mExperiences: 'Expérience en gestion',
pay: 'Pay',
sessionWishes: 'Write your wishes about the session',
errors: { errors: {
invalidEmail: 'L\'adresse e-mail n\'est pas valide', invalidEmail: 'L\'adresse e-mail n\'est pas valide',
emptyEmail: 'Veuillez saisir votre e-mail', emptyEmail: 'Veuillez saisir votre e-mail',

View File

@ -42,7 +42,7 @@ export default {
addComment: 'Aggiungi nuovo commento', addComment: 'Aggiungi nuovo commento',
commentPlaceholder: 'Il tuo commento', commentPlaceholder: 'Il tuo commento',
clientComments: 'Commenti del cliente', clientComments: 'Commenti del cliente',
coachComments: 'Commenti dell\'allenatore' coachComments: 'Commenti dell\'esperto'
}, },
room: { room: {
upcoming: 'Prossime sale', upcoming: 'Prossime sale',
@ -110,9 +110,9 @@ export default {
seminars: 'Seminari', seminars: 'Seminari',
courses: 'Corsi', courses: 'Corsi',
mba: 'Info sull\'MBA', mba: 'Info sull\'MBA',
aboutCoach: 'Informazioni sul coach', aboutCoach: 'Informazioni sull\'esperto',
education: 'Istruzione', education: 'Istruzione',
coaching: 'Coaching', coaching: 'Profilo dell\'esperto',
experiences: 'Esperienza pratica', experiences: 'Esperienza pratica',
payInfo: 'Info pagamento', payInfo: 'Info pagamento',
sessionDuration: 'Durata della sessione', sessionDuration: 'Durata della sessione',
@ -146,6 +146,8 @@ export default {
saturday: 'Sa', saturday: 'Sa',
addNew: 'Aggiungi nuovo', addNew: 'Aggiungi nuovo',
mExperiences: 'Esperienza manageriale', mExperiences: 'Esperienza manageriale',
pay: 'Pay',
sessionWishes: 'Write your wishes about the session',
errors: { errors: {
invalidEmail: 'L\'indirizzo e-mail non è valido', invalidEmail: 'L\'indirizzo e-mail non è valido',
emptyEmail: 'Inserisci l\'e-mail', emptyEmail: 'Inserisci l\'e-mail',

View File

@ -42,7 +42,7 @@ export default {
addComment: 'Добавить новый', addComment: 'Добавить новый',
commentPlaceholder: 'Ваш комментарий', commentPlaceholder: 'Ваш комментарий',
clientComments: 'Комментарии клиента', clientComments: 'Комментарии клиента',
coachComments: 'Комментарии коуча' coachComments: 'Комментарии эксперта'
}, },
room: { room: {
upcoming: 'Предстоящие комнаты', upcoming: 'Предстоящие комнаты',
@ -111,9 +111,9 @@ export default {
courses: 'Курсы', courses: 'Курсы',
mba: 'Информация о MBA', mba: 'Информация о MBA',
experiences: 'Практический опыт', experiences: 'Практический опыт',
aboutCoach: 'О коуче', aboutCoach: 'Информация об эксперте',
education: 'Образование', education: 'Образование',
coaching: 'Коучинг', coaching: 'Профиль эксперта',
payInfo: 'Платежная информация', payInfo: 'Платежная информация',
sessionDuration: 'Продолжительность сессии', sessionDuration: 'Продолжительность сессии',
experienceHours: 'Общее количество часов практического опыта', experienceHours: 'Общее количество часов практического опыта',
@ -146,6 +146,8 @@ export default {
saturday: 'Сб', saturday: 'Сб',
addNew: 'Добавить', addNew: 'Добавить',
mExperiences: 'Управленческий опыт', mExperiences: 'Управленческий опыт',
pay: 'Pay',
sessionWishes: 'Write your wishes about the session',
errors: { errors: {
invalidEmail: 'Адрес электронной почты недействителен', invalidEmail: 'Адрес электронной почты недействителен',
emptyEmail: 'Пожалуйста, введите ваш E-mail', emptyEmail: 'Пожалуйста, введите ваш E-mail',

View File

@ -0,0 +1,62 @@
.b-calendar {
padding: 44px 40px !important;
&-month {
text-transform: capitalize;
}
&-header {
justify-content: center;
border-bottom: none !important;
}
&-cell {
span {
color: #66A5AD;
}
&__weekend {
span {
color: #FFBD00;
}
}
}
.ant-picker-body {
margin-bottom: -42px !important;
}
.ant-picker-panel {
border-top: none !important;
margin-top: 12px;
}
.ant-picker-cell {
opacity: 0 !important;
&-disabled {
&::before {
background: transparent !important;
}
span {
color: rgba(0, 0, 0, 0.25) !important;
}
}
&.ant-picker-cell-in-view {
opacity: 1 !important;
background: transparent !important;
}
}
th, td {
vertical-align: middle !important;
height: 40px !important;
}
th {
color: #66A5AD !important;
}
}

View File

@ -0,0 +1,26 @@
.b-schedule {
&-time {
padding: 44px 40px;
display: flex;
flex-direction: column;
gap: 24px;
.b-button-link-big {
font-size: 24px;
line-height: 32px;
color: #6FB98F !important;
font-family: var(--font-comfortaa);
padding: 0 !important;
border: none !important;
text-transform: capitalize;
}
}
&-radio-list {
.ant-radio-group {
display: flex;
flex-direction: column;
gap: 12px;
}
}
}

View File

@ -9,3 +9,5 @@
@import "_practice.scss"; @import "_practice.scss";
@import "_collapse.scss"; @import "_collapse.scss";
@import "_timepicker.scss"; @import "_timepicker.scss";
@import "_calendar.scss";
@import "_schedule.scss";

View File

@ -71,28 +71,23 @@ export type ExpertDetails = {
associationLevels?: AssociationLevel[]; associationLevels?: AssociationLevel[];
}; };
export type Tags = {
id: number,
groupId: number,
name: string,
couchCount: number,
group: {
id: number,
name: string,
tags: string[];
}
}
export type Slot = { export type Slot = {
startTime: string; startTime: string;
endTime: string; endTime: string;
} }
export type ExpertScheduler = { export type ExpertScheduler = {
tags: Tags[], tags: Tag[],
availableSlots: Slot[]; availableSlots: Slot[];
} }
export type ExpertSchedulerSession = { export type ExpertSchedulerSession = {
sessionId: string sessionId: string
} };
export type SignupSessionData = {
coachId: number,
tagId?: number,
startAtUtc?: string,
clientComment?: string
};