feat: add sessions actions
This commit is contained in:
parent
8f00a5c41c
commit
0828e944b4
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 106 KiB |
|
@ -1,7 +1,6 @@
|
||||||
import { AxiosResponse } from 'axios';
|
import { AxiosResponse } from 'axios';
|
||||||
import { apiClient } from '../lib/apiClient';
|
import { apiClient } from '../lib/apiClient';
|
||||||
import { Profile } from '../types/profile';
|
import { Profile } from '../types/profile';
|
||||||
import { Session, SessionsFilter } from '../types/sessions';
|
|
||||||
|
|
||||||
export const setPersonData = (person: { login: string, password: string, role: string, languagesLinks: any[] }, locale: string, jwt: string): Promise<AxiosResponse<{ userData: Profile }>> => (
|
export const setPersonData = (person: { login: string, password: string, role: string, languagesLinks: any[] }, locale: string, jwt: string): Promise<AxiosResponse<{ userData: Profile }>> => (
|
||||||
apiClient.post(
|
apiClient.post(
|
||||||
|
@ -28,61 +27,3 @@ export const getPersonalData = (locale: string, jwt: string): Promise<AxiosRespo
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
export const getUpcomingSessions = (locale: string, jwt: string, filter?: SessionsFilter): Promise<AxiosResponse<Session[]>> => (
|
|
||||||
apiClient.post(
|
|
||||||
'/home/upcomingsessionsall',
|
|
||||||
{
|
|
||||||
sessionType: 'session',
|
|
||||||
...(filter || {})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'X-User-Language': locale,
|
|
||||||
Authorization: `Bearer ${jwt}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
export const getRequestedSessions = (locale: string, jwt: string): Promise<AxiosResponse<{ requestedSessions: Session[] }>> => (
|
|
||||||
apiClient.post(
|
|
||||||
'/home/coachhomedata',
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'X-User-Language': locale,
|
|
||||||
Authorization: `Bearer ${jwt}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
export const getRecentSessions = (locale: string, jwt: string, filter?: SessionsFilter): Promise<AxiosResponse<Session[]>> => (
|
|
||||||
apiClient.post(
|
|
||||||
'/home/historicalmeetings',
|
|
||||||
{
|
|
||||||
sessionType: 'session',
|
|
||||||
...(filter || {})
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'X-User-Language': locale,
|
|
||||||
Authorization: `Bearer ${jwt}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
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}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
import { AxiosResponse } from 'axios';
|
||||||
|
import { apiClient } from '../lib/apiClient';
|
||||||
|
import { DeclineSessionData, Session, SessionsFilter, SessionCommentData } from '../types/sessions';
|
||||||
|
|
||||||
|
export const getUpcomingSessions = (locale: string, jwt: string, filter?: SessionsFilter): Promise<AxiosResponse<Session[]>> => (
|
||||||
|
apiClient.post(
|
||||||
|
'/home/upcomingsessionsall',
|
||||||
|
{
|
||||||
|
sessionType: 'session',
|
||||||
|
...(filter || {})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'X-User-Language': locale,
|
||||||
|
Authorization: `Bearer ${jwt}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getRequestedSessions = (locale: string, jwt: string): Promise<AxiosResponse<{ requestedSessions: Session[] }>> => (
|
||||||
|
apiClient.post(
|
||||||
|
'/home/coachhomedata',
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'X-User-Language': locale,
|
||||||
|
Authorization: `Bearer ${jwt}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const getRecentSessions = (locale: string, jwt: string, filter?: SessionsFilter): Promise<AxiosResponse<Session[]>> => (
|
||||||
|
apiClient.post(
|
||||||
|
'/home/historicalmeetings',
|
||||||
|
{
|
||||||
|
sessionType: 'session',
|
||||||
|
...(filter || {})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'X-User-Language': locale,
|
||||||
|
Authorization: `Bearer ${jwt}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
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}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const approveRequestedSession = (locale: string, jwt: string, sessionId: number): Promise<AxiosResponse> => (
|
||||||
|
apiClient.post(
|
||||||
|
'/home/approverequestedsession',
|
||||||
|
{ sessionId },
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'X-User-Language': locale,
|
||||||
|
Authorization: `Bearer ${jwt}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const declineRequestedSession = (locale: string, jwt: string, { sessionId, reason }: DeclineSessionData): Promise<AxiosResponse> => (
|
||||||
|
apiClient.post(
|
||||||
|
'/home/declinerequestedsession',
|
||||||
|
{
|
||||||
|
sessionId,
|
||||||
|
coachDeclineReason: reason
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'X-User-Language': locale,
|
||||||
|
Authorization: `Bearer ${jwt}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const cancelUpcomingSession = (locale: string, jwt: string, { sessionId, reason }: DeclineSessionData): Promise<AxiosResponse> => (
|
||||||
|
apiClient.post(
|
||||||
|
'/home/cancelupcomingsession',
|
||||||
|
{
|
||||||
|
sessionId,
|
||||||
|
coachCancelReason: reason
|
||||||
|
},
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'X-User-Language': locale,
|
||||||
|
Authorization: `Bearer ${jwt}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
export const addSessionComment = (locale: string, jwt: string, data: SessionCommentData): Promise<AxiosResponse> => (
|
||||||
|
apiClient.post(
|
||||||
|
'/home/session_comment',
|
||||||
|
data,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'X-User-Language': locale,
|
||||||
|
Authorization: `Bearer ${jwt}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
|
@ -1,7 +1,7 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { Tag, Button } from 'antd';
|
import {Tag, Button, notification, Empty} from 'antd';
|
||||||
import { RightOutlined, PlusOutlined, LeftOutlined } from '@ant-design/icons';
|
import { RightOutlined, PlusOutlined, LeftOutlined } from '@ant-design/icons';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
@ -10,9 +10,11 @@ import { i18nText } from '../../../i18nKeys';
|
||||||
import { getDuration, getPrice } from '../../../utils/expert';
|
import { getDuration, getPrice } from '../../../utils/expert';
|
||||||
import { PublicUser, Session } from '../../../types/sessions';
|
import { PublicUser, Session } from '../../../types/sessions';
|
||||||
import { AUTH_TOKEN_KEY, AUTH_USER } from '../../../constants/common';
|
import { AUTH_TOKEN_KEY, AUTH_USER } from '../../../constants/common';
|
||||||
import { getSessionDetails } from '../../../actions/profile';
|
import { approveRequestedSession, getSessionDetails } from '../../../actions/sessions';
|
||||||
import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
||||||
import { Loader } from '../../view/Loader';
|
import { Loader } from '../../view/Loader';
|
||||||
|
import { DeclineSessionModal } from '../../Modals/DeclineSessionModal';
|
||||||
|
import { AddCommentModal } from '../../Modals/AddCommentModal';
|
||||||
|
|
||||||
type SessionDetailsProps = {
|
type SessionDetailsProps = {
|
||||||
locale: string;
|
locale: string;
|
||||||
|
@ -25,10 +27,11 @@ export const SessionDetails = ({ sessionId, locale, goBack, activeTab }: Session
|
||||||
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||||||
const [userId] = useLocalStorage(AUTH_USER, '');
|
const [userId] = useLocalStorage(AUTH_USER, '');
|
||||||
const [loading, setLoading] = useState<boolean>(false);
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const [approveLoading, setApproveLoading] = useState<boolean>(false);
|
||||||
const [errorData, setErrorData] = useState<any>();
|
const [errorData, setErrorData] = useState<any>();
|
||||||
const [session, setSession] = useState<Session>();
|
const [session, setSession] = useState<Session>();
|
||||||
|
const [openDeclineModal, setOpenDeclineModal] = useState<boolean>(false);
|
||||||
console.log('activeTab', activeTab);
|
const [openAddCommentModal, setOpenAddCommentModal] = useState<boolean>(false);
|
||||||
|
|
||||||
const fetchData = useCallback(() => {
|
const fetchData = useCallback(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
@ -37,7 +40,6 @@ export const SessionDetails = ({ sessionId, locale, goBack, activeTab }: Session
|
||||||
|
|
||||||
getSessionDetails(locale, jwt, sessionId)
|
getSessionDetails(locale, jwt, sessionId)
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
console.log(data);
|
|
||||||
setSession(data);
|
setSession(data);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
@ -52,6 +54,25 @@ export const SessionDetails = ({ sessionId, locale, goBack, activeTab }: Session
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [sessionId]);
|
}, [sessionId]);
|
||||||
|
|
||||||
|
const onApproveSession = (tab: typeof activeTab) => {
|
||||||
|
if (tab === 1) {
|
||||||
|
setApproveLoading(true);
|
||||||
|
approveRequestedSession(locale, jwt, sessionId)
|
||||||
|
.then(() => {
|
||||||
|
goBack();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: err?.response?.data?.errMessage
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const startDate = session?.scheduledStartAtUtc ? dayjs(session?.scheduledStartAtUtc).locale(locale) : null;
|
const startDate = session?.scheduledStartAtUtc ? dayjs(session?.scheduledStartAtUtc).locale(locale) : null;
|
||||||
const endDate = session?.scheduledEndAtUtc ? dayjs(session?.scheduledEndAtUtc).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;
|
const today = startDate ? dayjs().format('YYYY-MM-DD') === startDate.format('YYYY-MM-DD') : false;
|
||||||
|
@ -126,7 +147,8 @@ export const SessionDetails = ({ sessionId, locale, goBack, activeTab }: Session
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
const client = session?.clients?.length ? session?.clients[0] : null;
|
const client = session?.clients?.length ? session?.clients[0] : null;
|
||||||
const Current = +userId !== client?.id ? StudentCard(client) : CoachCard(session?.coach);
|
const isCoach = +userId !== client?.id;
|
||||||
|
const Current = isCoach ? StudentCard(client) : CoachCard(session?.coach);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Loader
|
<Loader
|
||||||
|
@ -148,16 +170,29 @@ export const SessionDetails = ({ sessionId, locale, goBack, activeTab }: Session
|
||||||
{Current}
|
{Current}
|
||||||
{(activeTab === 0 || activeTab === 1) && (
|
{(activeTab === 0 || activeTab === 1) && (
|
||||||
<div className="card-detail__actions">
|
<div className="card-detail__actions">
|
||||||
{activeTab === 0 ? (
|
<Button
|
||||||
<>
|
className="card-detail__apply"
|
||||||
<Button className="card-detail__apply">Start Session</Button>
|
onClick={() => onApproveSession(activeTab)}
|
||||||
<Button className="card-detail__decline">Decline Session</Button>
|
loading={approveLoading}
|
||||||
</>
|
>
|
||||||
) : (
|
{activeTab === 0 ? 'Start Session' : 'Confirm Session'}
|
||||||
<>
|
</Button>
|
||||||
<Button className="card-detail__apply">Confirm Session</Button>
|
<Button
|
||||||
<Button className="card-detail__decline">Decline Session</Button>
|
className="card-detail__decline"
|
||||||
</>
|
onClick={() => setOpenDeclineModal(true)}
|
||||||
|
disabled={approveLoading}
|
||||||
|
>
|
||||||
|
Decline Session
|
||||||
|
</Button>
|
||||||
|
{session?.id && (
|
||||||
|
<DeclineSessionModal
|
||||||
|
open={openDeclineModal}
|
||||||
|
handleCancel={() => setOpenDeclineModal(false)}
|
||||||
|
activeTab={activeTab}
|
||||||
|
locale={locale}
|
||||||
|
sessionId={session.id}
|
||||||
|
success={goBack}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -196,28 +231,52 @@ export const SessionDetails = ({ sessionId, locale, goBack, activeTab }: Session
|
||||||
<div className="card-detail__comments">
|
<div className="card-detail__comments">
|
||||||
<div className="card-detail__comments_header">
|
<div className="card-detail__comments_header">
|
||||||
<div className="card-detail__comments_title">
|
<div className="card-detail__comments_title">
|
||||||
My Comments
|
{session?.clientComments?.length === 0 && session?.coachComments?.length === 0 ? 'Comments' : 'My Comments'}
|
||||||
</div>
|
</div>
|
||||||
{activeTab === 0 && (
|
{activeTab === 0 && (
|
||||||
|
<>
|
||||||
<Button
|
<Button
|
||||||
className="card-detail__comments_add"
|
className="card-detail__comments_add"
|
||||||
type="link"
|
type="link"
|
||||||
iconPosition="end"
|
iconPosition="end"
|
||||||
icon={<PlusOutlined style={{ fontSize: 18 }} />}
|
icon={<PlusOutlined style={{ fontSize: 18 }} />}
|
||||||
|
onClick={() => setOpenAddCommentModal(true)}
|
||||||
>
|
>
|
||||||
Add new
|
Add new
|
||||||
</Button>
|
</Button>
|
||||||
|
<AddCommentModal
|
||||||
|
open={openAddCommentModal}
|
||||||
|
handleCancel={() => setOpenAddCommentModal(false)}
|
||||||
|
locale={locale}
|
||||||
|
sessionId={sessionId}
|
||||||
|
refresh={fetchData}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="card-detail__comments_item">
|
{(session?.clientComments?.length > 0 || session?.coachComments?.length > 0) ? (
|
||||||
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.
|
<>
|
||||||
|
{(isCoach ? session?.coachComments : session?.clientComments)?.map(({ id, comment }) => (
|
||||||
|
<div key={`my_${id}`} className="card-detail__comments_item">
|
||||||
|
{comment}
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
{(isCoach ? session?.clientComments : session?.coachComments)?.length > 0 && (
|
||||||
<div className="card-detail__comments_title">
|
<div className="card-detail__comments_title">
|
||||||
Coach Comments
|
{isCoach ? 'Client Comments' : 'Coach Comments'}
|
||||||
</div>
|
</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.
|
{(isCoach ? session?.clientComments : session?.coachComments)?.map(({ id , comment }) => (
|
||||||
|
<div key={`oth_${id}`} className="card-detail__comments_item">
|
||||||
|
{comment}
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import 'dayjs/locale/es';
|
||||||
import { Loader } from '../../view/Loader';
|
import { Loader } from '../../view/Loader';
|
||||||
import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
||||||
import { AUTH_TOKEN_KEY, AUTH_USER } from '../../../constants/common';
|
import { AUTH_TOKEN_KEY, AUTH_USER } from '../../../constants/common';
|
||||||
import { getRecentSessions, getRequestedSessions, getUpcomingSessions } from '../../../actions/profile';
|
import { getRecentSessions, getRequestedSessions, getUpcomingSessions } from '../../../actions/sessions';
|
||||||
import { Session, Sessions, SessionType } from '../../../types/sessions';
|
import { Session, Sessions, SessionType } from '../../../types/sessions';
|
||||||
import { i18nText } from '../../../i18nKeys';
|
import { i18nText } from '../../../i18nKeys';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { FC, useEffect, useState } from 'react';
|
||||||
|
import { Modal, Form, Input, notification, Button } from 'antd';
|
||||||
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import utc from 'dayjs/plugin/utc';
|
||||||
|
import { i18nText } from '../../i18nKeys';
|
||||||
|
import { addSessionComment } from '../../actions/sessions';
|
||||||
|
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
||||||
|
import { AUTH_TOKEN_KEY } from '../../constants/common';
|
||||||
|
|
||||||
|
dayjs.extend(utc);
|
||||||
|
|
||||||
|
type AddCommentModalProps = {
|
||||||
|
open: boolean;
|
||||||
|
handleCancel: () => void;
|
||||||
|
locale: string;
|
||||||
|
sessionId: number;
|
||||||
|
refresh: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AddCommentModal: FC<AddCommentModalProps> = ({
|
||||||
|
open,
|
||||||
|
handleCancel,
|
||||||
|
locale,
|
||||||
|
sessionId,
|
||||||
|
refresh
|
||||||
|
}) => {
|
||||||
|
const [form] = Form.useForm<{ comment: string }>();
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||||||
|
|
||||||
|
const onAfterClose = () => {
|
||||||
|
form.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (form) {
|
||||||
|
form.resetFields();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const onAddComment = () => {
|
||||||
|
form.validateFields().then(({ comment }) => {
|
||||||
|
const createdAtUtc = dayjs().utc().format();
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
addSessionComment(locale, jwt, { sessionId, comment, createdAtUtc })
|
||||||
|
.then(() => {
|
||||||
|
handleCancel();
|
||||||
|
refresh();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: err?.response?.data?.errMessage
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
className="b-modal"
|
||||||
|
open={open}
|
||||||
|
title={undefined}
|
||||||
|
onOk={undefined}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
afterClose={onAfterClose}
|
||||||
|
footer={false}
|
||||||
|
width={498}
|
||||||
|
closeIcon={<CloseOutlined style={{ fontSize: 20, color: '#000' }}/>}
|
||||||
|
>
|
||||||
|
<div className="b-modal__comment__content">
|
||||||
|
<Form form={form} style={{ width: '100%' }}>
|
||||||
|
<Form.Item
|
||||||
|
name="comment"
|
||||||
|
noStyle
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: 'Please input your comment'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input.TextArea
|
||||||
|
className="b-textarea"
|
||||||
|
rows={4}
|
||||||
|
maxLength={1000}
|
||||||
|
placeholder="Describe the reason for the rejection"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
<div className="b-modal__decline__button">
|
||||||
|
<Button
|
||||||
|
className="card-detail__apply"
|
||||||
|
onClick={onAddComment}
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
Send
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
|
@ -0,0 +1,114 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { FC, useEffect, useState } from 'react';
|
||||||
|
import { Modal, Form, Input, notification } from 'antd';
|
||||||
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
|
import { FilledButton } from '../view/FilledButton';
|
||||||
|
import { i18nText } from '../../i18nKeys';
|
||||||
|
import { cancelUpcomingSession, declineRequestedSession } from '../../actions/sessions';
|
||||||
|
import { useLocalStorage } from '../../hooks/useLocalStorage';
|
||||||
|
import { AUTH_TOKEN_KEY } from '../../constants/common';
|
||||||
|
|
||||||
|
type DeclineModalProps = {
|
||||||
|
open: boolean;
|
||||||
|
handleCancel: () => void;
|
||||||
|
activeTab: 0 | 1;
|
||||||
|
locale: string;
|
||||||
|
sessionId: number;
|
||||||
|
success: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DeclineSessionModal: FC<DeclineModalProps> = ({
|
||||||
|
open,
|
||||||
|
handleCancel,
|
||||||
|
activeTab,
|
||||||
|
locale,
|
||||||
|
sessionId,
|
||||||
|
success
|
||||||
|
}) => {
|
||||||
|
const [form] = Form.useForm<{ reason: string }>();
|
||||||
|
const [loading, setLoading] = useState<boolean>(false);
|
||||||
|
const [jwt] = useLocalStorage(AUTH_TOKEN_KEY, '');
|
||||||
|
|
||||||
|
const onAfterClose = () => {
|
||||||
|
form.resetFields();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (form) {
|
||||||
|
form.resetFields();
|
||||||
|
}
|
||||||
|
}, [activeTab]);
|
||||||
|
|
||||||
|
const onDecline = () => {
|
||||||
|
form.validateFields().then(({ reason }) => {
|
||||||
|
const fetchFunc = activeTab === 0 ? cancelUpcomingSession : declineRequestedSession;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
fetchFunc(locale, jwt, { sessionId, reason })
|
||||||
|
.then(() => {
|
||||||
|
success();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
notification.error({
|
||||||
|
message: 'Error',
|
||||||
|
description: err?.response?.data?.errMessage
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
className="b-modal"
|
||||||
|
open={open}
|
||||||
|
title={undefined}
|
||||||
|
onOk={undefined}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
afterClose={onAfterClose}
|
||||||
|
footer={false}
|
||||||
|
width={498}
|
||||||
|
closeIcon={<CloseOutlined style={{ fontSize: 20, color: '#000' }}/>}
|
||||||
|
>
|
||||||
|
<div className="b-modal__decline__content">
|
||||||
|
<div className="b-modal__decline__logo">
|
||||||
|
<img className="" src="/images/decline-sign.svg" alt=""/>
|
||||||
|
</div>
|
||||||
|
<div className="b-modal__decline__title">
|
||||||
|
Enter a reason for cancelling the session
|
||||||
|
</div>
|
||||||
|
<Form form={form} style={{ width: '100%' }}>
|
||||||
|
<Form.Item
|
||||||
|
name="reason"
|
||||||
|
noStyle
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: 'Please input the reason'
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input.TextArea
|
||||||
|
className="b-textarea"
|
||||||
|
rows={1}
|
||||||
|
placeholder="Describe the reason for the rejection"
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
<div className="b-modal__decline__button">
|
||||||
|
<FilledButton
|
||||||
|
type="primary"
|
||||||
|
danger
|
||||||
|
onClick={onDecline}
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
Decline
|
||||||
|
</FilledButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
|
|
||||||
export const FilledButton = (props: any) => (
|
export const FilledButton = (props: any) => (
|
||||||
<Button className="b-button__filled" {...props}>
|
<Button className={`b-button__filled${props?.danger ? ' danger': ''}`} {...props}>
|
||||||
{props.children}
|
{props.children}
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
|
@ -16,9 +16,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ant-modal-footer {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
&__content {
|
&__content {
|
||||||
p {
|
p {
|
||||||
margin: 16px 0;
|
margin: 16px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__comment__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 44px 40px;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-modal-mask {
|
||||||
|
background-color: rgba(0, 59, 70, 0.4) !important;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
.b-modal__decline {
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: stretch;
|
||||||
|
margin: 44px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__title {
|
||||||
|
color: #2C7873;
|
||||||
|
@include rem(18);
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__button {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
@import "_details.scss";
|
@import "_details.scss";
|
||||||
|
@import "_decline-modal.scss";
|
||||||
|
|
|
@ -5,6 +5,11 @@
|
||||||
border-radius: 8px !important;
|
border-radius: 8px !important;
|
||||||
height: 54px !important;
|
height: 54px !important;
|
||||||
box-shadow: 0px 2px 4px 0px rgba(102, 165, 173, 0.32) !important;
|
box-shadow: 0px 2px 4px 0px rgba(102, 165, 173, 0.32) !important;
|
||||||
|
|
||||||
|
&.danger {
|
||||||
|
background: #D93E5C !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__link {
|
&__link {
|
||||||
|
|
|
@ -51,3 +51,27 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.b-textarea {
|
||||||
|
padding: 15px 16px !important;
|
||||||
|
background: #F8F8F7 !important;
|
||||||
|
border: 1px solid #F8F8F7 !important;
|
||||||
|
border-radius: 8px !important;
|
||||||
|
color: #000 !important;
|
||||||
|
align-items: center;
|
||||||
|
resize: none !important;
|
||||||
|
|
||||||
|
&:focus, &:hover, &:focus-within {
|
||||||
|
border-color: #66A5AD !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::placeholder {
|
||||||
|
color: #000 !important;
|
||||||
|
opacity: .4 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.ant-input-status-error:not(.ant-input-disabled):not(.ant-input-borderless) {
|
||||||
|
border-color: #ff4d4f !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ export type SessionsFilter = {
|
||||||
export type SessionComment = {
|
export type SessionComment = {
|
||||||
id: number;
|
id: number;
|
||||||
createdAtUtc: string;
|
createdAtUtc: string;
|
||||||
comment?: string;
|
comment: string;
|
||||||
author?: PublicUser;
|
author?: PublicUser;
|
||||||
authorId?: number;
|
authorId?: number;
|
||||||
sessionId?: number;
|
sessionId?: number;
|
||||||
|
@ -150,3 +150,14 @@ export type Sessions = {
|
||||||
[SessionType.REQUESTED]?: Session[];
|
[SessionType.REQUESTED]?: Session[];
|
||||||
[SessionType.RECENT]?: Session[];
|
[SessionType.RECENT]?: Session[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DeclineSessionData = {
|
||||||
|
sessionId: number;
|
||||||
|
reason: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SessionCommentData = {
|
||||||
|
createdAtUtc: string;
|
||||||
|
comment: string;
|
||||||
|
sessionId: number;
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import { SearchData } from '../types/tags';
|
|
||||||
import { AdditionalFilter, Filter, GeneralFilter } from '../types/experts';
|
import { AdditionalFilter, Filter, GeneralFilter } from '../types/experts';
|
||||||
import { DEFAULT_PAGE } from '../constants/common';
|
import { DEFAULT_PAGE } from '../constants/common';
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue