From b2b6410eed45c123fa77dab6c184c6d8cda4429d Mon Sep 17 00:00:00 2001 From: olena <154923065+nebby2105@users.noreply.github.com> Date: Wed, 4 Dec 2024 15:17:20 +0200 Subject: [PATCH] close cooperation process (#2900) * Add possibility for a student to close a cooperation * add functionality to close cooperation * add colors to palette * add some tests * add test for CooperationContainer * one more test for CooperationContainer * add one more test for CooperationCompletion * removed onResponseError import * add test for CooperationContainer * implemented comments suggestions * fixed test * add test for handleCooperationStatusUpdate func * delete unused test * added useCallback and fixed translation * changed || to ?? * fixed comments * fixed tests --- .../status-chip/StatusChip.styles.ts | 3 +- .../translations/en/cooperation-details.json | 6 +- .../translations/en/cooperations-page.json | 9 ++- src/constants/translations/en/titles.json | 4 +- .../translations/uk/cooperation-details.json | 6 +- .../translations/uk/cooperations-page.json | 16 ++-- src/constants/translations/uk/titles.json | 4 +- .../AcceptCooperationClosing.styles.ts | 27 +++++++ .../AcceptCooperationClosing.tsx | 43 ++++++++++ .../CooperationCompletion.tsx | 42 +++++++++- .../CooperationDetails.tsx | 45 ++++++++++- .../CooperationContainer.tsx | 5 +- .../MyCooperationsDetails.tsx | 53 ++++++++++-- src/redux/features/cooperationsSlice.ts | 13 ++- src/styles/app-theme/app.pallete.ts | 5 ++ src/types/common/enums/common.enums.ts | 3 +- .../interfaces/cooperation.interface.ts | 5 ++ .../cooperation/types/cooperation.types.ts | 4 + .../AcceptCooperationClosing.spec.jsx | 15 ++++ .../CooperationCompletion.spec.jsx | 46 +++++++++++ .../CooperationContainer.spec.jsx | 81 ++++++++++++++++++- 21 files changed, 404 insertions(+), 31 deletions(-) create mode 100644 src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.styles.ts create mode 100644 src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx create mode 100644 tests/unit/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.spec.jsx create mode 100644 tests/unit/containers/my-cooperations/cooperation-completion/CooperationCompletion.spec.jsx diff --git a/src/components/status-chip/StatusChip.styles.ts b/src/components/status-chip/StatusChip.styles.ts index d58a7eccf..20775fb80 100644 --- a/src/components/status-chip/StatusChip.styles.ts +++ b/src/components/status-chip/StatusChip.styles.ts @@ -7,7 +7,8 @@ const statusColors = { [StatusEnum.Active]: palette.success[600], [StatusEnum.Closed]: palette.primary[400], [StatusEnum.Draft]: palette.basic.blue, - [StatusEnum.NeedAction]: palette.error[600] + [StatusEnum.NeedAction]: palette.error[600], + [StatusEnum.RequestToClose]: palette.warning[500] } export const styles = (status: StatusEnum) => ({ diff --git a/src/constants/translations/en/cooperation-details.json b/src/constants/translations/en/cooperation-details.json index 5a92b8b50..83e61f3df 100644 --- a/src/constants/translations/en/cooperation-details.json +++ b/src/constants/translations/en/cooperation-details.json @@ -29,5 +29,9 @@ "yourProgress": "Your progress", "completed": "completed", "needToComplete": "to complete" - } + }, + "closingMessage1": " started a closing process for the current cooperation. You will have ", + "accessDuration": "1 month of access", + "closingMessage2": " to study materials after the cooperation has been closed.", + "acceptBtn": "Accept" } diff --git a/src/constants/translations/en/cooperations-page.json b/src/constants/translations/en/cooperations-page.json index ce6ffa854..10f3a307c 100644 --- a/src/constants/translations/en/cooperations-page.json +++ b/src/constants/translations/en/cooperations-page.json @@ -47,13 +47,13 @@ "or": "or ", "quizzes": "quizzes ", "resourcesLibrary": "from resource library.", - "seems": "Looks like there’re", + "seems": "Looks like there’re", "noActivities": " no activities added ", "engageTutor": "yet. As a student, you're here to engage with the materials provided by your tutor." }, "button": { "create": "Create Activity", - "add":"Add Activity" + "add": "Add Activity" }, "manyTypes": { "courseTemplate": "Course template", @@ -66,7 +66,7 @@ "closeCooperationDescription": "Start a closing process of current cooperation if your learning journey with the student is finished.", "closeCooperationBtn": "Close cooperation", "accessTitle": "Study materials access", - "accessDescription": "As a tutor, determine deadlines for student access to study materials after cooperation has finished.", + "accessDescription": "As a tutor, determine deadlines for student access to study materials after cooperation has finished.", "noAccess": "No access", "monthAccess": "month access", "yearAccess": "year access", @@ -83,5 +83,8 @@ "successUpdating": "Note was successfully updated", "confirmDeletionMessage": "This action is permanent and will remove all related content. Please review your decision before proceeding.", "confirmDeletionTitle": "Do you confirm note deletion?" + }, + "closeCooperationModal": { + "message": "Are you sure you want to close this cooperation?" } } diff --git a/src/constants/translations/en/titles.json b/src/constants/translations/en/titles.json index b2c930e3b..35bf7d745 100644 --- a/src/constants/translations/en/titles.json +++ b/src/constants/translations/en/titles.json @@ -1,5 +1,7 @@ { "confirmTitle": "Please Confirm", "discardOffer": "Discard offer changes?", - "discardChanges": "Discard recent changes?" + "discardChanges": "Discard recent changes?", + "confirmCooperationClosing": "Confirmation of cooperation closing", + "acceptCooperationClosing": "Cooperation closing process" } diff --git a/src/constants/translations/uk/cooperation-details.json b/src/constants/translations/uk/cooperation-details.json index ec734340e..6bffddeee 100644 --- a/src/constants/translations/uk/cooperation-details.json +++ b/src/constants/translations/uk/cooperation-details.json @@ -29,5 +29,9 @@ "yourProgress": "Ваш прогрес", "completed": "виконано", "needToComplete": "до завершення" - } + }, + "closingMessage1": " ініціював(-ла) процес припинення співпраці. У вас залишиться доступ до матеріалів поточної співпраці протягом ", + "accessDuration": "1 місяця", + "closingMessage2": " з моменту закриття.", + "acceptBtn": "Прийняти" } diff --git a/src/constants/translations/uk/cooperations-page.json b/src/constants/translations/uk/cooperations-page.json index 542696a12..91c257df8 100644 --- a/src/constants/translations/uk/cooperations-page.json +++ b/src/constants/translations/uk/cooperations-page.json @@ -48,12 +48,13 @@ "or": "або ", "quizzes": "тестами ", "resourcesLibrary": "з бібліотеки ресурсів.", - "seems": "Схоже, що зараз тут ", + "seems": "Схоже, що зараз тут ", "noActivities": " немає доданих активностей ", - "engageTutor": ". Як студента, Ваша задача - працювати з матеріалами, наданими Вашим викладачем." }, + "engageTutor": ". Як студента, Ваша задача - працювати з матеріалами, наданими Вашим викладачем." + }, "button": { "create": "Створення активності", - "add":"Додавання активності" + "add": "Додавання активності" }, "manyTypes": { "courseTemplate": "Шаблон курсу", @@ -61,10 +62,10 @@ "module": "Модуль" }, "cooperationDetails": { - "completionTitle": "Завершення кооперації", - "closeCooperationTitle": "Завершити кооперацію", + "completionTitle": "Завершення співпраці", + "closeCooperationTitle": "Завершити співпрацю", "closeCooperationDescription": "Розпочніть процес завершення поточної співпраці, якщо ваше навчання зі студентом закінчилося.", - "closeCooperationBtn": "Завершити кооперацію", + "closeCooperationBtn": "Завершити співпрацю", "accessTitle": "Доступ до навчальних матеріалів", "accessDescription": "Визначте терміни доступу до навчальних матеріалів після завершення співпраці зі студентом.", "noAccess": "Не має доступу", @@ -83,5 +84,8 @@ "successUpdating": "Нотатку було успішно оновлено", "confirmDeletionMessage": "Ця дія є постійною та призведе до видалення всього пов’язаного вмісту. Перш ніж продовжити, перегляньте своє рішення.", "confirmDeletionTitle": "Ви підтверджуєте видалення нотатки?" + }, + "closeCooperationModal": { + "message": "Ви впевнені, що хочете завершити співпрацю?" } } diff --git a/src/constants/translations/uk/titles.json b/src/constants/translations/uk/titles.json index b767f3424..2f028fd50 100644 --- a/src/constants/translations/uk/titles.json +++ b/src/constants/translations/uk/titles.json @@ -1,5 +1,7 @@ { "confirmTitle": "Підтвердіть вихід", "discardOffer": "Відхилити зміни пропозиції?", - "discardChanges": "Зберегти останні зміни?" + "discardChanges": "Зберегти останні зміни?", + "confirmCooperationClosing": "Підтвердження припинення cпівпраці", + "acceptCooperationClosing": "Процес припинення співпраці" } diff --git a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.styles.ts b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.styles.ts new file mode 100644 index 000000000..90c1c547f --- /dev/null +++ b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.styles.ts @@ -0,0 +1,27 @@ +import palette from '~/styles/app-theme/app.pallete' + +export const styles = { + root: { + display: 'flex', + alignItems: 'center', + border: `1px solid ${palette.basic.pinkishRed}`, + backgroundColor: palette.basic.softGray, + borderRadius: '5px', + padding: '24px', + justifyContent: 'space-between', + gap: '16px' + }, + span: { + fontWeight: '500' + }, + title: { + color: palette.basic.mediumRed, + fontWeight: '500', + display: 'flex', + gap: '8px', + mb: '4px' + }, + body: { + color: palette.basic.darkGray + } +} diff --git a/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx new file mode 100644 index 000000000..543dc85c2 --- /dev/null +++ b/src/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.tsx @@ -0,0 +1,43 @@ +import { Box, styled, Typography } from '@mui/material' +import { FC } from 'react' +import { styles } from './AcceptCooperationClosing.styles' +import { ErrorOutlineRounded } from '@mui/icons-material' +import Button from '~/design-system/components/button/Button' +import { useTranslation } from 'react-i18next' + +interface AcceptCooperationClosureProps { + user: string + onAccept: () => void +} + +const BoldText = styled('span')({ + fontWeight: 500 +}) + +const AcceptCooperationClosing: FC = ({ + user, + onAccept +}) => { + const { t } = useTranslation() + return ( + + + + + {t('titles.acceptCooperationClosing')} + + + {user} + {t('cooperationDetailsPage.closingMessage1')} + {t('cooperationDetailsPage.accessDuration')} + {t('cooperationDetailsPage.closingMessage2')} + + + + + ) +} + +export default AcceptCooperationClosing diff --git a/src/containers/my-cooperations/cooperation-completion/CooperationCompletion.tsx b/src/containers/my-cooperations/cooperation-completion/CooperationCompletion.tsx index 24648218e..6ad54130f 100644 --- a/src/containers/my-cooperations/cooperation-completion/CooperationCompletion.tsx +++ b/src/containers/my-cooperations/cooperation-completion/CooperationCompletion.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react' import { useTranslation } from 'react-i18next' import Box from '@mui/material/Box' import Typography from '@mui/material/Typography' @@ -5,12 +6,33 @@ import Divider from '@mui/material/Divider' import SettingItem from '~/components/setting-item/SettingItem' import AppButton from '~/components/app-button/AppButton' +import AppSelect from '~/components/app-select/AppSelect' +import { cooperationAccessValues } from '~/containers/my-cooperations/cooperation-completion/CooperationCompletion.constants' import { styles } from '~/containers/my-cooperations/cooperation-completion/CooperationCompletion.styles' -import { ButtonVariantEnum, SizeEnum } from '~/types' +import { + ButtonVariantEnum, + SizeEnum, + CooperationMaterialsAccessEnum, + UserRoleEnum, + StatusEnum +} from '~/types' +interface CooperationCompletionProps { + cooperationStatus: StatusEnum + onCloseCooperation: () => void + userRole: UserRoleEnum | '' +} -const CooperationCompletion = () => { +const CooperationCompletion: React.FC = ({ + cooperationStatus, + onCloseCooperation, + userRole +}) => { const { t } = useTranslation() + const [materialsAccess, setMaterialsAccess] = + useState( + CooperationMaterialsAccessEnum.OneMonthAccess + ) return ( @@ -25,6 +47,9 @@ const CooperationCompletion = () => { title={t('cooperationsPage.cooperationDetails.closeCooperationTitle')} > { {t('cooperationsPage.cooperationDetails.closeCooperationBtn')} + {userRole === UserRoleEnum.Tutor && ( + + + + )} ) } diff --git a/src/containers/my-cooperations/cooperation-details/CooperationDetails.tsx b/src/containers/my-cooperations/cooperation-details/CooperationDetails.tsx index 796d242f9..f6b7c4d42 100644 --- a/src/containers/my-cooperations/cooperation-details/CooperationDetails.tsx +++ b/src/containers/my-cooperations/cooperation-details/CooperationDetails.tsx @@ -37,13 +37,16 @@ import { PositionEnum, Cooperation, SizeEnum, - ButtonVariantEnum + ButtonVariantEnum, + StatusEnum } from '~/types' import { cooperationsSelector, setCooperationSections, + setCooperationStatus, setIsActivityCreated } from '~/redux/features/cooperationsSlice' +import AcceptCooperationClosing from '~/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing' const CooperationDetails = () => { const dispatch = useAppDispatch() @@ -54,6 +57,9 @@ const CooperationDetails = () => { const { isActivityCreated } = useAppSelector(cooperationsSelector) const [isNotesOpen, setIsNotesOpen] = useState(false) const [editMode, setEditMode] = useState(false) + const [isClosed, setIsClosed] = useState(false) + const { userRole } = useAppSelector((state) => state.appMain) + const cooperationStatus = useAppSelector((state) => state.cooperations.status) const [searchParams, setSearchParams] = useSearchParams() @@ -88,14 +94,28 @@ const CooperationDetails = () => { useEffect(() => { dispatch(setCooperationSections(response.sections)) + dispatch(setCooperationStatus(response.status)) setEditMode(Boolean(response?.sections?.length)) - }, [response.sections, dispatch]) + }, [response.sections, response.status, dispatch]) const handleEditMode = useCallback(() => { setEditMode((prev) => !prev) dispatch(setIsActivityCreated(true)) }, [dispatch]) + const handleCooperationStatusUpdate = useCallback(async () => { + await cooperationService.updateCooperation({ + _id: id, + status: StatusEnum.Closed + }) + setIsClosed(true) + dispatch(setCooperationStatus(StatusEnum.Closed)) + }, [id, dispatch]) + + const handleCooperationCloseAccept = useCallback(() => { + void handleCooperationStatusUpdate() + }, [handleCooperationStatusUpdate]) + if (loading) { return } @@ -132,6 +152,22 @@ const CooperationDetails = () => { return cooperationContent } + const closeCooperationInitiator = + response.needAction === response.receiverRole + ? response.initiator + : response.receiver + + const acceptClosingProcess = !isClosed && ( + + ) + + const isCooperationClosingRequestSend = + response.needAction === userRole && + response.status === StatusEnum.RequestToClose + const iconConditionals = isNotesOpen ? ( ) : ( @@ -141,7 +177,7 @@ const CooperationDetails = () => { return ( - + { + {activeTab === CooperationTabsEnum.Activities && + isCooperationClosingRequestSend && + acceptClosingProcess} {pageContent()} {!isDesktop && isNotesOpen && ( diff --git a/src/containers/my-cooperations/cooperations-container/CooperationContainer.tsx b/src/containers/my-cooperations/cooperations-container/CooperationContainer.tsx index dc3a38efa..270c5a589 100644 --- a/src/containers/my-cooperations/cooperations-container/CooperationContainer.tsx +++ b/src/containers/my-cooperations/cooperations-container/CooperationContainer.tsx @@ -50,8 +50,9 @@ const CooperationContainer: FC = ({ /> ) }) - : item.status === StatusEnum.Active && - navigate(`./${item._id}?tab=activities`) + : (item.status === StatusEnum.Active || + item.status === StatusEnum.RequestToClose) && + navigate(`./${item._id}`) } const cooperationGrid = ( diff --git a/src/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.tsx b/src/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.tsx index 78cc37afc..c1b5b72fd 100644 --- a/src/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.tsx +++ b/src/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.tsx @@ -15,6 +15,7 @@ import SubjectLevelChips from '~/components/subject-level-chips/SubjectLevelChip import AppButton from '~/components/app-button/AppButton' import ShowMoreCollapse from '~/components/show-more-collapse/ShowMoreCollapse' import Loader from '~/components/loader/Loader' +import useConfirm from '~/hooks/use-confirm' import { ButtonVariantEnum, @@ -22,6 +23,8 @@ import { Offer, ServiceFunction, SizeEnum, + StatusEnum, + UpdateCooperationStatusParams, UserRoleEnum } from '~/types' import { style } from '~/containers/my-cooperations/my-cooperations-details/MyCooperationsDetails.styles' @@ -31,7 +34,9 @@ import { useChatContext } from '~/context/chat-context' import CooperationCompletion from '../cooperation-completion/CooperationCompletion' import { getCategoryIcon } from '~/services/category-icon-service' import { getValidatedHexColor } from '~/utils/get-validated-hex-color' -import { useAppSelector } from '~/hooks/use-redux' +import { useAppDispatch, useAppSelector } from '~/hooks/use-redux' +import { AxiosResponse } from 'axios' +import { setCooperationStatus } from '~/redux/features/cooperationsSlice' const MyCooperationsDetails = () => { const { t } = useTranslation() @@ -40,6 +45,9 @@ const MyCooperationsDetails = () => { const { setChatInfo } = useChatContext() const userId = useAppSelector((state) => state.appMain.userId) const userRole = useAppSelector((state) => state.appMain.userRole) + const cooperationStatus = useAppSelector((state) => state.cooperations.status) + const { checkConfirmation } = useConfirm() + const dispatch = useAppDispatch() const getDetails: ServiceFunction< MyCooperationDetails | null, @@ -55,6 +63,25 @@ const MyCooperationsDetails = () => { defaultResponse: null }) + const handleCooperationStatusChange = ( + params: UpdateCooperationStatusParams + ): Promise => + cooperationService.updateCooperation({ + _id: id, + ...params + }) + + const onResponse = () => { + void fetchStatusData() + } + + const { fetchData: fetchStatusData } = useAxios({ + service: handleCooperationStatusChange, + fetchOnMount: false, + defaultResponse: null, + onResponse + }) + const updateInfo = useCallback(() => { void fetchData }, [fetchData]) @@ -105,9 +132,21 @@ const MyCooperationsDetails = () => { displayedUser.photo && createUrlPath(import.meta.env.VITE_APP_IMG_USER_URL, displayedUser.photo) - const cooperationCompletion = userRole === UserRoleEnum.Tutor && ( - - ) + const handleCooperationStatusUpdate = async () => { + const confirmed = await checkConfirmation({ + title: t('titles.confirmCooperationClosing'), + message: t('cooperationsPage.closeCooperationModal.message'), + check: true + }) + if (confirmed) { + await fetchStatusData({ status: StatusEnum.RequestToClose }) + dispatch(setCooperationStatus(StatusEnum.RequestToClose)) + } + } + + const onCooperationStatusUpdate = () => { + void handleCooperationStatusUpdate() + } return ( @@ -194,7 +233,11 @@ const MyCooperationsDetails = () => { {`${price} UAH/hour`} - {cooperationCompletion} + ) } diff --git a/src/redux/features/cooperationsSlice.ts b/src/redux/features/cooperationsSlice.ts index ec926e9d8..80eb34bd6 100644 --- a/src/redux/features/cooperationsSlice.ts +++ b/src/redux/features/cooperationsSlice.ts @@ -8,19 +8,22 @@ import { CourseSection, ResourceAvailabilityStatusEnum, ResourceAvailability, - ResourcesAvailabilityEnum + ResourcesAvailabilityEnum, + StatusEnum } from '~/types' interface CooperationsState { isActivityCreated: boolean sections: CourseSection[] resourcesAvailability: ResourcesAvailabilityEnum + status: StatusEnum } const initialState: CooperationsState = { isActivityCreated: false, sections: [], - resourcesAvailability: ResourcesAvailabilityEnum.OpenAll + resourcesAvailability: ResourcesAvailabilityEnum.OpenAll, + status: StatusEnum.Active } export const initialCooperationSectionData: CourseSection = { @@ -235,6 +238,9 @@ const cooperationsSlice = createSlice({ } } } + }, + setCooperationStatus(state, action: PayloadAction) { + state.status = action.payload } } }) @@ -252,7 +258,8 @@ export const { updateResource, deleteResource, setResourcesAvailability, - updateResourceAvailability + updateResourceAvailability, + setCooperationStatus } = actions export const cooperationsSelector = (state: RootState) => state.cooperations diff --git a/src/styles/app-theme/app.pallete.ts b/src/styles/app-theme/app.pallete.ts index af978b808..9b3db056e 100644 --- a/src/styles/app-theme/app.pallete.ts +++ b/src/styles/app-theme/app.pallete.ts @@ -7,11 +7,16 @@ const palette = { basic: { black: '#000000', gray: '#B0BEC5', + softGray: '#F1F2F3', blue: '#0B8AF8', white: '#FFFFFF', grey: '#ECEFF1', yellow: '#FFB000', carmenRed: '#B91F1B', + lightRed: '#F5D7D7', + pinkishRed: '#EBAFAF', + mediumRed: '#CD3636', + deepRed: '#A42B2B', burntOrange: '#F56F36', yellowBrown: '#F5D636', yellowGreen: '#9BC541', diff --git a/src/types/common/enums/common.enums.ts b/src/types/common/enums/common.enums.ts index 856de4ed8..23775860f 100644 --- a/src/types/common/enums/common.enums.ts +++ b/src/types/common/enums/common.enums.ts @@ -98,7 +98,8 @@ export enum StatusEnum { Active = 'active', Closed = 'closed', Draft = 'draft', - NeedAction = 'need action' + NeedAction = 'need action', + RequestToClose = 'request to close' } export enum PositionEnum { Left = 'left', diff --git a/src/types/cooperation/interfaces/cooperation.interface.ts b/src/types/cooperation/interfaces/cooperation.interface.ts index 1f9f0b3b8..a1aebf637 100644 --- a/src/types/cooperation/interfaces/cooperation.interface.ts +++ b/src/types/cooperation/interfaces/cooperation.interface.ts @@ -16,11 +16,15 @@ export interface Cooperation extends CommonEntityFields { user: Pick & { role: UserRoleEnum } + initiator: Pick + initiatorRole: 'tutor' | 'student' title: Offer['title'] price: Offer['price'] proficiencyLevel: ProficiencyLevelEnum status: StatusEnum needAction: UserRoleEnum + receiver: Pick + receiverRole: 'tutor' | 'student' sections: CourseSection[] } @@ -51,6 +55,7 @@ export interface MyCooperationDetails { proficiencyLevel: ProficiencyLevelEnum initiator: UserResponse initiatorRole: UserRoleEnum + status: StatusEnum } export interface CreateCooperationsParams extends EnrollOfferForm { diff --git a/src/types/cooperation/types/cooperation.types.ts b/src/types/cooperation/types/cooperation.types.ts index 68eb9cc00..85c35658a 100644 --- a/src/types/cooperation/types/cooperation.types.ts +++ b/src/types/cooperation/types/cooperation.types.ts @@ -7,3 +7,7 @@ export type UpdateCooperationsParams = Partial< export type UpdateCooperationsSections = Partial< Pick > + +export type UpdateCooperationStatusParams = Partial< + Pick +> diff --git a/tests/unit/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.spec.jsx b/tests/unit/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.spec.jsx new file mode 100644 index 000000000..0bd84ff2f --- /dev/null +++ b/tests/unit/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing.spec.jsx @@ -0,0 +1,15 @@ +import { render, screen } from '@testing-library/react' +import { beforeEach, vi } from 'vitest' +import AcceptCooperationClosing from '~/containers/my-cooperations/accept-cooperation-close/AcceptCooperationClosing' +describe('AcceptCooperationClosing', () => { + const mockOnAccept = vi.fn() + + beforeEach(() => { + render() + }) + + it('should render the title correctly', () => { + const titleText = screen.getByText('titles.acceptCooperationClosing') + expect(titleText).toBeInTheDocument() + }) +}) diff --git a/tests/unit/containers/my-cooperations/cooperation-completion/CooperationCompletion.spec.jsx b/tests/unit/containers/my-cooperations/cooperation-completion/CooperationCompletion.spec.jsx new file mode 100644 index 000000000..361462c36 --- /dev/null +++ b/tests/unit/containers/my-cooperations/cooperation-completion/CooperationCompletion.spec.jsx @@ -0,0 +1,46 @@ +import { screen, render, fireEvent, waitFor } from '@testing-library/react' +import { expect, vi } from 'vitest' +import CooperationCompletion from '~/containers/my-cooperations/cooperation-completion/CooperationCompletion' +import { UserRoleEnum } from '~/types' + +describe('CooperationCompletion Component', () => { + const mockOnCloseCooperation = vi.fn() + + it('should render the access selection dropdown when userRole is Tutor', () => { + render( + + ) + + const accessTitle = screen.getByText( + 'cooperationsPage.cooperationDetails.accessTitle' + ) + expect(accessTitle).toBeInTheDocument() + + const appSelect = screen.getByRole('combobox') + expect(appSelect).toBeInTheDocument() + + const closeButton = screen.getByTestId('close-cooperation-btn') + expect(closeButton).toBeInTheDocument() + }) + + it('should call mockCloseCooperation when closing cooperation', async () => { + render( + + ) + + const closeButton = screen.getByTestId('close-cooperation-btn') + + fireEvent.click(closeButton) + + await waitFor(() => { + expect(mockOnCloseCooperation).toHaveBeenCalled() + }) + }) +}) diff --git a/tests/unit/containers/my-cooperations/cooperation-container/CooperationContainer.spec.jsx b/tests/unit/containers/my-cooperations/cooperation-container/CooperationContainer.spec.jsx index 9b82547bd..cf2f34d43 100644 --- a/tests/unit/containers/my-cooperations/cooperation-container/CooperationContainer.spec.jsx +++ b/tests/unit/containers/my-cooperations/cooperation-container/CooperationContainer.spec.jsx @@ -1,7 +1,10 @@ -import { screen } from '@testing-library/react' +import { screen, waitFor } from '@testing-library/react' import { renderWithProviders } from '~tests/test-utils' import CooperationContainer from '~/containers/my-cooperations/cooperations-container/CooperationContainer' import { mockedCoop } from '~tests/unit/containers/my-cooperations/MyCooperations.spec.constants' +import { vi } from 'vitest' +import userEvent from '@testing-library/user-event' +import { StatusEnum } from '~/types' const filterOptionsMock = { filters: { @@ -17,6 +20,27 @@ const preloadedState = { socket: { usersOnline: [] } } +const navigateMock = vi.fn() + +vi.mock('react-router-dom', async () => ({ + ...(await vi.importActual('react-router-dom')), + useNavigate: () => navigateMock +})) + +const mockCloseModal = vi.fn() +const mockOpenModal = vi.fn() + +vi.mock('~/context/modal-context', async () => { + const actual = await vi.importActual('~/context/modal-context') + return { + ...actual, + useModalContext: () => ({ + closeModal: mockCloseModal, + openModal: mockOpenModal + }) + } +}) + describe('CooperationContainer component ', () => { it('should render card in container', () => { renderWithProviders( @@ -31,4 +55,59 @@ describe('CooperationContainer component ', () => { expect(level).toBeInTheDocument() }) + + it('navigates to cooperation detail for Active status', async () => { + const activeCoop = { ...mockedCoop, status: StatusEnum.Active } + renderWithProviders( + , + { preloadedState } + ) + + const card = screen.getByText(activeCoop.offer.subject.name) + userEvent.click(card) + + await waitFor(() => { + expect(navigateMock).toHaveBeenCalledWith(`./${activeCoop._id}`) + }) + }) + + it('navigates to cooperation detail for Active status', async () => { + const activeCoop = { ...mockedCoop, status: StatusEnum.RequestToClose } + renderWithProviders( + , + { preloadedState } + ) + + const card = screen.getByText(activeCoop.offer.subject.name) + userEvent.click(card) + + await waitFor(() => { + expect(navigateMock).toHaveBeenCalledWith(`./${activeCoop._id}`) + }) + }) + + it('opens modal for Pending status', async () => { + const pendingCoop = { ...mockedCoop, status: StatusEnum.Pending } + + renderWithProviders( + , + { preloadedState } + ) + + const card = screen.getByText(pendingCoop.offer.subject.name) + userEvent.click(card) + + await waitFor(() => { + expect(mockOpenModal).toHaveBeenCalled() + }) + }) })