feat: add sessions page, add delete modal, refactoring
This commit is contained in:
parent
ffbbeadb40
commit
c2e29cbef3
|
@ -11,6 +11,7 @@
|
|||
"@ant-design/cssinjs": "^1.18.1",
|
||||
"@ant-design/icons": "^5.2.6",
|
||||
"@ant-design/nextjs-registry": "^1.0.0",
|
||||
"agora-rtc-react": "^2.1.0",
|
||||
"antd": "^5.12.1",
|
||||
"antd-img-crop": "^4.21.0",
|
||||
"axios": "^1.6.5",
|
||||
|
@ -873,6 +874,17 @@
|
|||
"acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/agora-rtc-react": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/agora-rtc-react/-/agora-rtc-react-2.1.0.tgz",
|
||||
"integrity": "sha512-3FGteA7FG51oK5MusbYNgAcKZaAQK+4sbEz4F0DPzcpDxqNANpocJDqOsmXoUAj5yDBsBZelmagU3abd++6RGA==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
|
@ -5947,6 +5959,12 @@
|
|||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"agora-rtc-react": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/agora-rtc-react/-/agora-rtc-react-2.1.0.tgz",
|
||||
"integrity": "sha512-3FGteA7FG51oK5MusbYNgAcKZaAQK+4sbEz4F0DPzcpDxqNANpocJDqOsmXoUAj5yDBsBZelmagU3abd++6RGA==",
|
||||
"requires": {}
|
||||
},
|
||||
"ajv": {
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
"@ant-design/cssinjs": "^1.18.1",
|
||||
"@ant-design/icons": "^5.2.6",
|
||||
"@ant-design/nextjs-registry": "^1.0.0",
|
||||
"agora-rtc-react": "^2.1.0",
|
||||
"antd": "^5.12.1",
|
||||
"antd-img-crop": "^4.21.0",
|
||||
"axios": "^1.6.5",
|
||||
|
|
|
@ -73,3 +73,16 @@ export const getRecentSessions = (locale: string, jwt: string, filter?: Sessions
|
|||
}
|
||||
)
|
||||
);
|
||||
|
||||
export const getSessionDetails = (locale: string, jwt: string, id: number): Promise<AxiosResponse<Session>> => (
|
||||
apiClient.post(
|
||||
'/home/session',
|
||||
{ id },
|
||||
{
|
||||
headers: {
|
||||
'X-User-Language': locale,
|
||||
Authorization: `Bearer ${jwt}`
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { Suspense } from 'react';
|
||||
import type { Metadata } from 'next';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { SessionsTabs } from '../../../../../components/Account';
|
||||
import { SessionsAll } from '../../../../../components/Account';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Bbuddy - Account - Sessions',
|
||||
|
@ -13,9 +13,7 @@ export default function Sessions({ params: { locale } }: { params: { locale: str
|
|||
|
||||
return (
|
||||
<Suspense fallback={<p>Loading...</p>}>
|
||||
<SessionsTabs
|
||||
locale={locale}
|
||||
/>
|
||||
<SessionsAll locale={locale} />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Button } from 'antd';
|
||||
import { useSelectedLayoutSegment, usePathname } from 'next/navigation';
|
||||
|
@ -8,6 +8,7 @@ import { Link } from '../../navigation';
|
|||
import { AUTH_TOKEN_KEY, AUTH_USER } from '../../constants/common';
|
||||
import { deleteStorageKey } from '../../hooks/useLocalStorage';
|
||||
import { i18nText } from '../../i18nKeys';
|
||||
import { DeleteAccountModal } from '../Modals/DeleteAccountModal';
|
||||
|
||||
const Logout = styled(Button)`
|
||||
width: 100%;
|
||||
|
@ -24,6 +25,7 @@ export const AccountMenu = ({ menu, locale }: { menu: { path: string, title: str
|
|||
const selectedLayoutSegment = useSelectedLayoutSegment();
|
||||
const pathname = selectedLayoutSegment || '';
|
||||
const paths = usePathname();
|
||||
const [showDeleteModal, setShowDeleteModal] = useState<boolean>(false);
|
||||
|
||||
const onLogout = () => {
|
||||
deleteStorageKey(AUTH_TOKEN_KEY);
|
||||
|
@ -31,9 +33,7 @@ export const AccountMenu = ({ menu, locale }: { menu: { path: string, title: str
|
|||
window?.location?.replace(`/${paths.split('/')[1]}/`);
|
||||
};
|
||||
|
||||
const onDeleteAccount = () => {
|
||||
console.log('delete');
|
||||
};
|
||||
const onDeleteAccount = () => setShowDeleteModal(true);
|
||||
|
||||
return (
|
||||
<ul className="list-sidebar">
|
||||
|
@ -62,6 +62,10 @@ export const AccountMenu = ({ menu, locale }: { menu: { path: string, title: str
|
|||
>
|
||||
{i18nText('deleteAcc', locale)}
|
||||
</Logout>
|
||||
<DeleteAccountModal
|
||||
open={showDeleteModal}
|
||||
handleCancel={() => setShowDeleteModal(false)}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
'use client'
|
||||
|
||||
export { AccountMenu } from './AccountMenu';
|
||||
export { SessionsTabs } from './SessionsTabs';
|
||||
export { ProfileSettings } from './ProfileSettings';
|
||||
export * from './sessions';
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
'use client'
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Tag } from 'antd';
|
||||
import { RightOutlined } from '@ant-design/icons';
|
||||
import Image from 'next/image';
|
||||
import dayjs from 'dayjs';
|
||||
import { Link } from '../../../navigation';
|
||||
import { i18nText } from '../../../i18nKeys';
|
||||
import { getDuration, getPrice } from '../../../utils/expert';
|
||||
import { Session } from '../../../types/sessions';
|
||||
import { AUTH_TOKEN_KEY, AUTH_USER } from '../../../constants/common';
|
||||
import { getSessionDetails } from '../../../actions/profile';
|
||||
import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
||||
import { Loader } from '../../view/Loader';
|
||||
|
||||
type SessionDetailsProps = {
|
||||
locale: string;
|
||||
sessionId: number;
|
||||
goBack: () => void;
|
||||
};
|
||||
|
||||
export const SessionDetails = ({ sessionId, locale, goBack }: SessionDetailsProps) => {
|
||||
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||||
const [userId] = useLocalStorage(AUTH_USER, '');
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [errorData, setErrorData] = useState<any>();
|
||||
const [session, setSession] = useState<Session>();
|
||||
|
||||
const fetchData = useCallback(() => {
|
||||
setLoading(true);
|
||||
setErrorData(undefined);
|
||||
setSession(undefined);
|
||||
|
||||
getSessionDetails(locale, jwt, sessionId)
|
||||
.then(({ data }) => {
|
||||
console.log(data);
|
||||
setSession(data);
|
||||
})
|
||||
.catch((err) => {
|
||||
setErrorData(err);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
})
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [sessionId]);
|
||||
|
||||
const client = session?.clients?.length ? session?.clients[0] : null;
|
||||
const current = +userId !== client?.id ? client : session?.coach;
|
||||
const startDate = session?.scheduledStartAtUtc ? dayjs(session?.scheduledStartAtUtc).locale(locale) : null;
|
||||
const endDate = session?.scheduledEndAtUtc ? dayjs(session?.scheduledEndAtUtc).locale(locale) : null;
|
||||
const today = startDate ? dayjs().format('YYYY-MM-DD') === startDate.format('YYYY-MM-DD') : false;
|
||||
|
||||
return (
|
||||
<Loader
|
||||
isLoading={loading}
|
||||
errorData={errorData}
|
||||
refresh={fetchData}
|
||||
>
|
||||
<div className="card-detail">
|
||||
<div>
|
||||
<button onClick={goBack}>back</button>
|
||||
</div>
|
||||
<div className="card-detail__expert">
|
||||
<div className="card-detail__portrait">
|
||||
<Image src={current?.faceImageUrl || '/images/person.png'} width={140} height={140} alt="" />
|
||||
</div>
|
||||
<div className="card-detail__inner">
|
||||
<Link href={`/experts/${current?.id}` as any} target="_blank">
|
||||
<div className="card-detail__name">{`${current?.name} ${current?.surname || ''}`}</div>
|
||||
</Link>
|
||||
<div className="card-detail__info">
|
||||
<div className="card-detail__lang">
|
||||
{/* current?.coachLanguages?.map((lang) => (
|
||||
<Tag key={lang} className="skills__list__item">{lang}</Tag>
|
||||
)) */}
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="card-profile__title">{current?.speciality}</div> */}
|
||||
<div className="card-detail__coast">
|
||||
{getPrice(session?.cost)} <span>/ {getDuration(locale, session?.totalDuration)}</span>
|
||||
</div>
|
||||
<div className={`card-detail__date${today ? ' chosen': ''}`}>
|
||||
{today
|
||||
? `${i18nText('today', locale)} ${startDate.format('HH:mm')} - ${endDate.format('HH:mm')}`
|
||||
: `${startDate.format('D MMMM')} ${startDate.format('HH:mm')} - ${endDate.format('HH:mm')}`}
|
||||
</div>
|
||||
<div className="card-detail__skills">
|
||||
<div className="skills__list">
|
||||
{session?.themesTags?.slice(0, 2).map((skill) => <Tag key={skill?.id} className="skills__list__item">{skill?.name}</Tag>)}
|
||||
{session?.themesTags?.length > 2
|
||||
? (
|
||||
<Tag className="skills__list__more">
|
||||
<Link href={`/experts/${current?.id}` as any} target="_blank">
|
||||
{`+${session?.themesTags?.length - 2}`}
|
||||
</Link>
|
||||
</Tag>
|
||||
) : null }
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="card-profile__subtitle">{current?.specialityDesc}</div>
|
||||
<div className="card-profile__desc">{current?.description}</div> */}
|
||||
<Link href={`/experts/${current?.id}` as any} target="_blank">
|
||||
{i18nText('details', locale)}
|
||||
<RightOutlined style={{ fontSize: '10px', padding: '0 7px' }}/>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card-detail__actions">
|
||||
<button>Start Session</button>
|
||||
<button>Decline Session</button>
|
||||
</div>
|
||||
<div className="card-detail__comments">
|
||||
<div className="card-detail__comments_header">
|
||||
<div className="card-detail__comments_title">
|
||||
My Comments
|
||||
</div>
|
||||
<button>Add new</button>
|
||||
</div>
|
||||
<div className="card-detail__comments_item">
|
||||
Sed tincidunt finibus eros nec feugiat. Nulla facilisi. Nunc maximus magna et egestas tincidunt. Integer lobortis laoreet neque at sodales. Aenean eget risus pharetra, efficitur dolor ut, commodo lacus. Sed vitae nunc odio. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis et velit et dolor rutrum euismod a pretium est.
|
||||
</div>
|
||||
<div className="card-detail__comments_title">
|
||||
Coach Comments
|
||||
</div>
|
||||
<div className="card-detail__comments_item">
|
||||
Sed tincidunt finibus eros nec feugiat. Nulla facilisi. Nunc maximus magna et egestas tincidunt. Integer lobortis laoreet neque at sodales. Aenean eget risus pharetra, efficitur dolor ut, commodo lacus. Sed vitae nunc odio. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis et velit et dolor rutrum euismod a pretium est.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Loader>
|
||||
);
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState, MouseEvent } from 'react';
|
||||
import { Empty, Space } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import 'dayjs/locale/ru';
|
||||
|
@ -9,14 +9,19 @@ import 'dayjs/locale/de';
|
|||
import 'dayjs/locale/it';
|
||||
import 'dayjs/locale/fr';
|
||||
import 'dayjs/locale/es';
|
||||
import { Loader } from '../view/Loader';
|
||||
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
||||
import { AUTH_TOKEN_KEY, AUTH_USER } from '../../constants/common';
|
||||
import { getRecentSessions, getRequestedSessions, getUpcomingSessions } from '../../actions/profile';
|
||||
import { Session, Sessions, SessionType } from '../../types/sessions';
|
||||
import { i18nText } from '../../i18nKeys';
|
||||
import { Loader } from '../../view/Loader';
|
||||
import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
||||
import { AUTH_TOKEN_KEY, AUTH_USER } from '../../../constants/common';
|
||||
import { getRecentSessions, getRequestedSessions, getUpcomingSessions } from '../../../actions/profile';
|
||||
import { Session, Sessions, SessionType } from '../../../types/sessions';
|
||||
import { i18nText } from '../../../i18nKeys';
|
||||
|
||||
export const SessionsTabs = ({ locale }: { locale: string }) => {
|
||||
type SessionsTabsProps = {
|
||||
locale: string;
|
||||
updateSession: (val: number) => void;
|
||||
};
|
||||
|
||||
export const SessionsTabs = ({ locale, updateSession }: SessionsTabsProps) => {
|
||||
const [activeTab, setActiveTab] = useState<number>(0);
|
||||
const [sort, setSort] = useState<string>();
|
||||
const [sessions, setSessions] = useState<Sessions>();
|
||||
|
@ -56,6 +61,12 @@ export const SessionsTabs = ({ locale }: { locale: string }) => {
|
|||
setSort(value);
|
||||
}, [sort]);
|
||||
|
||||
const onClickSession = (event: MouseEvent<HTMLDivElement>, id: number) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
updateSession(id);
|
||||
};
|
||||
|
||||
const getChildren = (list?: Session[]) => (
|
||||
<>
|
||||
{/* <div className="filter-session">
|
||||
|
@ -82,7 +93,7 @@ export const SessionsTabs = ({ locale }: { locale: string }) => {
|
|||
const today = dayjs().format('YYYY-MM-DD') === startDate.format('YYYY-MM-DD');
|
||||
|
||||
return (
|
||||
<div key={id} className="card-profile session__item">
|
||||
<div key={id} className="card-profile session__item" onClick={(e: MouseEvent<HTMLDivElement>) => onClickSession(e, id)}>
|
||||
<div className="card-profile__header">
|
||||
<div className="card-profile__header__portrait">
|
||||
<img src={current?.faceImageUrl || '/images/person.png'} className="" alt="" />
|
|
@ -0,0 +1,22 @@
|
|||
'use client'
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { SessionDetails } from './SessionDetails';
|
||||
import { SessionsTabs } from './SessionsTabs';
|
||||
|
||||
export const SessionsAll = ({ locale }: { locale: string }) => {
|
||||
const [customSession, setCustomSession] = useState<number | undefined>();
|
||||
|
||||
return customSession ? (
|
||||
<SessionDetails
|
||||
locale={locale}
|
||||
sessionId={customSession}
|
||||
goBack={() => setCustomSession(undefined)}
|
||||
/>
|
||||
) : (
|
||||
<SessionsTabs
|
||||
locale={locale}
|
||||
updateSession={setCustomSession}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -9,6 +9,7 @@ import Image from 'next/image';
|
|||
import { Link, useRouter } from '../../navigation';
|
||||
import { ExpertsData, Filter, GeneralFilter } from '../../types/experts';
|
||||
import { getObjectByFilter, getObjectByAdditionalFilter } from '../../utils/filter';
|
||||
import { getDuration, getPrice } from '../../utils/expert';
|
||||
import { getExpertsList } from '../../actions/experts';
|
||||
import { CustomPagination } from '../view/CustomPagination';
|
||||
import { CustomSpin } from '../view/CustomSpin';
|
||||
|
@ -31,8 +32,6 @@ export const ExpertsList = ({
|
|||
}: ExpertListProps) => {
|
||||
const searchParams = useSearchParams();
|
||||
const router = useRouter();
|
||||
const getDuration = (value?: any): string => `${value || 0}${locale === 'ru' ? 'мин' : 'min'}`;
|
||||
const getPrice = (value?: any): string => `${value || 0}€`;
|
||||
const [experts, setExperts] = useState<ExpertsData | undefined>();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
|
@ -102,7 +101,7 @@ export const ExpertsList = ({
|
|||
<div className="card-profile__header__name">{`${item.name} ${item?.surname || ''}`}</div>
|
||||
</Link>
|
||||
<div className="card-profile__header__price">
|
||||
{getPrice(item?.sessionCost)} <span>/ {getDuration(item?.sessionDuration)}</span>
|
||||
{getPrice(item?.sessionCost)} <span>/ {getDuration(locale, item?.sessionDuration)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card-profile__header__lang">
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
import React, { Dispatch, FC, SetStateAction, useEffect } from 'react';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { Modal as AntdModal, Form } from 'antd';
|
||||
import { Modal, Form } from 'antd';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import { styled } from 'styled-components';
|
||||
import { RegisterContent, ResetContent, FinishContent, EnterContent } from './authModalContent';
|
||||
|
||||
type AuthModalProps = {
|
||||
|
@ -16,25 +15,6 @@ type AuthModalProps = {
|
|||
updateToken: string | Dispatch<SetStateAction<string | undefined>> | undefined;
|
||||
};
|
||||
|
||||
const Modal = styled(AntdModal)`
|
||||
.ant-modal-content {
|
||||
border-radius: 24px !important;
|
||||
}
|
||||
|
||||
.ant-modal-close {
|
||||
height: 64px !important;
|
||||
width: 64px !important;
|
||||
border-radius: 50% !important;
|
||||
background: #fff !important;
|
||||
top: -32px !important;
|
||||
inset-inline-end: -32px !important;
|
||||
|
||||
&:active, &:hover {
|
||||
background: #fff !important;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const AuthModal: FC<AuthModalProps> = ({
|
||||
open,
|
||||
handleCancel,
|
||||
|
@ -63,6 +43,7 @@ export const AuthModal: FC<AuthModalProps> = ({
|
|||
|
||||
return (
|
||||
<Modal
|
||||
className="b-modal"
|
||||
open={open}
|
||||
title={undefined}
|
||||
onOk={undefined}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
'use client';
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { Modal } from 'antd';
|
||||
import { CloseOutlined } from '@ant-design/icons';
|
||||
import Link from 'next/link';
|
||||
|
||||
type DeleteAccountModalProps = {
|
||||
open: boolean;
|
||||
handleCancel: () => void;
|
||||
};
|
||||
|
||||
export const DeleteAccountModal: FC<DeleteAccountModalProps> = ({
|
||||
open,
|
||||
handleCancel
|
||||
}) => (
|
||||
<Modal
|
||||
className="b-modal"
|
||||
open={open}
|
||||
title="Account deletion instructions"
|
||||
onOk={undefined}
|
||||
onCancel={handleCancel}
|
||||
footer={false}
|
||||
width={498}
|
||||
closeIcon={<CloseOutlined style={{ fontSize: 20, color: '#000' }}/>}
|
||||
>
|
||||
<div className="b-modal__content">
|
||||
<p>
|
||||
To delete your BBUDDY account, please send an email requesting account deletion to the following email address:
|
||||
</p>
|
||||
<Link href="mailto:info@bbuddy.expert">info@bbuddy.expert</Link>
|
||||
<p>
|
||||
Upon receiving the request, we will delete all existing data associated with your account within 24 hours.
|
||||
</p>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
|
@ -586,7 +586,6 @@ a {
|
|||
line-height: 160%;
|
||||
}
|
||||
|
||||
//
|
||||
.card-profile {
|
||||
display: flex !important;
|
||||
flex-direction: column;
|
||||
|
@ -598,6 +597,7 @@ a {
|
|||
&.session {
|
||||
&__item {
|
||||
margin: 0 !important;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
.b-modal {
|
||||
.ant-modal-content {
|
||||
border-radius: 24px !important;
|
||||
}
|
||||
|
||||
.ant-modal-close {
|
||||
height: 64px !important;
|
||||
width: 64px !important;
|
||||
border-radius: 50% !important;
|
||||
background: #fff !important;
|
||||
top: -32px !important;
|
||||
inset-inline-end: -32px !important;
|
||||
|
||||
&:active, &:hover {
|
||||
background: #fff !important;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
p {
|
||||
margin: 16px 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -767,9 +767,9 @@
|
|||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px 8px 4px 8px;
|
||||
padding: 6px 8px 4px;
|
||||
border-radius: 16px;
|
||||
background: #66A5AD;
|
||||
background: #FFBD00;
|
||||
color: $white;
|
||||
@include rem(12);
|
||||
font-style: normal;
|
||||
|
|
|
@ -0,0 +1,149 @@
|
|||
.card-detail {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
&__expert {
|
||||
border-block-end: 1px solid #C4DFE6;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
padding: 0 0 16px;
|
||||
}
|
||||
|
||||
&__portrait {
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
border-radius: 16px;
|
||||
border: 2px solid #FFF;
|
||||
background: lightgray 50%;
|
||||
box-shadow: 0 8px 16px 0 rgba(102, 165, 173, 0.32);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
flex: 1 0 0;
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&__comments {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
|
||||
&_header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&_title {
|
||||
color: #6FB98F;
|
||||
@include rem(18);
|
||||
font-weight: 600;
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
&_item {
|
||||
padding: 16px;
|
||||
background: #E4F5FA;
|
||||
border-radius: 0 16px 16px 16px;
|
||||
color: #66A5AD;
|
||||
@include rem(13);
|
||||
font-weight: 500;
|
||||
line-height: 133.333%;
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
padding-bottom: 8px;
|
||||
align-items: center !important;
|
||||
gap: 16px;
|
||||
align-self: stretch;
|
||||
margin-block-end: 0 !important;
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
&__name {
|
||||
overflow: hidden;
|
||||
color: #003B46;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@include rem(18);
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 133.333%;
|
||||
}
|
||||
|
||||
&__price {
|
||||
color: #6FB98F;
|
||||
@include rem(15);
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 120%;
|
||||
|
||||
span {
|
||||
color: #B7B7B7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
color: #003B46;
|
||||
@include rem(18);
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 133.333%;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
color: #003B46;
|
||||
@include rem(15);
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
&__desc {
|
||||
color: #66A5AD;
|
||||
@include rem(13);
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 123.077%;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #FF8A00 !important;
|
||||
@include rem(15);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 160%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
@import "_details.scss";
|
|
@ -14,8 +14,10 @@
|
|||
@import "_form.scss";
|
||||
@import "_message.scss";
|
||||
@import "_auth-modal.scss";
|
||||
@import "_modal.scss";
|
||||
|
||||
@import "./view/style.scss";
|
||||
@import "./sessions/style.scss";
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
type User = {
|
||||
import { Language } from './tags';
|
||||
|
||||
type PublicUser = {
|
||||
id: number;
|
||||
login?: string;
|
||||
name?: string;
|
||||
|
@ -6,6 +8,88 @@ type User = {
|
|||
faceImageUrl?: string;
|
||||
};
|
||||
|
||||
// type User = {
|
||||
// "id": 0,
|
||||
// "login": "string",
|
||||
// "password": "string",
|
||||
// "role": "coach",
|
||||
// "name": "string",
|
||||
// "surname": "string",
|
||||
// "phone": "string",
|
||||
// "faceImageId": 0,
|
||||
// "createdAtUtc": "2024-05-15T14:26:19.310Z",
|
||||
// "practiceHours": 0,
|
||||
// "supervisionPerYearId": 0,
|
||||
// "sessionDuration": 0,
|
||||
// "sessionCost": 0,
|
||||
// "adminRoles": "admin",
|
||||
// "isCoachApproved": true,
|
||||
// "isBanned": true,
|
||||
// "faceImage": {
|
||||
// "id": 0,
|
||||
// "userId": 0,
|
||||
// "descriptor": "string",
|
||||
// "fileType": "string",
|
||||
// "contentLength": 0,
|
||||
// "state": "active",
|
||||
// "createdAtUtc": "2024-05-15T14:26:19.310Z",
|
||||
// "unusedSinceUtc": "2024-05-15T14:26:19.310Z"
|
||||
// },
|
||||
// "coachRating": 0,
|
||||
// "isTestMode": true,
|
||||
// "beneficiaryName": "string",
|
||||
// "beneficiaryIban": "string",
|
||||
// "beneficiaryBicOrSwift": "string",
|
||||
// "userThemesTags": [
|
||||
// {
|
||||
// "userId": 0,
|
||||
// "themesTagId": 0,
|
||||
// "themesTag": {
|
||||
// "id": 0,
|
||||
// "groupId": 0,
|
||||
// "name": "string",
|
||||
// "isActive": true,
|
||||
// "userId": 0,
|
||||
// "group": {
|
||||
// "id": 0,
|
||||
// "name": "string",
|
||||
// "isActive": true,
|
||||
// "userId": 0,
|
||||
// "tags": [
|
||||
// "string"
|
||||
// ],
|
||||
// "multilangs": [
|
||||
// {
|
||||
// "parentId": 0,
|
||||
// "languageCode": "string",
|
||||
// "name": "string"
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// "userThemesTags": [
|
||||
// "string"
|
||||
// ],
|
||||
// "multilangs": [
|
||||
// {
|
||||
// "parentId": 0,
|
||||
// "languageCode": "string",
|
||||
// "name": "string"
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// "user": "string"
|
||||
// }
|
||||
// ],
|
||||
// "languagesLinks": [
|
||||
// {
|
||||
// "userId": 0,
|
||||
// "languageId": 0,
|
||||
// "user": "string",
|
||||
// "language": Language
|
||||
// }
|
||||
// ]
|
||||
// };
|
||||
|
||||
type SessionTag = {
|
||||
id: number;
|
||||
groupId?: number;
|
||||
|
@ -21,6 +105,15 @@ export type SessionsFilter = {
|
|||
endDate?: string;
|
||||
};
|
||||
|
||||
export type SessionComment = {
|
||||
id: number;
|
||||
createdAtUtc: string;
|
||||
comment?: string;
|
||||
author?: PublicUser;
|
||||
authorId?: number;
|
||||
sessionId?: number;
|
||||
};
|
||||
|
||||
export type Session = {
|
||||
id: number;
|
||||
scheduledStartAtUtc?: string;
|
||||
|
@ -37,11 +130,13 @@ export type Session = {
|
|||
description?: string;
|
||||
isNeedSupervisor?: boolean;
|
||||
supervisorComment?: string;
|
||||
user?: User;
|
||||
coach?: User;
|
||||
supervisor?: User;
|
||||
clients?: User[];
|
||||
themesTags?: SessionTag[]
|
||||
user?: PublicUser;
|
||||
coach?: PublicUser;
|
||||
supervisor?: PublicUser;
|
||||
clients?: PublicUser[];
|
||||
themesTags?: SessionTag[];
|
||||
coachComments?: SessionComment[];
|
||||
clientComments?: SessionComment[];
|
||||
};
|
||||
|
||||
export enum SessionType {
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export const getDuration = (locale: string, value?: any): string => `${value || 0}${locale === 'ru' ? 'мин' : 'min'}`;
|
||||
|
||||
export const getPrice = (value?: any): string => `${value || 0}€`;
|
Loading…
Reference in New Issue