diff --git a/frontend/src/contexts/AuthProvider.tsx b/frontend/src/contexts/AuthProvider.tsx index 8e5d77509..b77e54679 100644 --- a/frontend/src/contexts/AuthProvider.tsx +++ b/frontend/src/contexts/AuthProvider.tsx @@ -11,7 +11,7 @@ import { env } from '../env'; import FamUser from '../types/FamUser'; import LoginProviders from '../types/LoginProviders'; import AuthContext, { AuthContextData } from './AuthContext'; -import { SPAR_REDIRECT_PATH, TSC_ADMIN_ROLE } from '../shared-constants/shared-constants'; +import { MINISTRY_OF_FOREST_ID, SPAR_REDIRECT_PATH, TSC_ADMIN_ROLE } from '../shared-constants/shared-constants'; import { TWO_MINUTE } from '../config/TimeUnits'; import ROUTES from '../routes/constants'; @@ -42,7 +42,6 @@ const findFindAndLastName = (displayName: string, provider: string): Array { const separator = '_'; - const minitryOfForestId = '00012797'; const cognitoGroups: string[] = accessToken['cognito:groups']; if (!cognitoGroups) { @@ -51,19 +50,19 @@ const parseRole = (accessToken: { [id: string]: any }): UserClientRolesType[] => const parsedClientRoles: UserClientRolesType[] = []; - cognitoGroups.forEach((cognaitoRole) => { - if (!cognaitoRole.includes(separator)) { - throw new Error(`Invalid role format with string: ${cognaitoRole}`); + cognitoGroups.forEach((cognitoRole) => { + if (!cognitoRole.includes(separator)) { + throw new Error(`Invalid role format with string: ${cognitoRole}`); } - const lastUnderscoreIndex = cognaitoRole.lastIndexOf(separator); - let role = cognaitoRole.substring(0, lastUnderscoreIndex); - let clientId = cognaitoRole.substring(lastUnderscoreIndex + 1); + const lastUnderscoreIndex = cognitoRole.lastIndexOf(separator); + let role = cognitoRole.substring(0, lastUnderscoreIndex); + let clientId = cognitoRole.substring(lastUnderscoreIndex + 1); // If the last substring after an underscore is not a number then it's a concrete role, // we need to manually assign it a MoF client id for now. if (Number.isNaN(Number(clientId))) { - clientId = minitryOfForestId; - role = cognaitoRole; + clientId = MINISTRY_OF_FOREST_ID; + role = cognitoRole; } // Check if a client id already exist in parsed client role diff --git a/frontend/src/shared-constants/shared-constants.ts b/frontend/src/shared-constants/shared-constants.ts index 1b384d1f6..d5e1a7c97 100644 --- a/frontend/src/shared-constants/shared-constants.ts +++ b/frontend/src/shared-constants/shared-constants.ts @@ -21,3 +21,5 @@ export const LARGE_SCREEN_WIDTH = 1056; export const TSC_ADMIN_ROLE = 'SPAR_TSC_ADMIN'; export const PLACE_HOLDER = '--'; + +export const MINISTRY_OF_FOREST_ID = '00012797'; diff --git a/frontend/src/utils/BreadcrumbUtils.ts b/frontend/src/utils/BreadcrumbUtils.ts new file mode 100644 index 000000000..62e129010 --- /dev/null +++ b/frontend/src/utils/BreadcrumbUtils.ts @@ -0,0 +1,22 @@ +import ROUTES from '../routes/constants'; +import { addParamToPath } from './PathUtils'; +import { MINISTRY_OF_FOREST_ID } from '../shared-constants/shared-constants'; + +export const getSeedlotBreadcrumbs = ( + seedlotNumber: string, + seedlotApplicant: string, + isTscAdmin: boolean +) => { + const crumbsList = []; + crumbsList.push({ name: 'Seedlots', path: ROUTES.SEEDLOTS }); + if (isTscAdmin && seedlotApplicant !== MINISTRY_OF_FOREST_ID) { + crumbsList.push({ name: 'Review Seedlots', path: ROUTES.TSC_SEEDLOTS_TABLE }); + } else { + crumbsList.push({ name: 'My seedlots', path: ROUTES.MY_SEEDLOTS }); + } + crumbsList.push({ + name: `Seedlot ${seedlotNumber}`, + path: `${addParamToPath(ROUTES.SEEDLOT_DETAILS, seedlotNumber)}` + }); + return crumbsList; +}; diff --git a/frontend/src/views/Seedlot/ContextContainerClassA/constants.tsx b/frontend/src/views/Seedlot/ContextContainerClassA/constants.tsx index 0aa7a788d..7eb64a11c 100644 --- a/frontend/src/views/Seedlot/ContextContainerClassA/constants.tsx +++ b/frontend/src/views/Seedlot/ContextContainerClassA/constants.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import { EmptyMultiOptObj } from '../../../shared-constants/shared-constants'; +import { EmptyMultiOptObj, MINISTRY_OF_FOREST_ID } from '../../../shared-constants/shared-constants'; import MultiOptionsObj from '../../../types/MultiOptionsObject'; import { CollectionFormSubmitType, ExtractionFormSubmitType, InterimFormSubmitType, OrchardFormSubmitType, @@ -87,8 +87,8 @@ export const stepMap: StepMap = { }; export const tscAgencyObj: MultiOptionsObj = { - code: '00012797', - label: '00012797 - Tree Seed Centre - MOF', + code: MINISTRY_OF_FOREST_ID, + label: `${MINISTRY_OF_FOREST_ID} - Tree Seed Centre - MOF`, description: 'Tree Seed Centre' }; diff --git a/frontend/src/views/Seedlot/EditAClassApplication/Form.tsx b/frontend/src/views/Seedlot/EditAClassApplication/Form.tsx index a7bb6eb0d..af3db033c 100644 --- a/frontend/src/views/Seedlot/EditAClassApplication/Form.tsx +++ b/frontend/src/views/Seedlot/EditAClassApplication/Form.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useContext, useEffect } from 'react'; import { ActionableNotification, FlexGrid, @@ -13,26 +13,26 @@ import { AxiosError } from 'axios'; import { toast } from 'react-toastify'; import { useNavigate, useParams } from 'react-router-dom'; +import AuthContext from '../../../contexts/AuthContext'; import { getSeedlotById, patchSeedlotApplicationInfo } from '../../../api-service/seedlotAPI'; -import { THREE_HALF_HOURS, THREE_HOURS } from '../../../config/TimeUnits'; import getVegCodes from '../../../api-service/vegetationCodeAPI'; -import LotApplicantAndInfoForm from '../../../components/LotApplicantAndInfoForm'; +import ROUTES from '../../../routes/constants'; import { SeedlotType } from '../../../types/SeedlotType'; import { SeedlotPatchPayloadType, SeedlotRegFormType } from '../../../types/SeedlotRegistrationTypes'; import MultiOptionsObj from '../../../types/MultiOptionsObject'; -import PageTitle from '../../../components/PageTitle'; +import { THREE_HALF_HOURS, THREE_HOURS } from '../../../config/TimeUnits'; import focusById from '../../../utils/FocusUtils'; -import ROUTES from '../../../routes/constants'; -import ErrorToast from '../../../components/Toast/ErrorToast'; -import Breadcrumbs from '../../../components/Breadcrumbs'; -import { ErrToastOption } from '../../../config/ToastifyConfig'; import { getForestClientStringInput } from '../../../utils/ForestClientUtils'; import { getBooleanInputObj, getOptionsInputObj, getStringInputObj } from '../../../utils/FormInputUtils'; import { getSpeciesOptionByCode } from '../../../utils/SeedlotUtils'; import { addParamToPath } from '../../../utils/PathUtils'; import { getMultiOptList } from '../../../utils/MultiOptionsUtils'; - -import { getBreadcrumbs } from './utils'; +import { getSeedlotBreadcrumbs } from '../../../utils/BreadcrumbUtils'; +import LotApplicantAndInfoForm from '../../../components/LotApplicantAndInfoForm'; +import PageTitle from '../../../components/PageTitle'; +import ErrorToast from '../../../components/Toast/ErrorToast'; +import Breadcrumbs from '../../../components/Breadcrumbs'; +import { ErrToastOption } from '../../../config/ToastifyConfig'; import './styles.scss'; @@ -47,6 +47,8 @@ const EditAClassApplicationForm = ({ isReview, applicantData, setApplicantData } const navigate = useNavigate(); const { seedlotNumber } = useParams(); + const { isTscAdmin } = useContext(AuthContext); + const vegCodeQuery = useQuery({ queryKey: ['vegetation-codes'], queryFn: getVegCodes, @@ -160,7 +162,11 @@ const EditAClassApplicationForm = ({ isReview, applicantData, setApplicantData } : ( <> - + [ - { - name: 'Seedlots', - path: ROUTES.SEEDLOTS - }, - { - name: 'My seedlots', - path: ROUTES.MY_SEEDLOTS - }, - { - name: `Seedlot ${seedlotNumber}`, - path: `${addParamToPath(ROUTES.SEEDLOT_DETAILS, seedlotNumber)}` - } -]; diff --git a/frontend/src/views/Seedlot/SeedlotDetails/ApplicantInformation/index.tsx b/frontend/src/views/Seedlot/SeedlotDetails/ApplicantInformation/index.tsx index 6e0af9250..1f566f6e1 100644 --- a/frontend/src/views/Seedlot/SeedlotDetails/ApplicantInformation/index.tsx +++ b/frontend/src/views/Seedlot/SeedlotDetails/ApplicantInformation/index.tsx @@ -18,11 +18,15 @@ interface ApplicantSeedlotInformationProps { seedlotNumber?: string; applicant?: SeedlotApplicantType; isFetching: boolean; + hideEditButton: boolean; } -const ApplicantInformation = ( - { seedlotNumber, applicant, isFetching }: ApplicantSeedlotInformationProps -) => { +const ApplicantInformation = ({ + seedlotNumber, + applicant, + isFetching, + hideEditButton +}: ApplicantSeedlotInformationProps) => { const navigate = useNavigate(); return ( @@ -146,19 +150,25 @@ const ApplicantInformation = ( } - - - - - + { + hideEditButton + ? null + : ( + + + + + + ) + } ); }; diff --git a/frontend/src/views/Seedlot/SeedlotDetails/FormProgress/index.tsx b/frontend/src/views/Seedlot/SeedlotDetails/FormProgress/index.tsx index f54e0421e..fdfcc5158 100644 --- a/frontend/src/views/Seedlot/SeedlotDetails/FormProgress/index.tsx +++ b/frontend/src/views/Seedlot/SeedlotDetails/FormProgress/index.tsx @@ -10,6 +10,7 @@ import { Edit } from '@carbon/icons-react'; import { getAClassSeedlotProgressStatus } from '../../../../api-service/seedlotAPI'; import { ProgressIndicatorConfig } from '../../ContextContainerClassA/definitions'; +import DetailSection from '../../../../components/DetailSection'; import SeedlotRegistrationProgress from '../../../../components/SeedlotRegistrationProgress'; import NetworkError from '../../../../components/NetworkError'; import { completeProgressConfig, initialProgressConfig } from '../../ContextContainerClassA/constants'; @@ -19,7 +20,6 @@ import { QueryStatusType } from '../../../../types/QueryStatusType'; import { SeedlotStatusCode } from '../../../../types/SeedlotType'; import './styles.scss'; -import DetailSection from '../../../../components/DetailSection'; interface FormProgressProps { seedlotNumber?: string; @@ -115,7 +115,14 @@ const FormProgress = ( onClick={() => navigate(addParamToPath(ROUTES.SEEDLOT_A_CLASS_REGISTRATION, seedlotNumber ?? ''))} disabled={getSeedlotQueryStatus === 'loading'} > - {seedlotStatusCode === 'SUB' ? 'View your seedlot' : 'Edit seedlot form'} + { + seedlotStatusCode === 'SUB' + || seedlotStatusCode === 'EXP' + || seedlotStatusCode === 'COM' + || seedlotStatusCode === 'APP' + ? 'View your seedlot' + : 'Edit seedlot form' + } diff --git a/frontend/src/views/Seedlot/SeedlotDetails/index.tsx b/frontend/src/views/Seedlot/SeedlotDetails/index.tsx index 7fc9640df..6a34e8fd3 100644 --- a/frontend/src/views/Seedlot/SeedlotDetails/index.tsx +++ b/frontend/src/views/Seedlot/SeedlotDetails/index.tsx @@ -27,7 +27,7 @@ import { convertToApplicantInfoObj, covertRawToDisplayObj } from '../../../utils import { getForestClientByNumberOrAcronym } from '../../../api-service/forestClientsAPI'; import ROUTES from '../../../routes/constants'; import { addParamToPath } from '../../../utils/PathUtils'; -import { MEDIUM_SCREEN_WIDTH } from '../../../shared-constants/shared-constants'; +import { MEDIUM_SCREEN_WIDTH, MINISTRY_OF_FOREST_ID } from '../../../shared-constants/shared-constants'; import Breadcrumbs from '../../../components/Breadcrumbs'; import { getMultiOptList } from '../../../utils/MultiOptionsUtils'; import { StatusOnSaveType } from '../../../api-service/tscAdminAPI'; @@ -53,11 +53,16 @@ const SeedlotDetails = () => { const statusOnSave = searchParams.get('statusOnSave') as StatusOnSaveType | null; + const viewOnlySeedlot: boolean = seedlotData?.seedlotStatus === 'Submitted' + || seedlotData?.seedlotStatus === 'Expired' + || seedlotData?.seedlotStatus === 'Complete' + || seedlotData?.seedlotStatus === 'Approved'; + const manageOptions = [ { text: 'Edit seedlot applicant', onClickFunction: () => navigate(addParamToPath(ROUTES.SEEDLOT_A_CLASS_EDIT, seedlotNumber ?? '')), - disabled: false + disabled: viewOnlySeedlot }, { text: 'Print seedlot', @@ -108,7 +113,7 @@ const SeedlotDetails = () => { if (isTscAdmin && seedlotData?.seedlotStatus === 'Submitted') { return 'Review seedlot'; } - if (seedlotData?.seedlotStatus === 'Submitted') { + if (viewOnlySeedlot) { return 'View your seedlot'; } return 'Edit seedlot form'; @@ -141,6 +146,17 @@ const SeedlotDetails = () => { } }; + const createBreadcrumbItems = () => { + const crumbsList = []; + crumbsList.push({ name: 'Seedlots', path: ROUTES.SEEDLOTS }); + if (isTscAdmin && seedlotData?.applicantAgency !== MINISTRY_OF_FOREST_ID) { + crumbsList.push({ name: 'Review Seedlots', path: ROUTES.TSC_SEEDLOTS_TABLE }); + } else { + crumbsList.push({ name: 'My seedlots', path: ROUTES.MY_SEEDLOTS }); + } + return crumbsList; + }; + useEffect(() => { if (forestClientQuery.isFetched && seedlotQuery.isFetchedAfterMount) { covertToClientObj(); @@ -150,11 +166,7 @@ const SeedlotDetails = () => { return ( - + @@ -244,6 +256,7 @@ const SeedlotDetails = () => { seedlotNumber={seedlotNumber} applicant={applicantData} isFetching={forestClientQuery?.isFetching} + hideEditButton={!isTscAdmin && viewOnlySeedlot} /> { ( diff --git a/frontend/src/views/Seedlot/SeedlotRegFormClassA/RegPage.tsx b/frontend/src/views/Seedlot/SeedlotRegFormClassA/RegPage.tsx index 87134766e..416a5a294 100644 --- a/frontend/src/views/Seedlot/SeedlotRegFormClassA/RegPage.tsx +++ b/frontend/src/views/Seedlot/SeedlotRegFormClassA/RegPage.tsx @@ -15,14 +15,16 @@ import { ArrowRight } from '@carbon/icons-react'; import { useNavigate } from 'react-router-dom'; import { AxiosError } from 'axios'; import Breadcrumbs from '../../../components/Breadcrumbs'; -import { getBreadcrumbs, getSeedlotSubmitErrDescription } from '../ContextContainerClassA/utils'; +import { getSeedlotSubmitErrDescription } from '../ContextContainerClassA/utils'; import PageTitle from '../../../components/PageTitle'; import SaveTooltipLabel from './SaveTooltip'; import SeedlotRegistrationProgress from '../../../components/SeedlotRegistrationProgress'; import RegForm from './RegForm'; import SubmitModal from '../../../components/SeedlotRegistrationSteps/SubmitModal'; +import AuthContext from '../../../contexts/AuthContext'; import ClassAContext from '../ContextContainerClassA/context'; import { addParamToPath } from '../../../utils/PathUtils'; +import { getSeedlotBreadcrumbs } from '../../../utils/BreadcrumbUtils'; import ROUTES from '../../../routes/constants'; import { completeProgressConfig, smartSaveText } from '../ContextContainerClassA/constants'; @@ -55,12 +57,22 @@ const RegPage = () => { const reloadFormDraft = () => getFormDraftQuery.refetch(); + const { isTscAdmin } = useContext(AuthContext); + return (
- + diff --git a/frontend/src/views/Seedlot/SeedlotReview/SeedlotReviewContent.tsx b/frontend/src/views/Seedlot/SeedlotReview/SeedlotReviewContent.tsx index c1c6f4db2..798de00f3 100644 --- a/frontend/src/views/Seedlot/SeedlotReview/SeedlotReviewContent.tsx +++ b/frontend/src/views/Seedlot/SeedlotReview/SeedlotReviewContent.tsx @@ -19,6 +19,7 @@ import getVegCodes from '../../../api-service/vegetationCodeAPI'; import Breadcrumbs from '../../../components/Breadcrumbs'; import PageTitle from '../../../components/PageTitle'; import RowGap from '../../../components/RowGap'; +import ErrorToast from '../../../components/Toast/ErrorToast'; import ApplicantAndSeedlotRead from '../../../components/SeedlotReviewSteps/ApplicantAndSeedlot/Read'; import ApplicantAndSeedlotEdit from '../../../components/SeedlotReviewSteps/ApplicantAndSeedlot/Edit'; import { SeedlotPatchPayloadType, SeedlotRegFormType } from '../../../types/SeedlotRegistrationTypes'; @@ -46,9 +47,10 @@ import { SeedPlanZoneDto, SeedlotReviewElevationLatLongDto, SeedlotReviewGeoInformationDto, TscSeedlotEditPayloadType } from '../../../types/SeedlotType'; -import ErrorToast from '../../../components/Toast/ErrorToast'; import { ErrToastOption } from '../../../config/ToastifyConfig'; +import AuthContext from '../../../contexts/AuthContext'; import { GeneticTrait } from '../../../types/PtCalcTypes'; +import { getSeedlotBreadcrumbs } from '../../../utils/BreadcrumbUtils'; import ClassAContext from '../ContextContainerClassA/context'; import { validateRegForm } from '../CreateAClass/utils'; @@ -63,7 +65,7 @@ import { } from '../ContextContainerClassA/utils'; import { - getBreadcrumbs, validateAreaOfUse, validateCollectGeoVals, + validateAreaOfUse, validateCollectGeoVals, validateGeneticWorth } from './utils'; import { GenWorthValType } from './definitions'; @@ -87,6 +89,8 @@ const SeedlotReviewContent = () => { const { seedlotNumber } = useParams(); + const { isTscAdmin } = useContext(AuthContext); + const vegCodeQuery = useQuery({ queryKey: ['vegetation-codes'], queryFn: getVegCodes, @@ -517,7 +521,15 @@ const SeedlotReviewContent = () => { {isReadMode ? 'Edit seedlot' : 'Save edit'} - + [ - { - name: 'Seedlots', - path: ROUTES.SEEDLOTS - }, - { - name: 'My seedlots', - path: ROUTES.MY_SEEDLOTS - }, - { - name: `Seedlot ${seedlotNumber}`, - path: `${addParamToPath(ROUTES.SEEDLOT_DETAILS, seedlotNumber)}` - } -]; - /** * Validate genetic worth values. *