Compare commits
No commits in common. "9a3aa9815842d06ffd489cca923b06b75c311d5d" and "5b8ba1b5c45675af91decb76b2f28471c3a1a302" have entirely different histories.
9a3aa98158
...
5b8ba1b5c4
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "bbuddy-ui",
|
"name": "bbuddy-ui",
|
||||||
"version": "0.2.0",
|
"version": "0.1.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bbuddy-ui",
|
"name": "bbuddy-ui",
|
||||||
"version": "0.2.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/cssinjs": "^1.18.1",
|
"@ant-design/cssinjs": "^1.18.1",
|
||||||
"@ant-design/icons": "^5.2.6",
|
"@ant-design/icons": "^5.2.6",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "bbuddy-ui",
|
"name": "bbuddy-ui",
|
||||||
"version": "0.2.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -p 4200",
|
"dev": "next dev -p 4200",
|
||||||
|
|
|
@ -1,42 +0,0 @@
|
||||||
'use client'
|
|
||||||
|
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
|
||||||
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
|
||||||
import { AUTH_TOKEN_KEY } from '../../constants/common';
|
|
||||||
import { Room } from '../../types/rooms';
|
|
||||||
import { getRoomDetails } from '../rooms';
|
|
||||||
|
|
||||||
export const useRoomDetails = (locale: string, roomId: number) => {
|
|
||||||
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
|
||||||
const [room, setRoom] = useState<Room>();
|
|
||||||
const [errorData, setErrorData] = useState<any>();
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const fetchData = useCallback(() => {
|
|
||||||
setLoading(true);
|
|
||||||
setErrorData(undefined);
|
|
||||||
setRoom(undefined);
|
|
||||||
|
|
||||||
getRoomDetails(locale, jwt, roomId)
|
|
||||||
.then((room) => {
|
|
||||||
setRoom(room);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
setErrorData(err);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false);
|
|
||||||
})
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return {
|
|
||||||
fetchData,
|
|
||||||
loading,
|
|
||||||
room,
|
|
||||||
errorData
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,109 +0,0 @@
|
||||||
import { apiRequest } from './helpers';
|
|
||||||
import {GetUsersForRooms, Room, RoomEdit, RoomEditDTO} from '../types/rooms';
|
|
||||||
|
|
||||||
export const getUpcomingRooms = (locale: string, token: string): Promise<Room[]> => apiRequest({
|
|
||||||
url: '/home/upcomingsessionsall',
|
|
||||||
method: 'post',
|
|
||||||
data: {
|
|
||||||
sessionType: 'room'
|
|
||||||
},
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getRecentRooms = (locale: string, token: string): Promise<Room[]> => apiRequest({
|
|
||||||
url: '/home/historicalmeetings',
|
|
||||||
method: 'post',
|
|
||||||
data: {
|
|
||||||
sessionType: 'room'
|
|
||||||
},
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getRoomDetails = (locale: string, token: string, id: number): Promise<Room> => apiRequest({
|
|
||||||
url: '/home/room',
|
|
||||||
method: 'post',
|
|
||||||
data: { id },
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
export const deleteRoomClient = (locale: string, token: string, data: { sessionId: number, clientUserId: number }): Promise<any> => apiRequest({
|
|
||||||
url: '/home/deleteclientfromroom',
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
export const deleteRoomSupervisor = (locale: string, token: string, data: { sessionId: number, supervisorUserId: number }): Promise<any> => apiRequest({
|
|
||||||
url: '/home/deletesupervisorfromroom',
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
export const becomeRoomClient = (locale: string, token: string, data: { sessionId: number, clientUserId: number }): Promise<any> => apiRequest({
|
|
||||||
url: '/home/becomeroomclient',
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
export const becomeRoomSupervisor = (locale: string, token: string, data: { sessionId: number, supervisorUserId: number }): Promise<any> => apiRequest({
|
|
||||||
url: '/home/becomeroomsupervisor',
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getUsersList = (locale: string, token: string, data: { template: string }): Promise<GetUsersForRooms> => apiRequest({
|
|
||||||
url: '/home/findusersforroom',
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
export const addClient = (locale: string, token: string, data: { sessionId: number, clientUserId: number }): Promise<any> => apiRequest({
|
|
||||||
url: '/home/addclienttoroom',
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
export const addSupervisor = (locale: string, token: string, data: { sessionId: number, supervisorUserId: number }): Promise<any> => apiRequest({
|
|
||||||
url: '/home/addsupervisortoroom',
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
export const createRoom = (locale: string, token: string): Promise<any> => apiRequest({
|
|
||||||
url: '/home/createroom',
|
|
||||||
method: 'post',
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
export const updateRoom = (locale: string, token: string, data: RoomEdit): Promise<any> => apiRequest({
|
|
||||||
url: '/home/updateroom',
|
|
||||||
method: 'post',
|
|
||||||
data,
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
||||||
|
|
||||||
export const getRoomById = (locale: string, token: string, id: number): Promise<RoomEditDTO> => apiRequest({
|
|
||||||
url: '/home/getroomforedit',
|
|
||||||
method: 'post',
|
|
||||||
data: { id },
|
|
||||||
locale,
|
|
||||||
token
|
|
||||||
});
|
|
|
@ -1,57 +0,0 @@
|
||||||
import React, { Suspense } from 'react';
|
|
||||||
import { unstable_setRequestLocale } from 'next-intl/server';
|
|
||||||
import { notFound } from 'next/navigation';
|
|
||||||
import { AccountMenu, RoomDetails, RoomsTabs } from '../../../../../../components/Account';
|
|
||||||
import { RoomsType } from '../../../../../../types/rooms';
|
|
||||||
|
|
||||||
const ROOMS_ROUTES = [RoomsType.UPCOMING, RoomsType.RECENT, RoomsType.NEW];
|
|
||||||
|
|
||||||
export async function generateStaticParams({
|
|
||||||
params: { locale },
|
|
||||||
}: { params: { locale: string } }) {
|
|
||||||
return [{ locale, slug: [RoomsType.UPCOMING] }];
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function RoomsDetailItem({ params: { locale, slug } }: { params: { locale: string, slug?: string[] } }) {
|
|
||||||
unstable_setRequestLocale(locale);
|
|
||||||
const roomType: string = slug?.length > 0 && slug[0] || '';
|
|
||||||
const roomId: number | null = slug?.length > 1 && Number(slug[1]) || null;
|
|
||||||
|
|
||||||
if (!slug?.length || slug?.length > 2) {
|
|
||||||
notFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ROOMS_ROUTES.includes(roomType as RoomsType) && Number.isInteger(roomId)) {
|
|
||||||
return (
|
|
||||||
<Suspense fallback={<p>Loading...</p>}>
|
|
||||||
<RoomDetails
|
|
||||||
locale={locale}
|
|
||||||
roomId={roomId || 0}
|
|
||||||
activeType={roomType as RoomsType}
|
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ROOMS_ROUTES.includes(roomType as RoomsType) && !Number.isInteger(roomId)) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="col-xl-3 col-lg-4 d-none d-lg-block">
|
|
||||||
<AccountMenu locale={locale}/>
|
|
||||||
</div>
|
|
||||||
<div className="col-xl-9 col-lg-8 ">
|
|
||||||
<div className="page-account__inner">
|
|
||||||
<Suspense fallback={<p>Loading...</p>}>
|
|
||||||
<RoomsTabs
|
|
||||||
locale={locale}
|
|
||||||
activeTab={roomType as RoomsType}
|
|
||||||
/>
|
|
||||||
</Suspense>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return notFound();
|
|
||||||
};
|
|
|
@ -1,12 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import { redirect } from 'next/navigation';
|
|
||||||
import { useLocalStorage } from '../../../../../hooks/useLocalStorage';
|
|
||||||
import { AUTH_TOKEN_KEY } from '../../../../../constants/common';
|
|
||||||
import { RoomsType } from '../../../../../types/rooms';
|
|
||||||
|
|
||||||
export default function RoomsMainPage() {
|
|
||||||
const [token] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
|
||||||
|
|
||||||
return token ? redirect(`rooms/${RoomsType.UPCOMING}`) : null;
|
|
||||||
};
|
|
|
@ -37,7 +37,7 @@ export const Agora = ({ sessionId, secret, stopCalling, remoteUser }: AgoraProps
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="b-agora__wrap b-agora__wrap__single">
|
<div className="b-agora__wrap">
|
||||||
<RemoteUserPanel calling={calling} user={remoteUser} />
|
<RemoteUserPanel calling={calling} user={remoteUser} />
|
||||||
<div className="b-agora__panel">
|
<div className="b-agora__panel">
|
||||||
<MediaControl
|
<MediaControl
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
'use client'
|
|
||||||
|
|
||||||
import { useJoin } from 'agora-rtc-react';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { MediaControl } from './view';
|
|
||||||
import { UsersGroupPanel } from './components';
|
|
||||||
|
|
||||||
type AgoraProps = {
|
|
||||||
roomId: number;
|
|
||||||
secret?: string;
|
|
||||||
stopCalling: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AgoraGroup = ({ roomId, secret, stopCalling }: AgoraProps) => {
|
|
||||||
const [calling, setCalling] = useState(false);
|
|
||||||
const [micOn, setMic] = useState(false);
|
|
||||||
const [cameraOn, setCamera] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setCalling(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useJoin(
|
|
||||||
{
|
|
||||||
appid: process.env.NEXT_PUBLIC_AGORA_APPID,
|
|
||||||
channel: `${roomId}-${secret}`,
|
|
||||||
token: null,
|
|
||||||
},
|
|
||||||
calling,
|
|
||||||
);
|
|
||||||
|
|
||||||
const stop = () => {
|
|
||||||
stopCalling();
|
|
||||||
setCalling(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="b-agora__wrap">
|
|
||||||
<UsersGroupPanel calling={calling} micOn={micOn} cameraOn={cameraOn}/>
|
|
||||||
</div>
|
|
||||||
<div className="b-agora__panel_group">
|
|
||||||
<MediaControl
|
|
||||||
calling={calling}
|
|
||||||
cameraOn={cameraOn}
|
|
||||||
micOn={micOn}
|
|
||||||
setCalling={stop}
|
|
||||||
setCamera={() => setCamera(a => !a)}
|
|
||||||
setMic={() => setMic(a => !a)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,44 +0,0 @@
|
||||||
import {
|
|
||||||
type IRemoteVideoTrack,
|
|
||||||
useIsConnected, useLocalCameraTrack, useLocalMicrophoneTrack, usePublish,
|
|
||||||
useRemoteAudioTracks,
|
|
||||||
useRemoteUsers,
|
|
||||||
useRemoteVideoTracks
|
|
||||||
} from 'agora-rtc-react';
|
|
||||||
import { LocalUser } from './LocalUser';
|
|
||||||
import { RemoteVideoPlayer } from './RemoteVideoPlayer';
|
|
||||||
|
|
||||||
type UsersGroupPanelProps = {
|
|
||||||
calling: boolean;
|
|
||||||
micOn: boolean;
|
|
||||||
cameraOn: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const UsersGroupPanel = ({ calling, micOn, cameraOn }: UsersGroupPanelProps) => {
|
|
||||||
const isConnected = useIsConnected();
|
|
||||||
const remoteUsers = useRemoteUsers();
|
|
||||||
const { localMicrophoneTrack } = useLocalMicrophoneTrack(micOn);
|
|
||||||
const { localCameraTrack } = useLocalCameraTrack(cameraOn);
|
|
||||||
const { audioTracks } = useRemoteAudioTracks(remoteUsers);
|
|
||||||
|
|
||||||
usePublish([localMicrophoneTrack, localCameraTrack]);
|
|
||||||
audioTracks.map(track => track.play());
|
|
||||||
|
|
||||||
return calling && isConnected && remoteUsers ? (
|
|
||||||
<div className={`b-agora__remote_groups gr-${remoteUsers.length + 1}`}>
|
|
||||||
<div>
|
|
||||||
<LocalUser
|
|
||||||
audioTrack={localMicrophoneTrack}
|
|
||||||
cameraOn={cameraOn}
|
|
||||||
micOn={micOn}
|
|
||||||
videoTrack={localCameraTrack}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{remoteUsers.length > 0 && remoteUsers.map(({ uid, videoTrack }) => (
|
|
||||||
<div key={uid}>
|
|
||||||
<RemoteVideoPlayer track={videoTrack} />
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : null;
|
|
||||||
}
|
|
|
@ -3,4 +3,3 @@ export * from './UserCover';
|
||||||
export * from './RemoteUsers';
|
export * from './RemoteUsers';
|
||||||
export * from './LocalUserPanel';
|
export * from './LocalUserPanel';
|
||||||
export * from './RemoteUserPanel';
|
export * from './RemoteUserPanel';
|
||||||
export * from './UsersGroupPanel';
|
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
|
|
||||||
import AgoraRTC, { AgoraRTCProvider } from 'agora-rtc-react';
|
import AgoraRTC, { AgoraRTCProvider } from 'agora-rtc-react';
|
||||||
import { Session } from '../../../types/sessions';
|
import { Session } from '../../../types/sessions';
|
||||||
import { Room } from '../../../types/rooms';
|
|
||||||
import { Agora } from './Agora';
|
import { Agora } from './Agora';
|
||||||
import { AgoraGroup } from './AgoraGroup';
|
|
||||||
|
|
||||||
export const AgoraClient = ({ session, stopCalling, isCoach }: { session?: Session, stopCalling: () => void, isCoach: boolean }) => {
|
export const AgoraClient = ({ session, stopCalling, isCoach }: { session?: Session, stopCalling: () => void, isCoach: boolean }) => {
|
||||||
const remoteUser = isCoach ? (session?.clients?.length ? session?.clients[0] : undefined) : session?.coach;
|
const remoteUser = isCoach ? (session?.clients?.length ? session?.clients[0] : undefined) : session?.coach;
|
||||||
|
@ -22,17 +20,3 @@ export const AgoraClient = ({ session, stopCalling, isCoach }: { session?: Sessi
|
||||||
</AgoraRTCProvider>
|
</AgoraRTCProvider>
|
||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AgoraClientGroup = ({ room, stopCalling }: { room?: Room, stopCalling: () => void }) => {
|
|
||||||
return room ? (
|
|
||||||
<AgoraRTCProvider client={AgoraRTC.createClient({ mode: "rtc", codec: "vp8" })}>
|
|
||||||
{room && (
|
|
||||||
<AgoraGroup
|
|
||||||
roomId={room.id}
|
|
||||||
secret={room.secret}
|
|
||||||
stopCalling={stopCalling}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</AgoraRTCProvider>
|
|
||||||
) : null;
|
|
||||||
};
|
|
||||||
|
|
|
@ -3,4 +3,3 @@
|
||||||
export { AccountMenu } from './AccountMenu';
|
export { AccountMenu } from './AccountMenu';
|
||||||
export { ProfileSettings } from './ProfileSettings';
|
export { ProfileSettings } from './ProfileSettings';
|
||||||
export * from './sessions';
|
export * from './sessions';
|
||||||
export * from './rooms';
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
'use client'
|
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { EditRoomForm } from './EditRoomForm';
|
|
||||||
import debounce from 'lodash/debounce';
|
|
||||||
import { createRoom } from '../../../actions/rooms';
|
|
||||||
import { Loader } from '../../view/Loader';
|
|
||||||
import { useRouter } from '../../../navigation';
|
|
||||||
import { RoomsType } from '../../../types/rooms';
|
|
||||||
|
|
||||||
|
|
||||||
export const CreateRoom = ({ locale, jwt }: { locale: string, jwt: string }) => {
|
|
||||||
const [roomId, setRoomId] = useState<number>();
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const getRoom = debounce(() => {
|
|
||||||
setRoomId(2556);
|
|
||||||
// createRoom(locale, jwt)
|
|
||||||
// .then((data) => {
|
|
||||||
// setRoomId(data);
|
|
||||||
// })
|
|
||||||
// .finally(() => {
|
|
||||||
// setLoading(false);
|
|
||||||
// })
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// setLoading(true);
|
|
||||||
getRoom();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Loader isLoading={loading}>
|
|
||||||
{roomId && (
|
|
||||||
<EditRoomForm
|
|
||||||
roomId={roomId}
|
|
||||||
locale={locale}
|
|
||||||
jwt={jwt}
|
|
||||||
mode="create"
|
|
||||||
afterSubmit={() => router.push(`/account/rooms/${RoomsType.UPCOMING}`)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Loader>
|
|
||||||
)
|
|
||||||
};
|
|
|
@ -1,220 +0,0 @@
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
|
||||||
import { Button, Form, Input, notification } from 'antd';
|
|
||||||
import dayjs, { Dayjs } from 'dayjs';
|
|
||||||
import { i18nText } from '../../../i18nKeys';
|
|
||||||
import { Tag } from '../../../types/tags';
|
|
||||||
import { Slot } from '../../../types/experts';
|
|
||||||
import { RoomEdit, RoomEditDTO } from '../../../types/rooms';
|
|
||||||
import { getRoomById, updateRoom } from '../../../actions/rooms';
|
|
||||||
import { Loader } from '../../view/Loader';
|
|
||||||
import { CustomInput } from '../../view/CustomInput';
|
|
||||||
import { CustomSelect } from '../../view/CustomSelect';
|
|
||||||
import { CustomSwitch } from '../../view/CustomSwitch';
|
|
||||||
import { CustomMultiSelect } from '../../view/CustomMultiSelect';
|
|
||||||
import { CustomDatePicker } from '../../view/CustomDatePicker';
|
|
||||||
|
|
||||||
type EditRoomFormProps = {
|
|
||||||
roomId: number,
|
|
||||||
locale: string,
|
|
||||||
jwt: string,
|
|
||||||
mode: 'create' | 'edit';
|
|
||||||
afterSubmit?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
type RoomFormState = {
|
|
||||||
title?: string;
|
|
||||||
description?: string;
|
|
||||||
date?: Dayjs;
|
|
||||||
maxCount?: number;
|
|
||||||
startAt?: string;
|
|
||||||
supervisor?: boolean;
|
|
||||||
tags?: number[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const EditRoomForm = ({ roomId, locale, jwt, mode, afterSubmit }: EditRoomFormProps) => {
|
|
||||||
const [form] = Form.useForm<RoomFormState>();
|
|
||||||
const [editingRoom, setEditingRoom] = useState<RoomEditDTO>();
|
|
||||||
const dateValue = Form.useWatch('date', form);
|
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
|
||||||
const [fetchLoading, setFetchLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setFetchLoading(true);
|
|
||||||
getRoomById(locale, jwt, roomId)
|
|
||||||
.then((data) => {
|
|
||||||
setEditingRoom(data);
|
|
||||||
const { item } = data || {};
|
|
||||||
|
|
||||||
if (mode === 'edit' && item) {
|
|
||||||
form.setFieldsValue({
|
|
||||||
title: item.title,
|
|
||||||
description: item.description,
|
|
||||||
date: item?.scheduledStartAtUtc ? dayjs(item.scheduledStartAtUtc) : undefined,
|
|
||||||
maxCount: item.maxClients,
|
|
||||||
startAt: item?.scheduledStartAtUtc,
|
|
||||||
supervisor: item.isNeedSupervisor,
|
|
||||||
tags: item.tagIds || undefined
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setFetchLoading(false);
|
|
||||||
})
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const getAvailableSlots = useCallback((): string[] => {
|
|
||||||
const dateList = new Set<string>();
|
|
||||||
if (editingRoom?.availableSlots) {
|
|
||||||
editingRoom.availableSlots.forEach(({ startTime }) => {
|
|
||||||
const [date] = startTime.split('T');
|
|
||||||
dateList.add(date);
|
|
||||||
});
|
|
||||||
|
|
||||||
return Array.from(dateList);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}, [editingRoom?.availableSlots]);
|
|
||||||
|
|
||||||
const getTimeOptions = (slots?: Slot[], curDate?: Dayjs) => {
|
|
||||||
const date = curDate ? curDate.utc().format('YYYY-MM-DD') : '';
|
|
||||||
if (slots && slots?.length && date) {
|
|
||||||
return slots.filter(({ startTime }) => startTime.indexOf(date) > -1)
|
|
||||||
.map(({ startTime, endTime }) => ({ value: startTime, label: `${dayjs(startTime).format('HH:mm')} - ${dayjs(endTime).format('HH:mm')}` }));
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const getTagsOptions = (tags?: Tag[]) => {
|
|
||||||
if (tags) {
|
|
||||||
return tags.map(({ id, name }) => ({ value: id, label: <span>{name}</span> })) || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSubmit = () => {
|
|
||||||
setLoading(true);
|
|
||||||
const { title, description, startAt, maxCount, tags, supervisor } = form.getFieldsValue();
|
|
||||||
const result: RoomEdit = {
|
|
||||||
...editingRoom,
|
|
||||||
id: roomId,
|
|
||||||
title,
|
|
||||||
scheduledStartAtUtc: startAt,
|
|
||||||
maxClients: maxCount,
|
|
||||||
isNeedSupervisor: supervisor,
|
|
||||||
tagIds: tags || []
|
|
||||||
};
|
|
||||||
|
|
||||||
if (description) {
|
|
||||||
result.description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateRoom(locale, jwt, result)
|
|
||||||
.then(() => {
|
|
||||||
afterSubmit && afterSubmit();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
notification.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: err?.response?.data?.errMessage
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const disabledDate = (current: Dayjs) => current && !getAvailableSlots().includes(current.format('YYYY-MM-DD'));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Loader isLoading={fetchLoading}>
|
|
||||||
<Form
|
|
||||||
form={form}
|
|
||||||
autoComplete="off"
|
|
||||||
style={{ display: 'flex', gap: 16, flexDirection: 'column' }}
|
|
||||||
onFinish={onSubmit}
|
|
||||||
className="b-room-form"
|
|
||||||
>
|
|
||||||
<Form.Item
|
|
||||||
name="title"
|
|
||||||
rules={[{ required: true }]}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<CustomInput
|
|
||||||
size="small"
|
|
||||||
placeholder={i18nText('title', locale)}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="description">
|
|
||||||
<Input.TextArea
|
|
||||||
className="b-textarea"
|
|
||||||
rows={4}
|
|
||||||
maxLength={1000}
|
|
||||||
placeholder={i18nText('description', locale)}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<div className="b-room-form__grid">
|
|
||||||
<Form.Item
|
|
||||||
name="date"
|
|
||||||
rules={[{ required: true }]}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<CustomDatePicker
|
|
||||||
locale={locale}
|
|
||||||
label={i18nText('room.date', locale)}
|
|
||||||
disabledDate={disabledDate}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="startAt"
|
|
||||||
rules={[{ required: true }]}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<CustomSelect
|
|
||||||
label={i18nText('room.time', locale)}
|
|
||||||
options={getTimeOptions(editingRoom?.availableSlots, dateValue)}
|
|
||||||
disabled={!dateValue}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="maxCount"
|
|
||||||
rules={[{ required: true }]}
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<CustomSelect
|
|
||||||
label={i18nText('room.maxParticipants', locale)}
|
|
||||||
options={Array.from({ length: 16 }).map((_, i) => ({ value: i+1, label: i+1 }))}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
name="supervisor"
|
|
||||||
valuePropName="checked"
|
|
||||||
label={i18nText('room.presenceOfSupervisor', locale)}
|
|
||||||
className="b-room-switch"
|
|
||||||
>
|
|
||||||
<CustomSwitch />
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
<Form.Item
|
|
||||||
name="tags"
|
|
||||||
noStyle
|
|
||||||
>
|
|
||||||
<CustomMultiSelect
|
|
||||||
label={i18nText('topics', locale)}
|
|
||||||
options={getTagsOptions(editingRoom?.tags)}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Button
|
|
||||||
className="card-detail__apply"
|
|
||||||
htmlType="submit"
|
|
||||||
loading={loading}
|
|
||||||
disabled={loading}
|
|
||||||
>
|
|
||||||
{i18nText('room.save', locale)}
|
|
||||||
</Button>
|
|
||||||
</Form>
|
|
||||||
</Loader>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,66 +0,0 @@
|
||||||
'use client'
|
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { RoomsType } from '../../../types/rooms';
|
|
||||||
import { useSessionTracking } from '../../../actions/hooks/useSessionTracking';
|
|
||||||
import { AccountMenu } from '../AccountMenu';
|
|
||||||
import { Loader } from '../../view/Loader';
|
|
||||||
import { RoomDetailsContent } from './RoomDetailsContent';
|
|
||||||
import { useRoomDetails } from '../../../actions/hooks/useRoomDetails';
|
|
||||||
import { AgoraClientGroup } from '../agora';
|
|
||||||
|
|
||||||
type RoomDetailsProps = {
|
|
||||||
locale: string;
|
|
||||||
roomId: number;
|
|
||||||
activeType: RoomsType;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RoomDetails = ({ roomId, locale, activeType }: RoomDetailsProps) => {
|
|
||||||
const { room, errorData, loading, fetchData } = useRoomDetails(locale, roomId);
|
|
||||||
const tracking = useSessionTracking(locale, roomId);
|
|
||||||
const [isCalling, setIsCalling] = useState<boolean>(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isCalling) {
|
|
||||||
tracking.start();
|
|
||||||
} else {
|
|
||||||
tracking.stop();
|
|
||||||
}
|
|
||||||
}, [isCalling]);
|
|
||||||
|
|
||||||
const stopCalling = () => {
|
|
||||||
setIsCalling(false);
|
|
||||||
fetchData();
|
|
||||||
}
|
|
||||||
|
|
||||||
return isCalling
|
|
||||||
? (
|
|
||||||
<AgoraClientGroup
|
|
||||||
room={room}
|
|
||||||
stopCalling={stopCalling}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<div className="col-xl-3 col-lg-4 d-none d-lg-block">
|
|
||||||
<AccountMenu locale={locale} />
|
|
||||||
</div>
|
|
||||||
<div className="col-xl-9 col-lg-8 ">
|
|
||||||
<div className="page-account__inner">
|
|
||||||
<Loader
|
|
||||||
isLoading={loading}
|
|
||||||
errorData={errorData}
|
|
||||||
refresh={fetchData}
|
|
||||||
>
|
|
||||||
<RoomDetailsContent
|
|
||||||
locale={locale}
|
|
||||||
room={room}
|
|
||||||
activeType={activeType}
|
|
||||||
startRoom={() => setIsCalling(true)}
|
|
||||||
refresh={fetchData}
|
|
||||||
/>
|
|
||||||
</Loader>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,355 +0,0 @@
|
||||||
'use client'
|
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
|
||||||
import { Button, notification, Tag } from 'antd';
|
|
||||||
import { DeleteOutlined, LeftOutlined } from '@ant-design/icons';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import Image from 'next/image';
|
|
||||||
import { useRouter } from '../../../navigation';
|
|
||||||
import { Room, RoomsType } from '../../../types/rooms';
|
|
||||||
import { i18nText } from '../../../i18nKeys';
|
|
||||||
import { LinkButton } from '../../view/LinkButton';
|
|
||||||
import {
|
|
||||||
addClient,
|
|
||||||
addSupervisor,
|
|
||||||
becomeRoomClient,
|
|
||||||
becomeRoomSupervisor,
|
|
||||||
deleteRoomClient,
|
|
||||||
deleteRoomSupervisor
|
|
||||||
} from '../../../actions/rooms';
|
|
||||||
import { AUTH_TOKEN_KEY, AUTH_USER } from '../../../constants/common';
|
|
||||||
import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
|
||||||
import { UserListModal } from '../../Modals/UsersListModal';
|
|
||||||
import { SessionState } from '../../../types/sessions';
|
|
||||||
import { EditRoomForm } from './EditRoomForm';
|
|
||||||
|
|
||||||
type RoomDetailsContentProps = {
|
|
||||||
locale: string;
|
|
||||||
activeType: RoomsType;
|
|
||||||
room?: Room;
|
|
||||||
startRoom: () => void;
|
|
||||||
refresh: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RoomDetailsContent = ({ room, startRoom, locale, activeType, refresh }: RoomDetailsContentProps) => {
|
|
||||||
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
|
||||||
const [userData] = useLocalStorage(AUTH_USER, '');
|
|
||||||
const { id: userId = 0 } = userData ? JSON.parse(userData) : {};
|
|
||||||
const router = useRouter();
|
|
||||||
const [showModal, setShowModal] = useState<boolean>(false);
|
|
||||||
const [forSupervisor, setForSupervisor] = useState<boolean>(false);
|
|
||||||
const startDate = room?.scheduledStartAtUtc ? dayjs(room?.scheduledStartAtUtc).locale(locale) : null;
|
|
||||||
const endDate = room?.scheduledEndAtUtc ? dayjs(room?.scheduledEndAtUtc).locale(locale) : null;
|
|
||||||
const today = startDate ? dayjs().format('YYYY-MM-DD') === startDate.format('YYYY-MM-DD') : false;
|
|
||||||
const isCreator = room?.coach && room.coach.id === +userId || false;
|
|
||||||
const isSupervisor = room?.supervisor && room.supervisor.id === +userId || false;
|
|
||||||
const isClient = room?.clients && room.clients.length > 0 && room.clients.map(({ id }) => id).includes(+userId) || false;
|
|
||||||
const isTimeBeforeStart = room?.scheduledStartAtUtc ? dayjs() < dayjs(room.scheduledStartAtUtc) : false;
|
|
||||||
const [isEdit, setIsEdit] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const goBack = () => router.push(`/account/rooms/${activeType}`);
|
|
||||||
|
|
||||||
const checkUserApply = (): boolean => (!room?.supervisor || !isSupervisor) && (!room?.clients || room?.clients && room?.clients.length === 0 || !isClient);
|
|
||||||
|
|
||||||
const deleteClient = (clientUserId: number) => {
|
|
||||||
if (room?.id) {
|
|
||||||
deleteRoomClient(locale, jwt, { sessionId: room.id, clientUserId })
|
|
||||||
.then(() => {
|
|
||||||
refresh();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
notification.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: err?.response?.data?.errMessage
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const deleteSupervisor = (supervisorUserId?: number) => {
|
|
||||||
if (room?.id && supervisorUserId) {
|
|
||||||
deleteRoomSupervisor(locale, jwt, { sessionId: room.id, supervisorUserId })
|
|
||||||
.then(() => {
|
|
||||||
refresh();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
notification.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: err?.response?.data?.errMessage
|
|
||||||
});
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const becomeClient = () => {
|
|
||||||
if (room?.id && userId) {
|
|
||||||
becomeRoomClient(locale, jwt, { sessionId: room.id, clientUserId: +userId })
|
|
||||||
.then(() => {
|
|
||||||
refresh();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
notification.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: err?.response?.data?.errMessage
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const becomeSupervisor = () => {
|
|
||||||
if (room?.id && userId) {
|
|
||||||
becomeRoomSupervisor(locale, jwt, { sessionId: room.id, supervisorUserId: +userId })
|
|
||||||
.then(() => {
|
|
||||||
refresh();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
notification.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: err?.response?.data?.errMessage
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onInviteSupervisor = () => {
|
|
||||||
setForSupervisor(true)
|
|
||||||
setShowModal(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onAddUser = (id: number) => {
|
|
||||||
if (room?.id) {
|
|
||||||
setShowModal(false);
|
|
||||||
|
|
||||||
if (forSupervisor) {
|
|
||||||
addSupervisor(locale, jwt, { sessionId: room.id, supervisorUserId: id })
|
|
||||||
.then(() => {
|
|
||||||
refresh();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
notification.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: err?.response?.data?.errMessage
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
addClient(locale, jwt, { sessionId: room.id, clientUserId: id })
|
|
||||||
.then(() => {
|
|
||||||
refresh();
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
notification.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: err?.response?.data?.errMessage
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const afterEditing = () => {
|
|
||||||
setIsEdit(false);
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
return !isEdit ? (
|
|
||||||
<div className="card-detail">
|
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
className="card-detail__back"
|
|
||||||
type="link"
|
|
||||||
icon={<LeftOutlined/>}
|
|
||||||
onClick={goBack}
|
|
||||||
>
|
|
||||||
{i18nText('back', locale)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="card-detail__name">{room?.title || ''}</div>
|
|
||||||
<div
|
|
||||||
className={`card-detail__date${today ? ' chosen' : ''}${activeType === RoomsType.RECENT ? ' history' : ''}`}>
|
|
||||||
{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>
|
|
||||||
{room?.themesTags && room.themesTags.length > 0 && (
|
|
||||||
<div className="card-detail__skills">
|
|
||||||
<div className="skills__list">
|
|
||||||
{room.themesTags.map((skill) => <Tag key={skill?.id}
|
|
||||||
className="skills__list__item">{skill?.name}</Tag>)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{room?.description && <div className="card-profile__desc">{room.description}</div>}
|
|
||||||
{activeType === RoomsType.UPCOMING && (isCreator || isSupervisor || isClient) && (
|
|
||||||
<div className="card-detail__actions">
|
|
||||||
{(isCreator || isClient || isSupervisor) && (
|
|
||||||
<Button
|
|
||||||
className="card-detail__apply"
|
|
||||||
onClick={startRoom}
|
|
||||||
>
|
|
||||||
{isCreator ? i18nText('session.start', locale) : i18nText('session.join', locale)}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{isCreator && isTimeBeforeStart && room?.state === SessionState.COACH_APPROVED && (
|
|
||||||
<Button
|
|
||||||
className="card-detail__filled"
|
|
||||||
onClick={() => setIsEdit(true)}
|
|
||||||
>
|
|
||||||
{i18nText('room.editRoom', locale)}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="card-detail__profile">
|
|
||||||
<div className="card-detail__profile_title">
|
|
||||||
<div>{i18nText('room.roomCreator', locale)}</div>
|
|
||||||
</div>
|
|
||||||
<div className="card-detail__profile_list">
|
|
||||||
<div className="card-detail__profile_item">
|
|
||||||
<div className="card-detail__portrait card-detail__portrait_small">
|
|
||||||
<Image src={room?.coach?.faceImageUrl || '/images/user-avatar.png'} width={86} height={86} alt=""/>
|
|
||||||
</div>
|
|
||||||
<div className="card-detail__inner">
|
|
||||||
<div className="card-detail__name">{`${room?.coach?.name} ${room?.coach?.surname || ''}`}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{room?.isNeedSupervisor && (
|
|
||||||
<div className="card-detail__profile">
|
|
||||||
<div className="card-detail__profile_title">
|
|
||||||
<div>{i18nText('room.supervisor', locale)}</div>
|
|
||||||
</div>
|
|
||||||
{room?.supervisor && (
|
|
||||||
<div className="card-detail__profile_list">
|
|
||||||
<div className="card-detail__profile_item">
|
|
||||||
<div className="card-detail__portrait card-detail__portrait_small">
|
|
||||||
<Image src={room?.supervisor?.faceImageUrl || '/images/user-avatar.png'} width={86}
|
|
||||||
height={86}
|
|
||||||
alt=""/>
|
|
||||||
</div>
|
|
||||||
<div className="card-detail__inner">
|
|
||||||
<div
|
|
||||||
className="card-detail__name">{`${room?.supervisor?.name} ${room?.supervisor?.surname || ''}`}</div>
|
|
||||||
</div>
|
|
||||||
{isCreator && activeType === RoomsType.UPCOMING && isTimeBeforeStart && room?.state === SessionState.COACH_APPROVED && (
|
|
||||||
<LinkButton
|
|
||||||
type="link"
|
|
||||||
style={{alignSelf: 'flex-start'}}
|
|
||||||
danger
|
|
||||||
icon={<DeleteOutlined/>}
|
|
||||||
onClick={() => deleteSupervisor(room?.supervisor?.id)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{room?.supervisor && activeType === RoomsType.RECENT && (
|
|
||||||
<>
|
|
||||||
{room?.supervisorComment && (
|
|
||||||
<div className="card-detail__supervisor-comment">{room.supervisorComment}</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{isTimeBeforeStart && room?.state === SessionState.COACH_APPROVED && !room?.supervisor && isCreator && activeType === RoomsType.UPCOMING && (
|
|
||||||
<Button
|
|
||||||
className="card-detail__filled"
|
|
||||||
onClick={onInviteSupervisor}
|
|
||||||
>
|
|
||||||
{i18nText('room.inviteSupervisor', locale)}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{isTimeBeforeStart && room?.state === SessionState.COACH_APPROVED && !room?.supervisor && !isCreator && activeType === RoomsType.UPCOMING && checkUserApply() && (
|
|
||||||
<Button
|
|
||||||
className="card-detail__apply"
|
|
||||||
onClick={becomeSupervisor}
|
|
||||||
>
|
|
||||||
{i18nText('room.joinSupervisor', locale)}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{!room?.supervisor && !isCreator && !checkUserApply() && (
|
|
||||||
<div className="card-profile__desc">{i18nText('noData', locale)}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div className="card-detail__profile">
|
|
||||||
<div className="card-detail__profile_title">
|
|
||||||
<div>{i18nText('room.participants', locale)}</div>
|
|
||||||
<div>{`${room?.clients?.length || 0}/${room?.maxClients}`}</div>
|
|
||||||
</div>
|
|
||||||
{room?.clients && room?.clients?.length > 0 && (
|
|
||||||
<div className="card-detail__profile_list">
|
|
||||||
{room.clients.map(({id, faceImageUrl, name, surname}) => (
|
|
||||||
<div key={id} className="card-detail__profile_item">
|
|
||||||
<div className="card-detail__portrait card-detail__portrait_small">
|
|
||||||
<Image src={faceImageUrl || '/images/user-avatar.png'} width={86}
|
|
||||||
height={86}
|
|
||||||
alt=""/>
|
|
||||||
</div>
|
|
||||||
<div className="card-detail__inner">
|
|
||||||
<div
|
|
||||||
className="card-detail__name">{`${name} ${surname || ''}`}</div>
|
|
||||||
</div>
|
|
||||||
{isCreator && room?.state === SessionState.COACH_APPROVED && activeType === RoomsType.UPCOMING && isTimeBeforeStart && (
|
|
||||||
<LinkButton
|
|
||||||
type="link"
|
|
||||||
style={{alignSelf: 'flex-start'}}
|
|
||||||
danger
|
|
||||||
icon={<DeleteOutlined/>}
|
|
||||||
onClick={() => deleteClient(id)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{isTimeBeforeStart && room?.state === SessionState.COACH_APPROVED && isCreator && activeType === RoomsType.UPCOMING && (!room?.clients || (room?.clients && room?.clients?.length < room.maxClients)) && (
|
|
||||||
<Button
|
|
||||||
className="card-detail__filled"
|
|
||||||
onClick={() => setShowModal(true)}
|
|
||||||
>
|
|
||||||
{i18nText('room.inviteParticipant', locale)}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{isTimeBeforeStart && room?.state === SessionState.COACH_APPROVED && !isCreator && activeType === RoomsType.UPCOMING && (!room?.clients || (room?.clients && room?.clients?.length < room.maxClients)) && checkUserApply() && (
|
|
||||||
<Button
|
|
||||||
className="card-detail__apply"
|
|
||||||
onClick={becomeClient}
|
|
||||||
>
|
|
||||||
{i18nText('room.joinParticipant', locale)}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{room && (
|
|
||||||
<UserListModal
|
|
||||||
locale={locale}
|
|
||||||
jwt={jwt}
|
|
||||||
isOpen={showModal}
|
|
||||||
handleCancel={() => setShowModal(false)}
|
|
||||||
submit={onAddUser}
|
|
||||||
afterCloseModal={() => setForSupervisor(false)}
|
|
||||||
room={room}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="card-detail">
|
|
||||||
<div>
|
|
||||||
<Button
|
|
||||||
className="card-detail__back"
|
|
||||||
type="link"
|
|
||||||
icon={<LeftOutlined/>}
|
|
||||||
onClick={() => setIsEdit(false)}
|
|
||||||
>
|
|
||||||
{i18nText('back', locale)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<EditRoomForm
|
|
||||||
roomId={room?.id || 0}
|
|
||||||
locale={locale}
|
|
||||||
jwt={jwt}
|
|
||||||
mode="edit"
|
|
||||||
afterSubmit={afterEditing}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,173 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import React, { MouseEvent, useCallback, useEffect, useState } from 'react';
|
|
||||||
import { Empty, Space } from 'antd';
|
|
||||||
import 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 { RoomsType } from '../../../types/rooms';
|
|
||||||
import { getRecentRooms, getUpcomingRooms } from '../../../actions/rooms';
|
|
||||||
import { Loader } from '../../view/Loader';
|
|
||||||
import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
|
||||||
import { AUTH_TOKEN_KEY } from '../../../constants/common';
|
|
||||||
import { usePathname, useRouter } from '../../../navigation';
|
|
||||||
import { i18nText } from '../../../i18nKeys';
|
|
||||||
import { CreateRoom } from './CreateRoom';
|
|
||||||
|
|
||||||
type RoomsTabsProps = {
|
|
||||||
locale: string;
|
|
||||||
activeTab: RoomsType;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RoomsTabs = ({ locale, activeTab }: RoomsTabsProps) => {
|
|
||||||
const [sort, setSort] = useState<string>();
|
|
||||||
const [rooms, setRooms] = useState<any>();
|
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
|
||||||
const [errorData, setErrorData] = useState<any>();
|
|
||||||
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
|
||||||
const router = useRouter();
|
|
||||||
const pathname = usePathname();
|
|
||||||
|
|
||||||
const fetchData = () => {
|
|
||||||
setErrorData(undefined);
|
|
||||||
setLoading(true);
|
|
||||||
Promise.all([
|
|
||||||
getUpcomingRooms(locale, jwt),
|
|
||||||
getRecentRooms(locale, jwt)
|
|
||||||
])
|
|
||||||
.then(([upcoming, recent]) => {
|
|
||||||
setRooms({
|
|
||||||
[RoomsType.UPCOMING]: upcoming || [],
|
|
||||||
[RoomsType.RECENT]: recent || []
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
setErrorData(err);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchData();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onChangeSort = useCallback((value: string) => {
|
|
||||||
setSort(value);
|
|
||||||
}, [sort]);
|
|
||||||
|
|
||||||
const onClickSession = (event: MouseEvent<HTMLDivElement>, id: number) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
router.push(`${pathname}/${id}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getChildren = (list?: any[]) => (
|
|
||||||
<>
|
|
||||||
{/* <div className="filter-session">
|
|
||||||
<div className="filter-session__item">
|
|
||||||
<CustomSelect
|
|
||||||
label="Topic"
|
|
||||||
value={sort}
|
|
||||||
onChange={onChangeSort}
|
|
||||||
options={[
|
|
||||||
{ value: 'topic1', label: 'Topic 1' },
|
|
||||||
{ value: 'topic2', label: 'Topic 2' },
|
|
||||||
{ value: 'topic3', label: 'Topic 3' },
|
|
||||||
{ value: 'topic4', label: 'Topic 4' }
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div> */}
|
|
||||||
<div className="list-session">
|
|
||||||
{list && list?.length > 0 ? list?.map(({ id, scheduledStartAtUtc, scheduledEndAtUtc, title, coach, clients, supervisor, maxClients }) => {
|
|
||||||
const startDate = dayjs(scheduledStartAtUtc).locale(locale);
|
|
||||||
const endDate = dayjs(scheduledEndAtUtc).locale(locale);
|
|
||||||
const today = dayjs().format('YYYY-MM-DD') === startDate.format('YYYY-MM-DD');
|
|
||||||
|
|
||||||
return (
|
|
||||||
<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={coach?.faceImageUrl || '/images/person.png'} className="" alt="" />
|
|
||||||
</div>
|
|
||||||
<div className="card-profile__header__inner">
|
|
||||||
<div>
|
|
||||||
<div className="card-profile__header__name">{`${coach?.name} ${coach?.surname || ''}`}</div>
|
|
||||||
<div className="card-profile__header__title">{title}</div>
|
|
||||||
<div className={`card-profile__header__date${activeTab === RoomsType.RECENT ? ' history' : (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-room__details">
|
|
||||||
{supervisor && (
|
|
||||||
<>
|
|
||||||
<div>{i18nText('room.supervisor', locale)}</div>
|
|
||||||
<div>{`${supervisor?.name} ${supervisor?.surname || ''}`}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
<div>{i18nText('room.members', locale)}</div>
|
|
||||||
<div>{`${clients.length}/${maxClients}`}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}) : (
|
|
||||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={i18nText('noData', locale)} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
const tabs = [
|
|
||||||
{
|
|
||||||
key: RoomsType.UPCOMING,
|
|
||||||
label: (
|
|
||||||
<>
|
|
||||||
{i18nText('room.upcoming', locale)}
|
|
||||||
{rooms?.upcoming && rooms?.upcoming?.length > 0 ? (<span className="count">{rooms?.upcoming.length}</span>) : null}
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
children: getChildren(rooms?.upcoming)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: RoomsType.RECENT,
|
|
||||||
label: i18nText('room.recent', locale),
|
|
||||||
children: getChildren(rooms?.recent)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: RoomsType.NEW,
|
|
||||||
label: i18nText('room.newRoom', locale),
|
|
||||||
children: <CreateRoom locale={locale} jwt={jwt} />
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Loader
|
|
||||||
isLoading={loading}
|
|
||||||
errorData={errorData}
|
|
||||||
refresh={fetchData}
|
|
||||||
>
|
|
||||||
<div className="tabs-session">
|
|
||||||
{tabs.map(({ key, label }) => (
|
|
||||||
<Space
|
|
||||||
key={key}
|
|
||||||
className={`tabs-session__item ${key === activeTab ? 'active' : ''}`}
|
|
||||||
onClick={() => router.push(`/account/rooms/${key}`)}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</Space>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{tabs.filter(({ key }) => key === activeTab)[0].children}
|
|
||||||
</Loader>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,6 +0,0 @@
|
||||||
'use client'
|
|
||||||
|
|
||||||
export * from './RoomDetails';
|
|
||||||
export * from './RoomsTabs';
|
|
||||||
export * from './RoomDetailsContent';
|
|
||||||
export * from './CreateRoom';
|
|
|
@ -81,7 +81,7 @@ export const SessionDetailsContent = ({ session, locale, activeType, startSessio
|
||||||
const CoachCard = (coach?: PublicUser) => coach ? (
|
const CoachCard = (coach?: PublicUser) => coach ? (
|
||||||
<div className="card-detail__expert">
|
<div className="card-detail__expert">
|
||||||
<div className="card-detail__portrait">
|
<div className="card-detail__portrait">
|
||||||
<Image src={coach?.faceImageUrl || '/images/user-avatar.png'} width={140} height={140} alt="" />
|
<Image src={coach?.faceImageUrl || '/images/person.png'} width={140} height={140} alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div className="card-detail__inner">
|
<div className="card-detail__inner">
|
||||||
<Link href={`/experts/${coach?.id}` as any} target="_blank">
|
<Link href={`/experts/${coach?.id}` as any} target="_blank">
|
||||||
|
@ -106,7 +106,7 @@ export const SessionDetailsContent = ({ session, locale, activeType, startSessio
|
||||||
<div className="card-detail__skills">
|
<div className="card-detail__skills">
|
||||||
<div className="skills__list">
|
<div className="skills__list">
|
||||||
{session?.themesTags?.slice(0, 2).map((skill) => <Tag key={skill?.id} className="skills__list__item">{skill?.name}</Tag>)}
|
{session?.themesTags?.slice(0, 2).map((skill) => <Tag key={skill?.id} className="skills__list__item">{skill?.name}</Tag>)}
|
||||||
{session?.themesTags && session?.themesTags?.length > 2
|
{session?.themesTags?.length > 2
|
||||||
? (
|
? (
|
||||||
<Tag className="skills__list__more">
|
<Tag className="skills__list__more">
|
||||||
<Link href={`/experts/${coach?.id}` as any} target="_blank">
|
<Link href={`/experts/${coach?.id}` as any} target="_blank">
|
||||||
|
@ -128,7 +128,7 @@ export const SessionDetailsContent = ({ session, locale, activeType, startSessio
|
||||||
const StudentCard = (student?: PublicUser | null) => student ? (
|
const StudentCard = (student?: PublicUser | null) => student ? (
|
||||||
<div className="card-detail__expert">
|
<div className="card-detail__expert">
|
||||||
<div className="card-detail__portrait">
|
<div className="card-detail__portrait">
|
||||||
<Image src={student?.faceImageUrl || '/images/user-avatar.png'} width={140} height={140} alt="" />
|
<Image src={student?.faceImageUrl || '/images/person.png'} width={140} height={140} alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div className="card-detail__inner">
|
<div className="card-detail__inner">
|
||||||
<div className="card-detail__name">{`${student?.name} ${student?.surname || ''}`}</div>
|
<div className="card-detail__name">{`${student?.name} ${student?.surname || ''}`}</div>
|
||||||
|
|
|
@ -55,6 +55,8 @@ export const ExpertsAdditionalFilter = ({
|
||||||
};
|
};
|
||||||
const search = getSearchParamsString(newFilter);
|
const search = getSearchParamsString(newFilter);
|
||||||
|
|
||||||
|
console.log('here1');
|
||||||
|
|
||||||
router.push(search ? `${basePath}?${search}#filter` : `${basePath}#filter`);
|
router.push(search ? `${basePath}?${search}#filter` : `${basePath}#filter`);
|
||||||
|
|
||||||
// router.push({
|
// router.push({
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React, { FC, useState } from 'react';
|
import React, { FC, useEffect, useState } from 'react';
|
||||||
import { Modal, Button, message, Form, Collapse } from 'antd';
|
import {Modal, Button, message, Form, Collapse, GetProp, UploadProps} from 'antd';
|
||||||
import type { CollapseProps } from 'antd';
|
import type { CollapseProps } from 'antd';
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import { i18nText } from '../../i18nKeys';
|
import { i18nText } from '../../i18nKeys';
|
||||||
import { PracticePersonData } from '../../types/practice';
|
import { PracticePersonData, PracticeDTO, PracticeData, PracticeCase } from '../../types/practice';
|
||||||
import { AUTH_TOKEN_KEY } from '../../constants/common';
|
import { AUTH_TOKEN_KEY } from '../../constants/common';
|
||||||
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
||||||
import { setEducation } from '../../actions/profile';
|
import {setEducation} from '../../actions/profile';
|
||||||
import { EducationData, EducationDTO } from '../../types/education';
|
import {Certificate, Details, EducationData, EducationDTO, Experience} from "../../types/education";
|
||||||
import { CertificatesContent } from './educationModalContent/Certificates';
|
import {CertificatesContent} from "./educationModalContent/Certificates";
|
||||||
import { EducationsContent } from './educationModalContent/Educations';
|
import {EducationsContent} from "./educationModalContent/Educations";
|
||||||
import { TrainingsContent } from './educationModalContent/Trainings';
|
import {TrainingsContent} from "./educationModalContent/Trainings";
|
||||||
import { MbasContent } from './educationModalContent/Mbas';
|
import {MbasContent} from "./educationModalContent/Mbas";
|
||||||
import { ExperiencesContent } from './educationModalContent/Experiences';
|
import {ExperiencesContent} from "./educationModalContent/Experiences";
|
||||||
|
|
||||||
type EditExpertEducationModalProps = {
|
type EditExpertEducationModalProps = {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
|
|
|
@ -6,6 +6,12 @@ import { Modal, Menu, Calendar, Radio, Button, Input, message, Form } from 'antd
|
||||||
import type { CalendarProps, MenuProps } from 'antd';
|
import type { CalendarProps, MenuProps } from 'antd';
|
||||||
import { ArrowLeftOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined } from '@ant-design/icons';
|
||||||
import { CloseOutlined } 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 dayjs, { Dayjs } from 'dayjs';
|
import dayjs, { Dayjs } from 'dayjs';
|
||||||
import 'dayjs/locale/ru';
|
import 'dayjs/locale/ru';
|
||||||
import 'dayjs/locale/en';
|
import 'dayjs/locale/en';
|
||||||
|
@ -13,7 +19,6 @@ import 'dayjs/locale/de';
|
||||||
import 'dayjs/locale/it';
|
import 'dayjs/locale/it';
|
||||||
import 'dayjs/locale/fr';
|
import 'dayjs/locale/fr';
|
||||||
import 'dayjs/locale/es';
|
import 'dayjs/locale/es';
|
||||||
import { getLocale } from '../../utils/locale';
|
|
||||||
import { AUTH_TOKEN_KEY, SESSION_DATA } from '../../constants/common';
|
import { AUTH_TOKEN_KEY, SESSION_DATA } from '../../constants/common';
|
||||||
import { ExpertScheduler, SignupSessionData } from '../../types/experts';
|
import { ExpertScheduler, SignupSessionData } from '../../types/experts';
|
||||||
import { Tag } from '../../types/tags';
|
import { Tag } from '../../types/tags';
|
||||||
|
@ -37,6 +42,27 @@ type ScheduleModalProps = {
|
||||||
|
|
||||||
type MenuItem = Required<MenuProps>['items'][number];
|
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 })
|
const getCalendarMenu = (start: Dayjs): MenuItem[] => Array.from({ length: 3 })
|
||||||
.map((_: unknown, index: number) => {
|
.map((_: unknown, index: number) => {
|
||||||
const date = index ? start.add(index, 'M') : start.clone();
|
const date = index ? start.add(index, 'M') : start.clone();
|
||||||
|
|
|
@ -1,113 +0,0 @@
|
||||||
'use client';
|
|
||||||
|
|
||||||
import React, { useCallback, useState } from 'react';
|
|
||||||
import { Button, Modal, notification } from 'antd';
|
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
|
||||||
import debounce from 'lodash/debounce';
|
|
||||||
import Image from 'next/image';
|
|
||||||
import { i18nText } from '../../i18nKeys';
|
|
||||||
import { getUsersList } from '../../actions/rooms';
|
|
||||||
import { PublicUser } from '../../types/sessions';
|
|
||||||
import { Room } from '../../types/rooms';
|
|
||||||
import { CustomInput } from '../view/CustomInput';
|
|
||||||
import { Loader } from '../view/Loader';
|
|
||||||
|
|
||||||
type UserListModalProps = {
|
|
||||||
room: Room;
|
|
||||||
isOpen: boolean;
|
|
||||||
locale: string;
|
|
||||||
handleCancel: () => void;
|
|
||||||
jwt: string;
|
|
||||||
submit: (id: number) => void;
|
|
||||||
afterCloseModal?: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const UserListModal = ({ room, isOpen, locale, handleCancel, jwt, submit, afterCloseModal }: UserListModalProps) => {
|
|
||||||
const [users, setUsers] = useState<PublicUser[] | undefined>();
|
|
||||||
const [loading, seLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const onSearch = useCallback(debounce((e: any) => {
|
|
||||||
if (e?.target?.value) {
|
|
||||||
seLoading(true);
|
|
||||||
getUsersList(locale, jwt, { template: e.target.value })
|
|
||||||
.then(({ items }) => {
|
|
||||||
const clients = room?.clients?.map(({ id }) => id);
|
|
||||||
setUsers(items
|
|
||||||
? items.filter(({ id }) => !(clients?.length && clients.includes(id) || id === room?.supervisor?.id || id === room?.coach?.id))
|
|
||||||
: undefined);
|
|
||||||
})
|
|
||||||
.catch((err: any) => {
|
|
||||||
notification.error({
|
|
||||||
message: 'Error',
|
|
||||||
description: err?.response?.data?.errMessage
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
seLoading(false);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setUsers(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, 300), []);
|
|
||||||
|
|
||||||
const onAfterClose = () => {
|
|
||||||
setUsers(undefined);
|
|
||||||
if (afterCloseModal) afterCloseModal();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
className="b-modal"
|
|
||||||
open={isOpen}
|
|
||||||
title={undefined}
|
|
||||||
onOk={undefined}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
footer={false}
|
|
||||||
width={498}
|
|
||||||
closeIcon={<CloseOutlined style={{ fontSize: 20, color: '#000' }}/>}
|
|
||||||
afterClose={onAfterClose}
|
|
||||||
>
|
|
||||||
<div className="b-modal__users-list__content">
|
|
||||||
<CustomInput
|
|
||||||
placeholder={i18nText('search', locale)}
|
|
||||||
onChange={onSearch}
|
|
||||||
allowClear
|
|
||||||
/>
|
|
||||||
{users && (
|
|
||||||
<div className="b-users-list__wrapper">
|
|
||||||
<Loader isLoading={loading}>
|
|
||||||
{users.length > 0 ? (
|
|
||||||
<div className="b-users-list">
|
|
||||||
{users.map(({ id, name, surname, faceImageUrl }) => (
|
|
||||||
<div className="b-users-list-item" key={id}>
|
|
||||||
<div>
|
|
||||||
<div className="card-detail__portrait card-detail__portrait_small">
|
|
||||||
<Image src={faceImageUrl || '/images/user-avatar.png'} width={86}
|
|
||||||
height={86}
|
|
||||||
alt=""/>
|
|
||||||
</div>
|
|
||||||
<div className="card-detail__inner">
|
|
||||||
<div
|
|
||||||
className="card-detail__name">{`${name} ${surname || ''}`}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
className="card-detail__filled"
|
|
||||||
onClick={() => submit(id)}
|
|
||||||
>
|
|
||||||
{i18nText('room.invite', locale)}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="b-users-list__empty">{i18nText('noData', locale)}</div>
|
|
||||||
)}
|
|
||||||
</Loader>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
'use client'
|
|
||||||
|
|
||||||
import React, { useEffect, useState } from 'react';
|
|
||||||
import { DatePicker } from 'antd';
|
|
||||||
import { CalendarOutlined } from '@ant-design/icons';
|
|
||||||
import 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';
|
|
||||||
|
|
||||||
export const CustomDatePicker = (props: any) => {
|
|
||||||
const { label, value, locale, ...other } = props;
|
|
||||||
const [isActiveLabel, setIsActiveLabel] = useState<boolean>(false);
|
|
||||||
|
|
||||||
dayjs.locale(locale);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (label) {
|
|
||||||
setIsActiveLabel(!!value);
|
|
||||||
} else {
|
|
||||||
setIsActiveLabel(false);
|
|
||||||
}
|
|
||||||
}, [value]);
|
|
||||||
|
|
||||||
const onOpenChange = (open: boolean) => {
|
|
||||||
if (open) {
|
|
||||||
if (!isActiveLabel) setIsActiveLabel(true)
|
|
||||||
} else {
|
|
||||||
setIsActiveLabel(!!value)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`b-datepicker-wrap ${isActiveLabel ? 'b-datepicker__active' : ''}`}>
|
|
||||||
<div className="b-datepicker-label">
|
|
||||||
<span>{label}</span>
|
|
||||||
</div>
|
|
||||||
<DatePicker
|
|
||||||
className="b-datepicker"
|
|
||||||
format="YYYY-MM-DD"
|
|
||||||
locale={getLocale(locale)}
|
|
||||||
value={value}
|
|
||||||
showNow={false}
|
|
||||||
onOpenChange={onOpenChange}
|
|
||||||
needConfirm={false}
|
|
||||||
placeholder=""
|
|
||||||
variant="filled"
|
|
||||||
allowClear={false}
|
|
||||||
popupClassName="b-datepicker-popup"
|
|
||||||
minDate={dayjs().startOf('month')}
|
|
||||||
suffixIcon={<CalendarOutlined style={{ color: '#2c7873', fontSize: 20 }} />}
|
|
||||||
{...other}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
|
@ -1,7 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
accountMenu: {
|
accountMenu: {
|
||||||
sessions: 'Kommende & letzte Sitzungen',
|
sessions: 'Kommende & letzte Sitzungen',
|
||||||
rooms: 'Zimmer',
|
|
||||||
notifications: 'Benachrichtigung',
|
notifications: 'Benachrichtigung',
|
||||||
support: 'Hilfe & Support',
|
support: 'Hilfe & Support',
|
||||||
information: 'Rechtliche Informationen',
|
information: 'Rechtliche Informationen',
|
||||||
|
@ -49,23 +48,7 @@ export default {
|
||||||
upcoming: 'Zukünftige Räume',
|
upcoming: 'Zukünftige Räume',
|
||||||
requested: 'Angeforderte Räume',
|
requested: 'Angeforderte Räume',
|
||||||
recent: 'Kürzliche Räume',
|
recent: 'Kürzliche Räume',
|
||||||
newRoom: 'Neuer Raum',
|
newRoom: 'Neuer Raum'
|
||||||
editRoom: 'Raum bearbeiten',
|
|
||||||
date: 'Datum',
|
|
||||||
time: 'Zeit',
|
|
||||||
maxParticipants: 'Max. erlaubte Teilnehmer',
|
|
||||||
presenceOfSupervisor: 'Anwesenheit eines Supervisors',
|
|
||||||
supervisor: 'Supervisor',
|
|
||||||
members: 'Mitglieder',
|
|
||||||
participants: 'Teilnehmer',
|
|
||||||
roomCreator: 'Raum-Ersteller',
|
|
||||||
inviteSupervisor: 'Supervisor einladen',
|
|
||||||
joinSupervisor: 'Als Supervisor beitreten',
|
|
||||||
inviteParticipant: 'Teilnehmer einladen',
|
|
||||||
joinParticipant: 'Als Teilnehmer beitreten',
|
|
||||||
rapport: 'Rapport',
|
|
||||||
invite: 'Invite',
|
|
||||||
save: 'Raum speichern'
|
|
||||||
},
|
},
|
||||||
agreementText: 'Folgendes habe ich gelesen und erkläre mich damit einverstanden: Benutzervereinbarung,',
|
agreementText: 'Folgendes habe ich gelesen und erkläre mich damit einverstanden: Benutzervereinbarung,',
|
||||||
userAgreement: 'Benutzervereinbarung',
|
userAgreement: 'Benutzervereinbarung',
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
accountMenu: {
|
accountMenu: {
|
||||||
sessions: 'Upcoming & Recent Sessions',
|
sessions: 'Upcoming & Recent Sessions',
|
||||||
rooms: 'Rooms',
|
|
||||||
notifications: 'Notification',
|
notifications: 'Notification',
|
||||||
support: 'Help & Support',
|
support: 'Help & Support',
|
||||||
information: 'Legal Information',
|
information: 'Legal Information',
|
||||||
|
@ -49,23 +48,7 @@ export default {
|
||||||
upcoming: 'Upcoming Rooms',
|
upcoming: 'Upcoming Rooms',
|
||||||
requested: 'Rooms Requested',
|
requested: 'Rooms Requested',
|
||||||
recent: 'Recent Rooms',
|
recent: 'Recent Rooms',
|
||||||
newRoom: 'New Room',
|
newRoom: 'New Room'
|
||||||
editRoom: 'Edit Room',
|
|
||||||
date: 'Date',
|
|
||||||
time: 'Time',
|
|
||||||
maxParticipants: 'Max Participants Allowed',
|
|
||||||
presenceOfSupervisor: 'Presence of a Supervisor',
|
|
||||||
supervisor: 'Supervisor',
|
|
||||||
members: 'Members',
|
|
||||||
participants: 'Participants',
|
|
||||||
roomCreator: 'Room Creator',
|
|
||||||
inviteSupervisor: 'Invite Supervisor',
|
|
||||||
joinSupervisor: 'Join As A Supervisor',
|
|
||||||
inviteParticipant: 'Invite Participant',
|
|
||||||
joinParticipant: 'Join as a participant',
|
|
||||||
rapport: 'Rapport',
|
|
||||||
invite: 'Invite',
|
|
||||||
save: 'Save room'
|
|
||||||
},
|
},
|
||||||
agreementText: 'I have read and agree with the terms of the User Agreement,',
|
agreementText: 'I have read and agree with the terms of the User Agreement,',
|
||||||
userAgreement: 'User Agreement',
|
userAgreement: 'User Agreement',
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
accountMenu: {
|
accountMenu: {
|
||||||
sessions: 'Próximas y recientes sesiones',
|
sessions: 'Próximas y recientes sesiones',
|
||||||
rooms: 'Habitaciones',
|
|
||||||
notifications: 'Notificación',
|
notifications: 'Notificación',
|
||||||
support: 'Ayuda y asistencia',
|
support: 'Ayuda y asistencia',
|
||||||
information: 'Información jurídica',
|
information: 'Información jurídica',
|
||||||
|
@ -49,23 +48,7 @@ export default {
|
||||||
upcoming: 'Próximas salas',
|
upcoming: 'Próximas salas',
|
||||||
requested: 'Salas solicitadas',
|
requested: 'Salas solicitadas',
|
||||||
recent: 'Salas recientes',
|
recent: 'Salas recientes',
|
||||||
newRoom: 'Nueva sala',
|
newRoom: 'Nueva sala'
|
||||||
editRoom: 'Editar la sala',
|
|
||||||
date: 'Fecha',
|
|
||||||
time: 'Tiempo',
|
|
||||||
maxParticipants: 'Máximo de participantes permitidos',
|
|
||||||
presenceOfSupervisor: 'Presencia de un supervisor',
|
|
||||||
supervisor: 'Supervisor',
|
|
||||||
members: 'Miembros',
|
|
||||||
participants: 'Participantes',
|
|
||||||
roomCreator: 'Creador de salas',
|
|
||||||
inviteSupervisor: 'Invitar al supervisor',
|
|
||||||
joinSupervisor: 'Unirse como supervisor',
|
|
||||||
inviteParticipant: 'Invitar a un participante',
|
|
||||||
joinParticipant: 'Unirse como participante',
|
|
||||||
rapport: 'Buena relación',
|
|
||||||
invite: 'Invitar',
|
|
||||||
save: 'Guardar sala'
|
|
||||||
},
|
},
|
||||||
agreementText: 'He leído y acepto las condiciones del Acuerdo de usuario,',
|
agreementText: 'He leído y acepto las condiciones del Acuerdo de usuario,',
|
||||||
userAgreement: 'Acuerdo de usuario',
|
userAgreement: 'Acuerdo de usuario',
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
accountMenu: {
|
accountMenu: {
|
||||||
sessions: 'Sessions futures et récentes',
|
sessions: 'Sessions futures et récentes',
|
||||||
rooms: 'Chambres',
|
|
||||||
notifications: 'Notification',
|
notifications: 'Notification',
|
||||||
support: 'Aide et support',
|
support: 'Aide et support',
|
||||||
information: 'Informations légales',
|
information: 'Informations légales',
|
||||||
|
@ -49,23 +48,7 @@ export default {
|
||||||
upcoming: 'Salles futures',
|
upcoming: 'Salles futures',
|
||||||
requested: 'Salles demandées',
|
requested: 'Salles demandées',
|
||||||
recent: 'Salles récentes',
|
recent: 'Salles récentes',
|
||||||
newRoom: 'Nouvelle salle',
|
newRoom: 'Nouvelle salle'
|
||||||
editRoom: 'Modifier la salle',
|
|
||||||
date: 'Date',
|
|
||||||
time: 'Temps',
|
|
||||||
maxParticipants: 'Max de participants autorisés',
|
|
||||||
presenceOfSupervisor: 'Présence d\'un superviseur',
|
|
||||||
supervisor: 'Superviseur',
|
|
||||||
members: 'Membres',
|
|
||||||
participants: 'Participants',
|
|
||||||
roomCreator: 'Créateur de la salle',
|
|
||||||
inviteSupervisor: 'Inviter un superviseur',
|
|
||||||
joinSupervisor: 'Rejoindre en tant que superviseur',
|
|
||||||
inviteParticipant: 'Inviter un participant',
|
|
||||||
joinParticipant: 'Rejoindre en tant que participant',
|
|
||||||
rapport: 'Rapport',
|
|
||||||
invite: 'Inviter',
|
|
||||||
save: 'Sauvegarder la salle'
|
|
||||||
},
|
},
|
||||||
agreementText: 'J\'ai lu et j\'accepte les dispositions de l\'Accord Utilisateur et de la',
|
agreementText: 'J\'ai lu et j\'accepte les dispositions de l\'Accord Utilisateur et de la',
|
||||||
userAgreement: '',
|
userAgreement: '',
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
accountMenu: {
|
accountMenu: {
|
||||||
sessions: 'Prossime e recenti sessioni',
|
sessions: 'Prossime e recenti sessioni',
|
||||||
rooms: 'Stanze',
|
|
||||||
notifications: 'Notifica',
|
notifications: 'Notifica',
|
||||||
support: 'Assistenza e supporto',
|
support: 'Assistenza e supporto',
|
||||||
information: 'Informazioni legali',
|
information: 'Informazioni legali',
|
||||||
|
@ -49,23 +48,7 @@ export default {
|
||||||
upcoming: 'Prossime sale',
|
upcoming: 'Prossime sale',
|
||||||
requested: 'Sale richieste',
|
requested: 'Sale richieste',
|
||||||
recent: 'Sale recenti',
|
recent: 'Sale recenti',
|
||||||
newRoom: 'Nuova sala',
|
newRoom: 'Nuova sala'
|
||||||
editRoom: 'Modifica sala',
|
|
||||||
date: 'Data',
|
|
||||||
time: 'Tempo',
|
|
||||||
maxParticipants: 'Numero massimo di partecipanti consentiti',
|
|
||||||
presenceOfSupervisor: 'Presenza di un relatore',
|
|
||||||
supervisor: 'Relatore',
|
|
||||||
members: 'Iscritti',
|
|
||||||
participants: 'Partecipanti',
|
|
||||||
roomCreator: 'Creatore sala',
|
|
||||||
inviteSupervisor: 'Invita relatore',
|
|
||||||
joinSupervisor: 'Partecipa come relatore',
|
|
||||||
inviteParticipant: 'Invita partecipante',
|
|
||||||
joinParticipant: 'Partecipa come partecipante',
|
|
||||||
rapport: 'Rapporto',
|
|
||||||
invite: 'Invita',
|
|
||||||
save: 'Salva sala'
|
|
||||||
},
|
},
|
||||||
agreementText: 'Ho letto e accetto i termini dell\'Accordo con l\'utente,',
|
agreementText: 'Ho letto e accetto i termini dell\'Accordo con l\'utente,',
|
||||||
userAgreement: '',
|
userAgreement: '',
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
export default {
|
export default {
|
||||||
accountMenu: {
|
accountMenu: {
|
||||||
sessions: 'Предстоящие и недавние сессии',
|
sessions: 'Предстоящие и недавние сессии',
|
||||||
rooms: 'Комнаты',
|
|
||||||
notifications: 'Уведомления',
|
notifications: 'Уведомления',
|
||||||
support: 'Служба поддержки',
|
support: 'Служба поддержки',
|
||||||
information: 'Юридическая информация',
|
information: 'Юридическая информация',
|
||||||
|
@ -49,23 +48,7 @@ export default {
|
||||||
upcoming: 'Предстоящие комнаты',
|
upcoming: 'Предстоящие комнаты',
|
||||||
requested: 'Запрошенные комнаты',
|
requested: 'Запрошенные комнаты',
|
||||||
recent: 'Недавние комнаты',
|
recent: 'Недавние комнаты',
|
||||||
newRoom: 'Новая комната',
|
newRoom: 'Новая комната'
|
||||||
editRoom: 'Изменить комнату',
|
|
||||||
date: 'Дата',
|
|
||||||
time: 'Время',
|
|
||||||
maxParticipants: 'Макс. кол-во участников',
|
|
||||||
presenceOfSupervisor: 'Присутствие супервизора',
|
|
||||||
supervisor: 'Супервайзер',
|
|
||||||
members: 'Участники',
|
|
||||||
participants: 'Участники',
|
|
||||||
roomCreator: 'Создатель комнаты',
|
|
||||||
inviteSupervisor: 'Пригласить супервизора',
|
|
||||||
joinSupervisor: 'Присоединиться как супервизор',
|
|
||||||
inviteParticipant: 'Пригласить участника',
|
|
||||||
joinParticipant: 'Присоединиться как участник',
|
|
||||||
rapport: 'Раппорт',
|
|
||||||
invite: 'Пригласить',
|
|
||||||
save: 'Сохранить комнату'
|
|
||||||
},
|
},
|
||||||
agreementText: 'Я прочитал и согласен с условиями Пользовательского соглашения,',
|
agreementText: 'Я прочитал и согласен с условиями Пользовательского соглашения,',
|
||||||
userAgreement: 'Пользовательского соглашения',
|
userAgreement: 'Пользовательского соглашения',
|
||||||
|
|
|
@ -668,7 +668,6 @@ a {
|
||||||
& > div {
|
& > div {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
padding-left: 1px;
|
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
|
@ -82,13 +82,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__users-list__content {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding: 40px;
|
|
||||||
gap: 24px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-modal-mask {
|
.ant-modal-mask {
|
||||||
|
|
|
@ -931,10 +931,6 @@
|
||||||
&.chosen {
|
&.chosen {
|
||||||
color: #D93E5C;
|
color: #D93E5C;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.history {
|
|
||||||
color: #c4c4c4;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,9 @@
|
||||||
&__wrap {
|
&__wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 716px;
|
height: 716px;
|
||||||
|
border-radius: 16px;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&__single {
|
|
||||||
border-radius: 16px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__container {
|
&__container {
|
||||||
|
@ -28,16 +25,6 @@
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
||||||
&_group {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
background: rgba(0, 59, 70, 0.4);
|
|
||||||
padding: 16px;
|
|
||||||
border-radius: 16px;
|
|
||||||
margin-top: 24px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__controls {
|
&__controls {
|
||||||
|
@ -139,44 +126,6 @@
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
&_groups {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 16px;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
border-radius: 16px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
video {
|
|
||||||
object-fit: contain !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.gr-1 {
|
|
||||||
& > div {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.gr-2, &.gr-3, &.gr-4 {
|
|
||||||
& > div {
|
|
||||||
flex: calc((100% - 16px) / 2) 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.gr-5, &.gr-6, &.gr-7, &.gr-8, &.gr-9 {
|
|
||||||
flex: calc((100% - 16px) / 3) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.gr-10, &.gr-11, &.gr-12, &.gr-13, &.gr-14, &.gr-15, &.gr-16 {
|
|
||||||
flex: calc((100% - 16px) / 4) 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__video {
|
&__video {
|
||||||
|
|
|
@ -18,11 +18,6 @@
|
||||||
background: lightgray 50%;
|
background: lightgray 50%;
|
||||||
box-shadow: 0 8px 16px 0 rgba(102, 165, 173, 0.32);
|
box-shadow: 0 8px 16px 0 rgba(102, 165, 173, 0.32);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&_small {
|
|
||||||
width: 86px;
|
|
||||||
height: 86px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__inner {
|
&__inner {
|
||||||
|
@ -46,17 +41,6 @@
|
||||||
line-height: 120%;
|
line-height: 120%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__supervisor-comment {
|
|
||||||
width: 100%;
|
|
||||||
background: #E4F5FA;
|
|
||||||
padding: 8px;
|
|
||||||
border-radius: 0 8px 8px 8px;
|
|
||||||
color: #66A5AD;
|
|
||||||
@include rem(13);
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 120%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__comments {
|
&__comments {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -216,31 +200,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__filled {
|
|
||||||
user-select: none;
|
|
||||||
outline: none !important;
|
|
||||||
border: none !important;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
border-radius: 8px !important;
|
|
||||||
background: #66A5AD !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
display: flex;
|
|
||||||
height: 54px !important;
|
|
||||||
padding: 15px 24px;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
color: #fff !important;
|
|
||||||
@include rem(15);
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 160%;
|
|
||||||
|
|
||||||
&:hover, &:active {
|
|
||||||
color: #fff !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding-bottom: 8px;
|
padding-bottom: 8px;
|
||||||
|
@ -309,54 +268,6 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__profile {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
padding-top: 16px;
|
|
||||||
align-items: flex-start;
|
|
||||||
border-top: 1px solid #C4DFE6;
|
|
||||||
|
|
||||||
&_title {
|
|
||||||
width: 100%;
|
|
||||||
gap: 16px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
div {
|
|
||||||
@include rem(18);
|
|
||||||
font-weight: 600;
|
|
||||||
line-height: 150%;
|
|
||||||
color: #6FB98F;
|
|
||||||
|
|
||||||
&:first-child {
|
|
||||||
color: #003B46;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&_list {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&_item {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.card-detail__inner {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-detail__name {
|
|
||||||
color: #2C7873;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__footer {
|
&__footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
th, td {
|
th, td {
|
||||||
|
|
|
@ -1,128 +0,0 @@
|
||||||
.b-datepicker {
|
|
||||||
width: 100% !important;
|
|
||||||
height: 54px !important;
|
|
||||||
|
|
||||||
&.ant-picker-filled {
|
|
||||||
background: transparent !important;
|
|
||||||
z-index: 1;
|
|
||||||
padding-top: 22px !important;
|
|
||||||
padding-left: 16px !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: #2c7873 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-input {
|
|
||||||
input {
|
|
||||||
font-size: 14px !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-suffix {
|
|
||||||
margin-top: -20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-wrap {
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
background-color: #F8F8F7;
|
|
||||||
border-radius: 8px;
|
|
||||||
|
|
||||||
&.b-datepicker__active .b-datepicker-label {
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 300;
|
|
||||||
line-height: 14px;
|
|
||||||
top: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-label {
|
|
||||||
font-size: 14px;
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 24px;
|
|
||||||
color: #000;
|
|
||||||
opacity: .3;
|
|
||||||
position: absolute;
|
|
||||||
left: 16px;
|
|
||||||
top: 15px;
|
|
||||||
right: 22px;
|
|
||||||
z-index: 0;
|
|
||||||
transition: all .1s ease;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
|
|
||||||
span {
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-popup {
|
|
||||||
padding: 16px !important;
|
|
||||||
|
|
||||||
.ant-picker-date-panel {
|
|
||||||
padding: 16px 8px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-header-view {
|
|
||||||
color: #2c7873 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-header {
|
|
||||||
border: none !important;
|
|
||||||
|
|
||||||
.ant-picker-header-super-prev-btn, .ant-picker-header-super-next-btn {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-cell {
|
|
||||||
opacity: 0 !important;
|
|
||||||
padding: 0 !important;
|
|
||||||
|
|
||||||
&:not(.ant-picker-cell-disabled) {
|
|
||||||
color: #66A5AD !important;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.ant-picker-cell-inner {
|
|
||||||
color: #6FB98F !important;
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-selected:not(.ant-picker-cell-disabled) .ant-picker-cell-inner {
|
|
||||||
color: #6FB98F !important;
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-disabled {
|
|
||||||
color: rgba(0, 0, 0, 0.25) !important;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.ant-picker-cell-in-view {
|
|
||||||
opacity: 1 !important;
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-picker-cell-inner::before {
|
|
||||||
border: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
th, td {
|
|
||||||
vertical-align: middle !important;
|
|
||||||
height: 36px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
th {
|
|
||||||
color: #66A5AD !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
.card-room {
|
|
||||||
&__details {
|
|
||||||
width: 100%;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 120px auto;
|
|
||||||
gap: 4px 8px;
|
|
||||||
|
|
||||||
div {
|
|
||||||
@include rem(13);
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 120%;
|
|
||||||
color: #2C7873;
|
|
||||||
|
|
||||||
&:nth-child(2n) {
|
|
||||||
color: #6FB98F;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.b-users-list {
|
|
||||||
width: 100%;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 24px;
|
|
||||||
padding: 0 16px;
|
|
||||||
|
|
||||||
&__empty {
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
|
|
||||||
&-item {
|
|
||||||
padding: 0 0 16px;
|
|
||||||
border-bottom: 1px solid #C4DFE6;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 16px;
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
border-bottom: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.b-room-form {
|
|
||||||
&__grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
gap: 16px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-form-item {
|
|
||||||
margin-bottom: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-detail__apply {
|
|
||||||
align-self: flex-start;
|
|
||||||
}
|
|
||||||
|
|
||||||
.b-room-switch {
|
|
||||||
label {
|
|
||||||
margin-right: 24px;
|
|
||||||
&:after {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-select-item-option-content {
|
|
||||||
span {
|
|
||||||
text-transform: capitalize;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,6 @@
|
||||||
border-radius: 8px !important;
|
border-radius: 8px !important;
|
||||||
padding: 22px 16px 8px !important;
|
padding: 22px 16px 8px !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
z-index: 1;
|
|
||||||
|
|
||||||
.ant-select-selection-item {
|
.ant-select-selection-item {
|
||||||
font-size: 15px !important;
|
font-size: 15px !important;
|
||||||
|
@ -54,7 +53,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&-label {
|
&-label {
|
||||||
font-size: 14px;
|
font-size: 15px;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
.ant-picker-input {
|
.ant-picker-input {
|
||||||
input {
|
input {
|
||||||
font-size: 14px !important;
|
font-size: 15px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
@import "_practice.scss";
|
@import "_practice.scss";
|
||||||
@import "_collapse.scss";
|
@import "_collapse.scss";
|
||||||
@import "_timepicker.scss";
|
@import "_timepicker.scss";
|
||||||
@import "_datepicker.scss";
|
|
||||||
@import "_calendar.scss";
|
@import "_calendar.scss";
|
||||||
@import "_schedule.scss";
|
@import "_schedule.scss";
|
||||||
@import "_radio.scss";
|
@import "_radio.scss";
|
||||||
@import "_room.scss";
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import type { ChainModifiers, Entry, EntryFieldTypes, EntrySkeletonType, LocaleCode } from 'contentful'
|
import type { ChainModifiers, Entry, EntryFieldTypes, EntrySkeletonType, LocaleCode } from 'contentful'
|
||||||
|
import {BlogPostFields} from "./blogPost";
|
||||||
import {ContentImage} from "../lib/contentful/contentImage";
|
import {ContentImage} from "../lib/contentful/contentImage";
|
||||||
|
|
||||||
export interface AuthorFields {
|
export interface AuthorFields {
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
import { PublicUser, Session, SessionState } from './sessions';
|
|
||||||
import { Tag } from './tags';
|
|
||||||
import { Slot } from './experts';
|
|
||||||
|
|
||||||
export enum RoomsType {
|
|
||||||
UPCOMING = 'upcoming',
|
|
||||||
RECENT = 'recent',
|
|
||||||
NEW = 'new',
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Record = {
|
|
||||||
id: number;
|
|
||||||
sessionId: number;
|
|
||||||
sid?: string;
|
|
||||||
resourceId?: string;
|
|
||||||
readyForLoad?: boolean;
|
|
||||||
cname?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Room = Session & { recordings?: Record[] };
|
|
||||||
|
|
||||||
export type GetUsersForRooms = {
|
|
||||||
items?: PublicUser[],
|
|
||||||
isTooManyResults?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RoomEdit = {
|
|
||||||
id: number,
|
|
||||||
scheduledStartAtUtc?: string,
|
|
||||||
scheduledEndAtUtc?: string,
|
|
||||||
state?: SessionState,
|
|
||||||
cost?: number,
|
|
||||||
maxClients?: number,
|
|
||||||
title?: string,
|
|
||||||
description?: string,
|
|
||||||
isNeedSupervisor?: boolean,
|
|
||||||
tagIds?: number[]
|
|
||||||
};
|
|
||||||
|
|
||||||
export type RoomEditDTO = {
|
|
||||||
item: RoomEdit;
|
|
||||||
tags?: Tag[];
|
|
||||||
availableSlots: Slot[];
|
|
||||||
};
|
|
|
@ -6,8 +6,6 @@ export type PublicUser = {
|
||||||
name?: string;
|
name?: string;
|
||||||
surname?: string;
|
surname?: string;
|
||||||
faceImageUrl?: string;
|
faceImageUrl?: string;
|
||||||
coachBotId?: number;
|
|
||||||
parentId?: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// type User = {
|
// type User = {
|
||||||
|
@ -150,7 +148,6 @@ export type Session = {
|
||||||
themesTags?: SessionTag[];
|
themesTags?: SessionTag[];
|
||||||
coachComments?: SessionComment[];
|
coachComments?: SessionComment[];
|
||||||
clientComments?: SessionComment[];
|
clientComments?: SessionComment[];
|
||||||
creatorId?: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export enum SessionType {
|
export enum SessionType {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { message } from 'antd';
|
||||||
import type { UploadFile } from 'antd';
|
import type { UploadFile } from 'antd';
|
||||||
import { i18nText } from '../i18nKeys';
|
import { i18nText } from '../i18nKeys';
|
||||||
|
|
||||||
const ROUTES = ['sessions', 'rooms', 'notifications', 'support', 'information', 'settings', 'messages', 'expert-profile'];
|
const ROUTES = ['sessions', 'notifications', 'support', 'information', 'settings', 'messages', 'expert-profile'];
|
||||||
const COUNTS: Record<string, number> = {
|
const COUNTS: Record<string, number> = {
|
||||||
sessions: 12,
|
sessions: 12,
|
||||||
notifications: 5,
|
notifications: 5,
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
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';
|
|
||||||
|
|
||||||
// for calendars
|
|
||||||
export 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;
|
|
||||||
};
|
|
Loading…
Reference in New Issue