diff --git a/appv2/src/UI/App.tsx b/appv2/src/UI/App.tsx index 7b9ec96cb..a944051c8 100644 --- a/appv2/src/UI/App.tsx +++ b/appv2/src/UI/App.tsx @@ -35,6 +35,7 @@ import { Browser } from "@capacitor/browser"; import { LayerPickerBasic } from './Map/LayerPickerBasic'; import { NewRecord } from './Map/Buttons/NewRecord'; import NewRecordDialog from './Map/Buttons/NewRecordDialog'; +import AccessRequestPage from './Overlay/AccessRequest/AccessRequestPage'; // URL listener so that the auth saga can redirect to the correct page const URL_LISTENER = (props) => { @@ -147,11 +148,12 @@ const OverlayContentMemo = React.memo((props: any) => { path="/Batch/list/:id" render={(props) => } /> - } /> - } /> - } /> - } /> - } /> + }/> + }/> + }/> + }/> + }/> + }/> } /> diff --git a/appv2/src/UI/Header/Header.tsx b/appv2/src/UI/Header/Header.tsx index 08c5da926..e67e152eb 100644 --- a/appv2/src/UI/Header/Header.tsx +++ b/appv2/src/UI/Header/Header.tsx @@ -202,6 +202,10 @@ const AdminPanelMemo = React.memo((props) => { }); const LoginOrOutMemo = React.memo((props) => { + const dispatch = useDispatch(); + const history = useHistory(); + const authenticated = useSelector((state: any) => state?.Auth?.authenticated); + const activated = useSelector((state: any) => state?.UserInfo?.activated); const [anchorEl, setAnchorEl] = React.useState(null); const openMenu = Boolean(anchorEl); const handleClick = (event: React.MouseEvent) => { @@ -214,11 +218,30 @@ const LoginOrOutMemo = React.memo((props) => { }; const navToUpdateRequest = () => { - return null; + history.push({ + pathname: '/AccessRequest', + state: { + updateInfo: true + } + }); + dispatch({ + type: TOGGLE_PANEL, + payload: { panelOpen: true, fullScreen: true } + }); } - const authenticated = useSelector((state: any) => state?.Auth?.authenticated); - const activated = useSelector((state: any) => state?.UserInfo?.activated); + const requestAccess = async () => { + if (!authenticated) { + dispatch({ type: AUTH_SIGNIN_REQUEST }); + } else { + history.push('/AccessRequest'); + dispatch({ + type: TOGGLE_PANEL, + payload: { panelOpen: true, fullScreen: true } + }); + } + }; + return @@ -231,16 +254,16 @@ const LoginOrOutMemo = React.memo((props) => { elevation: 3 }} transformOrigin={{ horizontal: 'right', vertical: 'top' }}> - {/* {showLoggedInTabs && ( */} + {activated && Update My Info - {/* )} */} + } {!activated && - + diff --git a/appv2/src/UI/Overlay/AccessRequest/AccessRequestPage.tsx b/appv2/src/UI/Overlay/AccessRequest/AccessRequestPage.tsx new file mode 100644 index 000000000..285cb2c2d --- /dev/null +++ b/appv2/src/UI/Overlay/AccessRequest/AccessRequestPage.tsx @@ -0,0 +1,740 @@ +import { useInvasivesApi } from 'hooks/useInvasivesApi'; +import React, { useEffect, useState } from 'react'; +import { useHistory } from 'react-router-dom'; +import { + Box, + Button, + Card, + CardActions, + CardContent, + Chip, + Container, + Divider, + FormControlLabel, + FormLabel, + Grid, + InputLabel, + Radio, + RadioGroup, + TextField, + FormControl, + MenuItem, + OutlinedInput, + Select, + Typography, + Tooltip, + Theme +} from '@mui/material'; +import { makeStyles } from '@mui/styles'; +import { SelectChangeEvent } from '@mui/material'; +import { useSelector } from 'react-redux'; +import { selectAuth } from 'state/reducers/auth'; + +const useStyles = makeStyles((theme: Theme) => ({ + root: { + width: '320px' + } +})); + +interface IAccessRequestPage { + classes?: any; + location?: any; +} + +const AccessRequestPage: React.FC = (props) => { + const history = useHistory(); + const api = useInvasivesApi(); + const classes = useStyles(); + const [transferAccess] = useState('yes'); + const [accountType, setAccountType] = useState(''); + + const authState = useSelector(selectAuth); + + const [idir, setIdir] = useState(authState.username ? authState.username : ''); + const [bceid, setBceid] = useState(authState.username ? authState.username : ''); + const [firstName, setFirstName] = React.useState( + authState.displayName.split(' ')[1] ? authState.displayName.split(' ')[1] : '' + ); + const [lastName, setLastName] = React.useState( + authState.displayName.split(' ')[0].replace(',', '') ? authState.displayName.split(',')[0].replace(',', '') : '' + ); + const [email, setEmail] = React.useState(authState.email ? authState.email : ''); + const idir_userid = authState?.idir_user_guid ? authState?.idir_user_guid : ''; + const bceid_userid = authState?.bceid_user_guid ? authState?.bceid_user_guid : ''; + const [phone, setPhone] = React.useState(''); + const [pacNumber, setPacNumber] = React.useState(null); + const [psn1, setPsn1] = React.useState(''); + const [psn2, setPsn2] = React.useState(''); + const [employer, setEmployer] = React.useState([]); + const [fundingAgencies, setFundingAgencies] = React.useState([]); + const [requestedRoles, setRequestedRoles] = React.useState([]); + const [fundingAgenciesList, setFundingAgenciesList] = React.useState([]); + const [employersList, setEmployersList] = React.useState([]); + const [submitted, setSubmitted] = React.useState(false); + const [comments, setComments] = React.useState(''); + const [roles, setRoles] = React.useState([]); + + // Validation Error Messages + const [idirErrorText, setIdirErrorText] = React.useState(''); + const [bceidErrorText, setBceidErrorText] = React.useState(''); + const [firstNameErrorText, setFirstNameErrorText] = React.useState(''); + const [lastNameErrorText, setLastNameErrorText] = React.useState(''); + const [emailErrorText, setEmailErrorText] = React.useState(''); + const [employerErrorText, setEmployerErrorText] = React.useState(''); + const [fundingAgenciesErrorText, setFundingAgenciesErrorText] = React.useState(''); + const [requestedRolesErrorText, setRequestedRolesErrorText] = React.useState(''); + + let isUpdating = false; + + const isValid = (decline: Boolean = false, valid: Boolean = true): Boolean => { + let requiredFields = [ + { value: firstName, error: setFirstNameErrorText, text: 'Please enter First name ' }, + { value: lastName, error: setLastNameErrorText, text: 'Please enter Last name ' }, + { value: email, error: setEmailErrorText, text: 'Please enter primary Email ' }, + ]; + // if not declining check more fields + if (!decline) { + requiredFields.push( + { value: employer, error: setEmployerErrorText, text: 'Please enter Employer ' }, + { value: fundingAgencies?.join(), error: setFundingAgenciesErrorText, text: 'Please enter 1 or more Funding Agencies ' }, + { value: requestedRoles?.join(), error: setRequestedRolesErrorText, text: 'Please enter 1 or more Requested Roles ' }, + ); + } + + if (accountType === 'IDIR') { + requiredFields.push({ value: idir, error: setIdirErrorText, text: 'Please enter IDIR name ' }); + } else { + requiredFields.push({ value: bceid, error: setBceidErrorText, text: 'Please enter BCeID ' }); + + } + + requiredFields.map((field) => { + if (!field.value || field.value.length === 0) { + field.error(field.text); + valid = false; + } else { + field.error(''); + } + }); + + return valid; + }; + + const submitAccessRequest = async () => { + if (isValid()) { + const accessRequest = { + idir: idir, + bceid: bceid, + firstName: firstName, + lastName: lastName, + email: email, + phone: phone, + pacNumber: pacNumber, + psn1: psn1, + psn2: psn2, + employer: employer?.toString(), + fundingAgencies: fundingAgencies?.toString(), + requestedRoles: requestedRoles?.toString(), + comments: comments, + status: 'NOT_APPROVED', + idirUserId: idir_userid, + bceidUserId: bceid_userid + }; + await api.submitAccessRequest(accessRequest); + setSubmitted(true); + } + }; + + const submitUpdateRequest = async () => { + if (isValid()) { + const updateRequest = { + idir: idir, + bceid: bceid, + firstName: firstName, + lastName: lastName, + email: email, + phone: phone, + pacNumber: pacNumber, + psn1: psn1, + psn2: psn2, + employer: employer?.toString(), + fundingAgencies: fundingAgencies?.toString(), + requestedRoles: requestedRoles?.toString(), + comments: comments, + status: 'NOT_APPROVED', + idirUserId: idir_userid, + bceidUserId: bceid_userid + }; + await api.submitUpdateRequest(updateRequest); + setSubmitted(true); + } + }; + + const declineAccess = async () => { + if (isValid(true)) { + const accessRequest = { + idir: idir, + bceid: bceid, + firstName: firstName, + lastName: lastName, + email: email, + phone: null, + pacNumber: null, + psn1: null, + psn2: null, + employer: '', + fundingAgencies: '', + requestedRoles: null, + comments: null, + status: 'REMOVED', + idir_userid: null, + bceid_userid: null + }; + await api.submitAccessRequest(accessRequest); + setSubmitted(true); + } + }; + + if (props?.location?.state?.updateInfo && props?.location?.state?.updateInfo === true) { + isUpdating = true; + } else { + isUpdating = false; + } + + const [userInfo, setUserInfo] = useState(undefined); + + useEffect(() => { + if (userInfo !== undefined) { + if (userInfo?.idir_account_name) { + setAccountType('IDIR'); + setIdir(userInfo?.idir_account_name); + } else if (userInfo?.bceid_business_name) { + setAccountType('BCeID'); + setBceid(userInfo?.bceid_business_name); + } + + if (userInfo?.bceid_business_name) { + setFirstName(userInfo?.bceid_business_name); + } else { + userInfo?.first_name && setFirstName(userInfo?.first_name); + } + userInfo?.last_name && setLastName(userInfo?.last_name); + userInfo?.primary_email && setEmail(userInfo?.primary_email); + userInfo?.work_phone_number && setPhone(userInfo?.work_phone_number); + userInfo?.pac_number && setPacNumber(userInfo?.pac_number); + userInfo?.pac_service_number_1 && setPsn1(userInfo?.pac_service_number_1); + userInfo?.pac_service_number_2 && setPsn2(userInfo?.pac_service_number_2); + userInfo?.employer && setEmployer(userInfo?.employer.split(',')); + userInfo?.funding_agencies && setFundingAgencies(userInfo?.funding_agencies.split(',')); + userInfo?.requested_roles && setRequestedRoles(userInfo?.requested_roles.split(',')); + } + }, [userInfo]); + + useEffect(() => { + const userName = authState.username; + const fetchFundingAgencies = async () => { + const response = await api.getFundingAgencies(); + setFundingAgenciesList(response); + }; + const fetchEmployers = async () => { + const response = await api.getEmployers(); + setEmployersList(response); + }; + const fetchAccessRequestData = async () => { + const response = await api.getAccessRequestData({ username: userName }); + setUserInfo(response); + }; + fetchAccessRequestData(); + fetchFundingAgencies(); + fetchEmployers(); + api.getRoles().then((response) => { + if (userInfo?.requested_roles.indexOf('administrator') == -1) + setRoles(response.filter(res => res.role_name.indexOf('administrator') == -1)); + else + setRoles(response); + }); + }, []); + + const handleAccountRadioChange = (event: React.ChangeEvent) => { + setAccountType(event.target.value); + }; + + const handleEmployerChange = (event: React.ChangeEvent) => { + setEmployer(event.target.value); + }; + + const ITEM_HEIGHT = 48; + const ITEM_PADDING_TOP = 8; + const MenuProps = { + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + width: 250, + }, + }, + }; + + const getAgencyDescription = (name: string): string => fundingAgenciesList.find(({ code_name }) => code_name === name)?.code_description; + + const getEmployerDescription = (name: string): string => employersList.find(({ code_name }) => code_name === name)?.code_description; + + const getRoleDescription = (name: string): string => roles.find(({ role_name }) => role_name === name)?.role_description; + + const handleRequestedRoleChange = (event: SelectChangeEvent) => { + const { + target: { value } + } = event; + setRequestedRoles(typeof value === 'string' ? value.split(',') : value); + }; + + const handleFundingAgenciesChange = (event: SelectChangeEvent) => { + const { + target: { value }, + } = event; + setFundingAgencies(typeof value === 'string' ? value.split(',') : value); + }; + + return ( + + + + + InvasivesBC Access Request + + + + + {!submitted && ( + + {/* {!isUpdating && ( + + + Do you wish to transfer your IAPP access to InvasivesBC when it replaces IAPP? + + + } label="Yes" /> + } label="No" /> + + + )} + {transferAccess === 'no' && ( + + You will be removed from the InvasivesBC lists moving forward. You may, of course, rejoin us at any + time. + + )} */} + {(transferAccess === 'yes' || isUpdating) && ( + + {!isUpdating && ( + + {' '} + + The following information is required to properly establish your access to the new InvasivesBC + applications. This information will not be shared with any other organization within + government or externally with other agencies. +
+
+ If you have more than one IAPP user account (i.e. two or more BCeIDs), please provide a + separate form for each account. +
+
+
+ )} + {isUpdating && ( + + + Please update any necessary fields if they have changed since you submitted your access + request. Your information will be updated upon review. + + + )} + {!isUpdating && ( + + + + + Account type + + } label="IDIR" /> + } label="BCeID" /> + + + + {accountType === 'IDIR' && ( + + {' '} + setIdir(e.target.value)} + required + variant="outlined" + error={!!idirErrorText} + id="idir" + label="IDIR Account Name" + /> + + )} + {accountType === 'BCeID' && ( + + {' '} + setBceid(e.target.value)} + style={{ width: 320 }} + variant="outlined" + error={!!bceidErrorText} + id="bceid" + label="BCeID Account Name" + /> + + )} + + + )} + + + + setFirstName(e.target.value)} + variant="outlined" + error={!!firstNameErrorText} + id="first-name" + label="First Name" + /> + + + setLastName(e.target.value)} + variant="outlined" + error={!!lastNameErrorText} + id="last-name" + label="Last Name" + /> + + + setEmail(e.target.value)} + variant="outlined" + error={!!emailErrorText} + id="primary-email" + label="Primary Email" + /> + + + + + + + setPhone(e.target.value)} + id="work-phone" + label="Work Phone (optional)" + /> + + + + + + + + <> + + Employer + + + + + + + + + + + + <> + + Funding Agencies + + + + + + + + + + + + { + const inputnumber = Number.parseInt(e.target.value); + if (inputnumber) { + setPacNumber(inputnumber); + } + }} + style={{ width: 320 }} + variant="outlined" + id="pac-number" + label="PAC Number" + /> + + + + + setPsn1(e.target.value)} + style={{ width: 320 }} + variant="outlined" + id="psn1" + label="Pesticide Service Number #1" + /> + + + + + setPsn2(e.target.value)} + style={{ width: 320 }} + variant="outlined" + id="psn2" + label="Pesticide Service Number #2" + /> + + + + + + + + <> + + Requested roles + + + + + + + + + + + setComments(e.target.value)} + name="Comments" + id="comments" + variant="outlined" + label="Comments" + /> + + + + + + {!isUpdating && ( + + We will inform you when the training materials are ready and again when your access is + approved + + )} + {isUpdating && ( + + We will inform you when your information has been updated. + + )} + + + +
+ )} +
+ )} + {submitted && ( + + + + {!isUpdating && ( + + Thank you for submitting your request. + + )} + {isUpdating && ( + + Your request to update your information has been received. We will inform you when your + information has been updated. + + )} + + + + )} + + + + + + + + + {!submitted && !isUpdating && ( + + )} + {!submitted && isUpdating && ( + + )} + + +
+
+
+
+ ); +}; + +export default AccessRequestPage;