From 8863c8bced759083344a201bd2d3281e63f34871 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Wed, 17 Apr 2024 15:18:46 -0700 Subject: [PATCH 01/83] CFD personal tags --- .../src/actions/FOI/foiActionConstants.js | 3 + .../src/actions/FOI/foiRequestActions.js | 18 ++ .../src/apiManager/endpoints/index.js | 3 + .../services/FOI/foiMasterDataServices.js | 90 ++++++ .../components/FOI/FOIRequest/FOIRequest.js | 12 +- .../MinistryReview/MinistryReview.js | 10 +- .../Attachments/AttachmentModal.js | 38 +++ .../FileUpload/FileUploadForMCFPersonal.js | 282 +++++++++++++++++- .../FOI/customComponents/Records/index.js | 4 + .../src/modules/FOI/foiRequestsReducer.js | 6 + .../d1d4f6cdfd68_cfd_personal_tags.py | 48 +++ .../models/ProgramAreaDivisions.py | 21 +- .../resources/foiflowmasterdata.py | 14 +- .../request_api/schemas/foirecord.py | 12 + .../services/divisionstageservice.py | 26 +- 15 files changed, 575 insertions(+), 12 deletions(-) create mode 100644 request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py diff --git a/forms-flow-web/src/actions/FOI/foiActionConstants.js b/forms-flow-web/src/actions/FOI/foiActionConstants.js index f9acfee86..396cade14 100644 --- a/forms-flow-web/src/actions/FOI/foiActionConstants.js +++ b/forms-flow-web/src/actions/FOI/foiActionConstants.js @@ -42,6 +42,9 @@ const FOI_ACTION_CONSTANTS = { FOI_MINISTRY_DIVISIONALSTAGES: "FOI_MINISTRY_DIVISIONALSTAGES", FOI_PERSONAL_DIVISIONS_SECTIONS: "FOI_PERSONAL_DIVISIONS_SECTIONS", FOI_PERSONAL_SECTIONS: "FOI_PERSONAL_SECTIONS", + FOI_PERSONAL_PEOPLE: "FOI_PERSONAL_PEOPLE", + FOI_PERSONAL_FILETYPES: "FOI_PERSONAL_FILETYPES", + FOI_PERSONAL_VOLUMES: "FOI_PERSONAL_VOLUMES", FOI_WATCHER_LIST: "FOI_WATCHER_LIST", diff --git a/forms-flow-web/src/actions/FOI/foiRequestActions.js b/forms-flow-web/src/actions/FOI/foiRequestActions.js index 55d3aca7b..aa6944bc1 100644 --- a/forms-flow-web/src/actions/FOI/foiRequestActions.js +++ b/forms-flow-web/src/actions/FOI/foiRequestActions.js @@ -315,6 +315,24 @@ export const setFOIPersonalSections = (data) => dispatch => { payload:data }) } +export const setFOIPersonalPeople = (data) => dispatch => { + dispatch({ + type:FOI_ACTION_CONSTANTS.FOI_PERSONAL_PEOPLE, + payload:data + }) +} +export const setFOIPersonalFiletypes = (data) => dispatch => { + dispatch({ + type:FOI_ACTION_CONSTANTS.FOI_PERSONAL_FILETYPES, + payload:data + }) +} +export const setFOIPersonalVolumes = (data) => dispatch => { + dispatch({ + type:FOI_ACTION_CONSTANTS.FOI_PERSONAL_VOLUMES, + payload:data + }) +} export const setFOIWatcherList = (data) => (dispatch) => { dispatch({ type: FOI_ACTION_CONSTANTS.FOI_WATCHER_LIST, diff --git a/forms-flow-web/src/apiManager/endpoints/index.js b/forms-flow-web/src/apiManager/endpoints/index.js index 4a03e6cc0..40eab5b9a 100644 --- a/forms-flow-web/src/apiManager/endpoints/index.js +++ b/forms-flow-web/src/apiManager/endpoints/index.js @@ -33,6 +33,9 @@ const API = { FOI_PERSONAL_DIVISIONS_SECTIONS: `${FOI_BASE_API_URL}/api/foiflow/divisions//true/divisionsandsections`, FOI_PERSONAL_SECTIONS: `${FOI_BASE_API_URL}/api/foiflow/divisions//true/sections`, FOI_PERSONAL_DIVISIONS: `${FOI_BASE_API_URL}/api/foiflow/divisions//true/divisions`, + FOI_PERSONAL_PEOPLE: `${FOI_BASE_API_URL}/api/foiflow/divisions//true/people`, + FOI_PERSONAL_FILETYPES: `${FOI_BASE_API_URL}/api/foiflow/divisions//true/filetypes`, + FOI_PERSONAL_VOLUMES: `${FOI_BASE_API_URL}/api/foiflow/divisions//true/volumes`, FOI_POST_RAW_REQUEST_WATCHERS: `${FOI_BASE_API_URL}/api/foiwatcher/rawrequest`, FOI_GET_RAW_REQUEST_WATCHERS: `${FOI_BASE_API_URL}/api/foiwatcher/rawrequest/`, FOI_POST_MINISTRY_REQUEST_WATCHERS: `${FOI_BASE_API_URL}/api/foiwatcher/ministryrequest`, diff --git a/forms-flow-web/src/apiManager/services/FOI/foiMasterDataServices.js b/forms-flow-web/src/apiManager/services/FOI/foiMasterDataServices.js index 5399f22da..2ae1629a7 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiMasterDataServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiMasterDataServices.js @@ -17,6 +17,9 @@ import { setFOIReceivedModeList, setFOIMinistryDivisionalStages, setFOIPersonalDivisionsAndSections, + setFOIPersonalPeople, + setFOIPersonalFiletypes, + setFOIPersonalVolumes, setFOIPersonalSections, setClosingReasons, setFOISubjectCodeList, @@ -418,6 +421,93 @@ import { } }; + export const fetchFOIPersonalPeople = (bcgovcode) => { + switch(bcgovcode) { + case "MCF": + const apiUrlMCF = replaceUrl(API.FOI_PERSONAL_PEOPLE, "", bcgovcode); + return (dispatch) => { + httpGETRequest(apiUrlMCF, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + const foiPersonalSections = res.data; + dispatch(setFOIPersonalPeople({})); + dispatch(setFOIPersonalPeople(foiPersonalSections)); + dispatch(setFOILoader(false)); + } else { + console.log(`Error while fetching (${bcgovcode}) FOI records people`, res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + console.log(`Error while fetching (${bcgovcode}) FOI records people`, error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + }; + default: + break; + } + }; + + export const fetchFOIPersonalFiletypes = (bcgovcode) => { + switch(bcgovcode) { + case "MCF": + const apiUrlMCF = replaceUrl(API.FOI_PERSONAL_FILETYPES, "", bcgovcode); + return (dispatch) => { + httpGETRequest(apiUrlMCF, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + const foiPersonalSections = res.data; + dispatch(setFOIPersonalFiletypes({})); + dispatch(setFOIPersonalFiletypes(foiPersonalSections)); + dispatch(setFOILoader(false)); + } else { + console.log(`Error while fetching (${bcgovcode}) FOI records people`, res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + console.log(`Error while fetching (${bcgovcode}) FOI records people`, error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + }; + default: + break; + } + }; + + export const fetchFOIPersonalVolumes = (bcgovcode) => { + switch(bcgovcode) { + case "MCF": + const apiUrlMCF = replaceUrl(API.FOI_PERSONAL_VOLUMES, "", bcgovcode); + return (dispatch) => { + httpGETRequest(apiUrlMCF, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + const foiPersonalSections = res.data; + dispatch(setFOIPersonalVolumes({})); + dispatch(setFOIPersonalVolumes(foiPersonalSections)); + dispatch(setFOILoader(false)); + } else { + console.log(`Error while fetching (${bcgovcode}) FOI records people`, res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + console.log(`Error while fetching (${bcgovcode}) FOI records people`, error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + }; + default: + break; + } + }; + export const fetchFOIPersonalDivisions = (bcgovcode) => { const apiUrl = replaceUrl(API.FOI_PERSONAL_DIVISIONS, "", bcgovcode); return (dispatch) => { diff --git a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js index 0a3556279..1159a6998 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js @@ -24,6 +24,9 @@ import { fetchFOIMinistryAssignedToList, fetchFOISubjectCodeList, fetchFOIPersonalDivisionsAndSections, + fetchFOIPersonalPeople, + fetchFOIPersonalFiletypes, + fetchFOIPersonalVolumes, fetchOIPCOutcomes, fetchOIPCStatuses, fetchOIPCReviewtypes, @@ -354,9 +357,16 @@ const FOIRequest = React.memo(({ userDetail }) => { setIsIAORestricted(isRequestRestricted(requestDetails, ministryId)); } - if(MinistryNeedsScanning.includes(bcgovcode.replaceAll('"', '')) && requestDetails.requestType == FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_PERSONAL) { + if( + MinistryNeedsScanning.includes(bcgovcode.replaceAll('"', '')) && + requestDetails.requestType == + FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_PERSONAL + ) { dispatch(fetchFOIPersonalDivisionsAndSections(bcgovcode.replaceAll('"', ''))); if(bcgovcode.replaceAll('"', '') == "MCF") { + dispatch(fetchFOIPersonalPeople(bcgovcode.replaceAll('"', ''))); + dispatch(fetchFOIPersonalFiletypes(bcgovcode.replaceAll('"', ''))); + dispatch(fetchFOIPersonalVolumes(bcgovcode.replaceAll('"', ''))); setIsMCFPersonal(true); } } diff --git a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js index 1312f3d3b..a1a4801fc 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js @@ -18,6 +18,9 @@ import { import { fetchFOIMinistryAssignedToList, fetchFOIPersonalDivisionsAndSections, + fetchFOIPersonalPeople, + fetchFOIPersonalFiletypes, + fetchFOIPersonalVolumes, } from "../../../../apiManager/services/FOI/foiMasterDataServices"; import { fetchFOIRequestAttachmentsList } from "../../../../apiManager/services/FOI/foiAttachmentServices"; @@ -269,10 +272,11 @@ const MinistryReview = React.memo(({ userDetail }) => { requestDetails.requestType == FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_PERSONAL ) { - dispatch( - fetchFOIPersonalDivisionsAndSections(bcgovcode.replaceAll('"', "")) - ); + dispatch(fetchFOIPersonalDivisionsAndSections(bcgovcode.replaceAll('"', ""))); if (bcgovcode.replaceAll('"', "") == "MCF") { + dispatch(fetchFOIPersonalPeople(bcgovcode.replaceAll('"', ''))); + dispatch(fetchFOIPersonalFiletypes(bcgovcode.replaceAll('"', ''))); + dispatch(fetchFOIPersonalVolumes(bcgovcode.replaceAll('"', ''))); setIsMCFPersonal(true); } } diff --git a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js index b250306fd..a29993383 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js +++ b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js @@ -71,6 +71,7 @@ export default function AttachmentModal({ replacementfiletypes = [], totalUploadedRecordSize = 0, requestType = FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_GENERAL, + isScanningTeamMember = false }) { let tagList = []; if (uploadFor === "attachment") { @@ -129,6 +130,10 @@ export default function AttachmentModal({ const [tagValue, setTagValue] = useState( uploadFor === "record" ? "" : "general" ); + const [person, setPerson] = useState({}); + const [volume, setVolume] = useState({}); + const [fileType, setFileType] = useState({}); + const [trackingID, setTrackingID] = useState(""); const attchmentFileNameList = attachmentsArray.map((_file) => _file.filename.toLowerCase() ); @@ -256,6 +261,22 @@ export default function AttachmentModal({ setTagValue(_tagValue); }; + const handlePersonChange = (_tagValue) => { + setPerson(_tagValue); + }; + + const handleVolumeChange = (_tagValue) => { + setVolume(_tagValue); + }; + + const handleFileTypeChange = (_tagValue) => { + setFileType(_tagValue); + }; + + const handleTrackingIDChange = (_tagValue) => { + setTrackingID(_tagValue); + }; + const handleSave = () => { if (modalFor.toLowerCase() === "delete") { handleModal(true, null, null); @@ -263,6 +284,7 @@ export default function AttachmentModal({ let fileInfoList = []; let fileStatusTransition = ""; + let personalAttributes = {}; if (modalFor === "replace" || modalFor === "replaceattachment") { fileStatusTransition = attachment?.category; } else if (uploadFor === "record") { @@ -276,6 +298,12 @@ export default function AttachmentModal({ MCFSections?.sections?.find( (division) => division.divisionid === tagValue )?.name; + personalAttributes = { + person: person.name, + filetype: fileType.name, + volume: volume.name, + trackingid: trackingID + } } else if ( bcgovcode == "MSD" && requestType == FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_PERSONAL @@ -298,6 +326,7 @@ export default function AttachmentModal({ filestatustransition: fileStatusTransition, filename: file.filename ? file.filename : file.name, filesize: file.size, + personalattributes: personalAttributes, ...(uploadFor === "record" && { divisionid: tagValue }), }; }); @@ -577,11 +606,20 @@ export default function AttachmentModal({ )} handleTagChange={handleTagChange} tagValue={tagValue} + handlePersonChange={handlePersonChange} + person={person} + handleVolumeChange={handleVolumeChange} + volume={volume} + handleFileTypeChange={handleFileTypeChange} + fileType={fileType} + handleTrackingIDChange={handleTrackingIDChange} + trackingID={trackingID} maxNumberOfFiles={maxNoFiles} isMinistryCoordinator={isMinistryCoordinator} existingDocuments={existingDocuments} totalUploadedRecordSize={totalUploadedRecordSize} totalRecordUploadLimit={totalRecordUploadLimit} + isScanningTeamMember={isScanningTeamMember} /> ) : bcgovcode == "MSD" && MSDSections?.divisions?.length > 0 ? ( { const fileInputField = useRef(null); const [files, setFiles] = useState({ ...existingDocuments }); @@ -52,6 +64,32 @@ const FileUploadForMCFPersonal = ({ const [additionalTagList, setAdditionalTagList] = useState([]); const [showAdditionalTags, setShowAdditionalTags] = useState(false); + const MCFPeople = useSelector( + (state) => state.foiRequests.foiPersonalPeople + ); + const MCFFiletypes = useSelector( + (state) => state.foiRequests.foiPersonalFiletypes + ); + const MCFVolumes = useSelector( + (state) => state.foiRequests.foiPersonalVolumes + ); + + const [allPeople, setAllPeople] = useState( + isMinistryCoordinator?MCFPeople?.people.filter((p)=>{return p.name !== 'PERSON 1'}):MCFPeople?.people.filter((p)=>{return p.name !== 'APPLICANT'}) + ); + const [allVolumes, setAllVolumes] = useState(MCFVolumes?.volumes); + const [fileTypes, setFileTypes] = useState(MCFFiletypes?.filetypes.slice(0, 6)); + const [otherFileTypes, setOtherFileTypes] = useState(MCFFiletypes?.filetypes.slice(6, MCFFiletypes?.filetypes.length)); + const [people, setPeople] = useState(allPeople.slice(0, 5)); + const [volumes, setVolumes] = useState(allVolumes.slice(0, 5)); + const [showAllPeople, setShowAllPeople] = useState(false); + const [showAllVolumes, setShowAllVolumes] = useState(false); + const [fileTypeSearchValue, setFileTypeSearchValue] = useState(""); + const [additionalFileTypes, setAdditionalFileTypes] = useState([]); + const [showAdditionalFileTypes, setShowAdditionalFileTypes] = useState(false); + + const [disableInput, setDisableInput] = useState(false); + const handleUploadBtnClick = (e) => { e.stopPropagation(); fileInputField.current.click(); @@ -221,11 +259,251 @@ const FileUploadForMCFPersonal = ({ setAdditionalTagList(searchSections(otherTagList, searchValue, tagValue)); },[searchValue, otherTagList, tagValue]) + const handleTrackingIDUpdate = (e) => { + handleTrackingIDChange(e.target.value); + } + + const searchFileTypes = (_fileTypeArray, _keyword, _selectedFileType) => { + let newFileTypeArray = []; + if(_keyword || _selectedFileType) { + _fileTypeArray.map((section) => { + if(_keyword && section.name.toLowerCase().includes(_keyword.toLowerCase())) { + newFileTypeArray.push(section); + } else if(section.divisionid === _selectedFileType.divisionid) { + newFileTypeArray.unshift(section); + } + }); + } + + if(newFileTypeArray.length > 0) { + setShowAdditionalFileTypes(true); + } else { + setShowAdditionalFileTypes(false); + } + + return newFileTypeArray; + } + + React.useEffect(() => { + if(showAllPeople) { + setPeople(allPeople) + } else { + setPeople(allPeople.slice(0, 5)) + } + if(showAllVolumes) { + setVolumes(allVolumes) + } else { + setVolumes(allVolumes.slice(0, 5)) + } + },[showAllPeople, showAllVolumes]) + + React.useEffect(() => { + setAdditionalFileTypes(searchFileTypes(otherFileTypes, fileTypeSearchValue, fileType)); + },[fileTypeSearchValue, otherFileTypes, fileType]) + return ( <> {(modalFor === "add" && (uploadFor === "attachment" || uploadFor === 'record')) && (
+ +
+ Select the records type, input the tracking id # and select the name of the section of records you are uploading. Once you have selected the section name you will be able to select the respective documents from your computer to add to the records log. +
+ +
+ Select person file is associated with (IAO will have the ability to edit this when redacting records): +
+ {isScanningTeamMember && (<> +
+ {people.map(p => + {handlePersonChange(p)}} + clicked={person?.name === p.name} + /> + )} + {!showAllPeople && ({setShowAllPeople(true)}} + />)} +
+ )} + +
+ Select volume (if required): +
+ {isScanningTeamMember && (<> +
+ {volumes.map(v => + {handleVolumeChange(v)}} + clicked={volume?.name === v.name} + /> + )} + {!showAllVolumes && ({setShowAllVolumes(true)}} + />)} +
+ )} + +
+ Select type of file: +
+ {isScanningTeamMember && (<> +
+ {fileTypes.map(f => + {handleFileTypeChange(f)}} + clicked={fileType?.name === f.name} + /> + )} +
+
+ + + + + {setFileTypeSearchValue(e.target.value.trim())}} + sx={{ + color: "#38598A", + }} + startAdornment={ + + + Search any additional filetypes here + + + + } + fullWidth + /> + + + {showAdditionalFileTypes === true && ( + {additionalFileTypes.map(f => + {handleFileTypeChange(f)}} + clicked={fileType?.name === f.name} + /> + )} + )} + +
+ )} + + {isScanningTeamMember && (<> +
+ +
+ )} +
- Select the name of the section of records you are uploading. Once you have selected the section name you will be able to select the respective documents from your computer. + { + isScanningTeamMember? + "Select Section Name:" + :"Select the name of the section of records you are uploading. Once you have selected the section name you will be able to select the respective documents from your computer." + }
{divisions.length > 0 && isMinistryCoordinator && (<>
diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index f2cde2048..d875d3c55 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -612,6 +612,7 @@ export const RecordsLog = ({ (fileInfo) => fileInfo.filename === header.filename ); var documentDetails; + let personalAttributes; if (modalFor === "replace") { documentDetails = { filename: header.filename, @@ -642,6 +643,7 @@ export const RecordsLog = ({ documentDetails.attributes.trigger = "recordreplace"; delete documentDetails.outputdocumentmasterid; } else { + personalAttributes = {divisions: [{ divisionid: _fileInfo.divisionid }], ..._fileInfo.personalattributes} documentDetails = { s3uripath: header.filepathdb, filename: header.filename, @@ -651,6 +653,7 @@ export const RecordsLog = ({ ? _file.lastModifiedDate : new Date(_file.lastModified), filesize: _file.size, + personalattributes: personalAttributes, }, }; } @@ -2258,6 +2261,7 @@ export const RecordsLog = ({ totalUploadedRecordSize={totalUploadedRecordSize} replacementfiletypes={getreplacementfiletypes()} requestType={requestType} + isScanningTeamMember={isScanningTeamMember} />
{ return { ...state, foiPersonalDivisionsAndSections: action.payload }; case FOI_ACTION_CONSTANTS.FOI_PERSONAL_SECTIONS: return { ...state, foiPersonalSections: action.payload }; + case FOI_ACTION_CONSTANTS.FOI_PERSONAL_PEOPLE: + return { ...state, foiPersonalPeople: action.payload }; + case FOI_ACTION_CONSTANTS.FOI_PERSONAL_FILETYPES: + return { ...state, foiPersonalFiletypes: action.payload }; + case FOI_ACTION_CONSTANTS.FOI_PERSONAL_VOLUMES: + return { ...state, foiPersonalVolumes: action.payload }; case FOI_ACTION_CONSTANTS.FOI_WATCHER_LIST: return { ...state, foiWatcherList: action.payload }; case FOI_ACTION_CONSTANTS.CLOSING_REASONS: diff --git a/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py b/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py new file mode 100644 index 000000000..185ef612c --- /dev/null +++ b/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py @@ -0,0 +1,48 @@ +"""CFD Personal Tags + +Revision ID: d1d4f6cdfd68 +Revises: 6646acda32fe +Create Date: 2024-04-15 22:36:29.501289 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'd1d4f6cdfd68' +down_revision = '6646acda32fe' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column('ProgramAreaDivisions', sa.Column('type', sa.String(length=25), nullable=True)) + + people = ["APPLICANT", "PERSON 1", "PERSON 2", "PERSON 3", "PERSON 4", "PERSON 5", "PERSON 6", "PERSON 7", "PERSON 8", "PERSON 9", "PERSON 10", "PERSON 11", "PERSON 12", "PERSON 13", "PERSON 14", "PERSON 15", "PERSON 16", "PERSON 17", "PERSON 18", "PERSON 19", "PERSON 20"] + sortorder = 0 + + for person in people: + op.execute('INSERT INTO public."ProgramAreaDivisions"(programareaid, name, isactive, created_at, createdby, sortorder, issection, specifictopersonalrequests, type)\ + VALUES ((SELECT programareaid FROM public."ProgramAreas" WHERE iaocode =\'CFD\'), \''+person+'\', TRUE, NOW(), \'system\', '+ str(sortorder) +', FALSE, TRUE, \'person\' );') + sortorder = sortorder+1 + + filetypes = ["CS", "FS", "ICM MEMO", "INCIDENT REQUEST", "SERVICE REQUEST", "FS CFP", "FT 1", "FY 2", "FY 3", "FY 4", "FY 5", "FY 6", "FY 7", "FY 8", "FY 9", "FY 10", "FY 11", "FY 12", "FY 13", "FY 14", "FY 15", "FY 16", "FY 17", "FY 18", "FY 19", "FY 20", "FT 21", "FY 22", "FY 23", "FY 24", "FY 25", "FY 26", "FY 27", "FY 28", "FY 29", "FY 30", "FT 31", "FY 32", "FY 33", "FY 34", "FY 35", "FY 36", "FY 37", "FY 38", "FY 39", "FY 40", "FT 41", "FY 42", "FY 43", "FY 44", "FY 45", "FY 46", "FY 47", "FY 48", "FY 49", "FY 50"] + sortorder = 1 + + for ftype in filetypes: + op.execute('INSERT INTO public."ProgramAreaDivisions"(programareaid, name, isactive, created_at, createdby, sortorder, issection, specifictopersonalrequests, type)\ + VALUES ((SELECT programareaid FROM public."ProgramAreas" WHERE iaocode =\'CFD\'), \''+ftype+'\', TRUE, NOW(), \'system\', '+ str(sortorder) +', FALSE, TRUE, \'filetype\' );') + sortorder = sortorder+1 + + volumes = ["VOL 1", "VOL 2", "VOL 3", "VOL 4", "VOL 5", "VOL 6", "VOL 7", "VOL 8", "VOL 9", "VOL 10", "VOL 11", "VOL 12", "VOL 13", "VOL 14", "VOL 15", "VOL 16", "VOL 17", "VOL 18", "VOL 19", "VOL 20"] + sortorder = 1 + + for vol in volumes: + op.execute('INSERT INTO public."ProgramAreaDivisions"(programareaid, name, isactive, created_at, createdby, sortorder, issection, specifictopersonalrequests, type)\ + VALUES ((SELECT programareaid FROM public."ProgramAreas" WHERE iaocode =\'CFD\'), \''+vol+'\', TRUE, NOW(), \'system\', '+ str(sortorder) +', FALSE, TRUE, \'volume\' );') + sortorder = sortorder+1 + +def downgrade(): + op.execute('DELETE FROM public."ProgramAreaDivisions" WHERE type in (\'person\', \'filetype\', \'volume\')') + op.drop_column('ProgramAreaDivisions', 'type') diff --git a/request-management-api/request_api/models/ProgramAreaDivisions.py b/request-management-api/request_api/models/ProgramAreaDivisions.py index 16c535135..a79afc5eb 100644 --- a/request-management-api/request_api/models/ProgramAreaDivisions.py +++ b/request-management-api/request_api/models/ProgramAreaDivisions.py @@ -20,6 +20,7 @@ class ProgramAreaDivision(db.Model): createdby = db.Column(db.String(120), unique=False, default='system') updated_at = db.Column(db.DateTime, nullable=True) updatedby = db.Column(db.String(120), unique=False, nullable=True) + type = db.Column(db.String(25), unique=False, nullable=True) @classmethod def getallprogramareadivisons(cls): @@ -63,6 +64,24 @@ def getpersonalrequestsdivisionsandsections(cls,programareaid): query = db.session.query(ProgramAreaDivision).filter_by(programareaid=programareaid, isactive=True,specifictopersonalrequests=True).order_by(ProgramAreaDivision.name.asc()) return division_schema.dump(query) + @classmethod + def getpersonalrequestsprogramareapeople(cls,programareaid): + division_schema = ProgramAreaDivisionSchema(many=True) + query = db.session.query(ProgramAreaDivision).filter_by(programareaid=programareaid,isactive=True,type='person',specifictopersonalrequests=True).order_by(ProgramAreaDivision.sortorder.asc(), ProgramAreaDivision.name.asc()) + return division_schema.dump(query) + + @classmethod + def getpersonalrequestsprogramareavolumes(cls,programareaid): + division_schema = ProgramAreaDivisionSchema(many=True) + query = db.session.query(ProgramAreaDivision).filter_by(programareaid=programareaid,isactive=True,type='volume',specifictopersonalrequests=True).order_by(ProgramAreaDivision.sortorder.asc(), ProgramAreaDivision.name.asc()) + return division_schema.dump(query) + + @classmethod + def getpersonalrequestsprogramareafiletypes(cls,programareaid): + division_schema = ProgramAreaDivisionSchema(many=True) + query = db.session.query(ProgramAreaDivision).filter_by(programareaid=programareaid,isactive=True,type='filetype',specifictopersonalrequests=True).order_by(ProgramAreaDivision.sortorder.asc(), ProgramAreaDivision.name.asc()) + return division_schema.dump(query) + @classmethod def createprogramareadivision(cls, programareadivision)->DefaultMethodResult: created_at = datetime2.now().isoformat() @@ -122,4 +141,4 @@ def getchilddivisions(cls, divisonid): class ProgramAreaDivisionSchema(ma.Schema): class Meta: - fields = ('divisionid','programareaid', 'name','isactive','sortorder','issection','parentid', 'specifictopersonalrequests') \ No newline at end of file + fields = ('divisionid','programareaid','name','isactive','sortorder','issection','parentid','specifictopersonalrequests','type') \ No newline at end of file diff --git a/request-management-api/request_api/resources/foiflowmasterdata.py b/request-management-api/request_api/resources/foiflowmasterdata.py index c8fe5cbde..d21456dc3 100644 --- a/request-management-api/request_api/resources/foiflowmasterdata.py +++ b/request-management-api/request_api/resources/foiflowmasterdata.py @@ -184,12 +184,18 @@ def get(bcgovcode,specifictopersonalrequests=None, fetchmode = None): data = None if(specifictopersonalrequests is not None and specifictopersonalrequests.lower() == 'true'): match fetchmode: - case 'divisions': + case 'divisions': data = divisionstageservice().getpersonalspecificdivisionandstages(bcgovcode) - case 'sections': + case 'sections': data = divisionstageservice().getpersonalspecificprogramareasections(bcgovcode) - case 'divisionsandsections': - data = divisionstageservice().getpersonalspecificdivisionsandsections(bcgovcode) + case 'divisionsandsections': + data = divisionstageservice().getpersonalspecificdivisionsandsections(bcgovcode) + case 'people': + data = divisionstageservice().getpersonalspecificpeople(bcgovcode) + case 'filetypes': + data = divisionstageservice().getpersonalspecificfiletypes(bcgovcode) + case 'volumes': + data = divisionstageservice().getpersonalspecificvolumes(bcgovcode) case _: data = divisionstageservice().getpersonalspecificdivisionandstages(bcgovcode) else: diff --git a/request-management-api/request_api/schemas/foirecord.py b/request-management-api/request_api/schemas/foirecord.py index 56a739a2f..b18e17964 100644 --- a/request-management-api/request_api/schemas/foirecord.py +++ b/request-management-api/request_api/schemas/foirecord.py @@ -16,6 +16,17 @@ class Meta: # pylint: disable=too-few-public-methods unknown = EXCLUDE divisionid = fields.Int(data_key="divisionid",allow_none=False) +class PersonalAttributesSchema(Schema): + class Meta: # pylint: disable=too-few-public-methods + """Exclude unknown fields in the deserialized output.""" + + unknown = EXCLUDE + person = fields.Str(data_key="person",allow_none=True) + filetype = fields.Str(data_key="filetype",allow_none=True) + volume = fields.Str(data_key="volume",allow_none=True) + trackingid = fields.Str(data_key="trackingid",allow_none=True) + divisions = fields.Nested(DivisionSchema, many=True, validate=validate.Length(min=1), required=True,allow_none=False) + class CreateRecordAttributeSchema(Schema): class Meta: # pylint: disable=too-few-public-methods """Exclude unknown fields in the deserialized output.""" @@ -26,6 +37,7 @@ class Meta: # pylint: disable=too-few-public-methods lastmodified = fields.Str(data_key="lastmodified",allow_none=False, validate=[validate.Length(max=120, error=MAX_EXCEPTION_MESSAGE)]) filesize = fields.Int(data_key="filesize", allow_none=False) parentpdfmasterid = fields.Int(required=False,allow_none=True) + personalattributes = fields.Nested(PersonalAttributesSchema,required=False,allow_none=True) class FOIRequestCreateRecordSchema(Schema): class Meta: # pylint: disable=too-few-public-methods diff --git a/request-management-api/request_api/services/divisionstageservice.py b/request-management-api/request_api/services/divisionstageservice.py index c62a8637c..c42012aa6 100644 --- a/request-management-api/request_api/services/divisionstageservice.py +++ b/request-management-api/request_api/services/divisionstageservice.py @@ -55,7 +55,31 @@ def getpersonalspecificdivisionsandsections(self, bcgovcode): _sections.append(_section) programareasections.append({"divisionid": divisionid, "name": self.escapestr(_division['name']),"sortorder":_division['sortorder'],"issection":_division['issection'],"sections":_sections}) return {"divisions": programareasections} - + + def getpersonalspecificpeople(self, bcgovcode): + people = [] + programarea = ProgramArea.getprogramarea(bcgovcode) + _sections = ProgramAreaDivision.getpersonalrequestsprogramareapeople(programarea['programareaid']) + for _section in _sections: + people.append({"divisionid": _section['divisionid'], "name": self.escapestr(_section['name']), "sortorder":_section['sortorder'], "issection":_section['issection']}) + return {"people": people} + + def getpersonalspecificfiletypes(self, bcgovcode): + filetypes = [] + programarea = ProgramArea.getprogramarea(bcgovcode) + _sections = ProgramAreaDivision.getpersonalrequestsprogramareafiletypes(programarea['programareaid']) + for _section in _sections: + filetypes.append({"divisionid": _section['divisionid'], "name": self.escapestr(_section['name']), "sortorder":_section['sortorder'], "issection":_section['issection']}) + return {"filetypes": filetypes} + + def getpersonalspecificvolumes(self, bcgovcode): + volumes = [] + programarea = ProgramArea.getprogramarea(bcgovcode) + _sections = ProgramAreaDivision.getpersonalrequestsprogramareavolumes(programarea['programareaid']) + for _section in _sections: + volumes.append({"divisionid": _section['divisionid'], "name": self.escapestr(_section['name']), "sortorder":_section['sortorder'], "issection":_section['issection']}) + return {"volumes": volumes} + def getstages(self): activestages = [] division_stages = ProgramAreaDivisionStage.getprogramareadivisionstages() From 79f993959ca739ccd5534975bfef7f07691f1a74 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Wed, 17 Apr 2024 23:57:58 -0700 Subject: [PATCH 02/83] list tags in record list --- .../FOI/customComponents/Records/index.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index d875d3c55..a87b4850e 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -299,6 +299,7 @@ export const RecordsLog = ({ dispatch(checkForRecordsChange(requestId, ministryId)); //To manage enabling and disabling of download for harms package recordsDownloadList[1].disabled = enableHarmsDonwnload(); + console.log("recordsObj: ", recordsObj); }, [recordsObj]); useEffect(() => { @@ -342,6 +343,7 @@ export const RecordsLog = ({ ) ).values(), ]; + console.log("divisionFilters", divisionFilters); if (divisionFilters?.length > 0) divisionFilters?.push( { divisionid: -1, divisionname: "All" }, @@ -2544,6 +2546,8 @@ const Attachment = React.memo( const classes = useStyles(); const [disabled, setDisabled] = useState(false); const [isRetry, setRetry] = useState(false); + const [personalAtts, setPersonalAtts] = useState([]); + // useEffect(() => { // if(record && record.filename) { // setDisabled(isMinistryCoordinator && record.category == 'personal') @@ -2560,6 +2564,18 @@ const Attachment = React.memo( handleSelectRecord(record, e); }; + useEffect(() => { + let _personalAtts = []; + if(record.attributes?.personalattributes) { + Object.entries(record.attributes?.personalattributes).forEach(([key, value]) => { + if(key !== "divisions" && value) { + _personalAtts.push({divisionname: value}); + } + }); + } + setPersonalAtts(_personalAtts); + }, [record]) + const recordtitle = () => { if (disabled) { return ( @@ -2736,7 +2752,7 @@ const Attachment = React.memo( alignItems="flex-start" > - {record.attributes?.divisions?.map((division, i) => ( + {[...record.attributes?.divisions, ...personalAtts].map((division, i) => ( Date: Thu, 18 Apr 2024 13:56:09 -0700 Subject: [PATCH 03/83] Add TBD --- .../f946afec2515_add_cfd_personal_tag_tbd.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 request-management-api/migrations/versions/f946afec2515_add_cfd_personal_tag_tbd.py diff --git a/request-management-api/migrations/versions/f946afec2515_add_cfd_personal_tag_tbd.py b/request-management-api/migrations/versions/f946afec2515_add_cfd_personal_tag_tbd.py new file mode 100644 index 000000000..28540026d --- /dev/null +++ b/request-management-api/migrations/versions/f946afec2515_add_cfd_personal_tag_tbd.py @@ -0,0 +1,34 @@ +"""add cfd personal tag TBD + +Revision ID: f946afec2515 +Revises: d1d4f6cdfd68 +Create Date: 2024-04-18 10:50:18.297693 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f946afec2515' +down_revision = 'd1d4f6cdfd68' +branch_labels = None +depends_on = None + + +def upgrade(): + op.execute('INSERT INTO public."ProgramAreaDivisions" \ + (programareaid, name, isactive, created_at, createdby, sortorder, issection, specifictopersonalrequests, type) \ + VALUES ( \ + (SELECT programareaid FROM public."ProgramAreas" WHERE iaocode =\'CFD\'), \ + \'TBD\', TRUE, NOW(), \'system\', \ + ( \ + select max(sortorder) from public."ProgramAreaDivisions" \ + where isactive=TRUE and issection=TRUE and specifictopersonalrequests=TRUE \ + and programareaid=(SELECT programareaid FROM public."ProgramAreas" WHERE iaocode=\'CFD\') \ + )+1, \ + TRUE, TRUE, \'section\' );') + + +def downgrade(): + op.execute('DELETE FROM public."ProgramAreaDivisions" WHERE programareaid=(SELECT programareaid FROM public."ProgramAreas" WHERE iaocode =\'CFD\') and name=\'TBD\' and type=\'section\'') From e737849596415abea23ae2a62aa5782e268e072c Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Thu, 18 Apr 2024 17:42:48 -0700 Subject: [PATCH 04/83] tag filter for records list --- .../FOI/customComponents/Records/index.js | 65 +++++++++++++++++-- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index a87b4850e..a3826e3bf 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -299,13 +299,53 @@ export const RecordsLog = ({ dispatch(checkForRecordsChange(requestId, ministryId)); //To manage enabling and disabling of download for harms package recordsDownloadList[1].disabled = enableHarmsDonwnload(); - console.log("recordsObj: ", recordsObj); }, [recordsObj]); useEffect(() => { dispatch(getRecordFormats()); }, []); + + const MCFPeople = useSelector( + (state) => state.foiRequests.foiPersonalPeople + ); + const MCFFiletypes = useSelector( + (state) => state.foiRequests.foiPersonalFiletypes + ); + const MCFVolumes = useSelector( + (state) => state.foiRequests.foiPersonalVolumes + ); + const [personFilters, setPersonFilters] = useState([]); + const [fileTypeFilters, setFileTypeFilters] = useState([]); + const [volumeFilters, setVolumeFilters] = useState([]); + useEffect(() => { + let _personFilters = []; + let _fileTypeFilters = []; + let _volumeFilters = []; + if(recordsObj?.records?.length > 0) { + recordsObj.records.forEach((record) => { + if(record.attributes?.personalattributes?.person && MCFPeople?.people) { + if(_personFilters.filter((p)=>{return p.name === record.attributes.personalattributes.person}).length === 0) { + _personFilters = _personFilters.concat(MCFPeople.people.filter((p)=>{return p.name === record.attributes.personalattributes.person})); + } + } + if(record.attributes?.personalattributes?.filetype && MCFFiletypes?.filetypes) { + if(_fileTypeFilters.filter((ft)=>{return ft.name === record.attributes.personalattributes.filetype}).length === 0) { + _fileTypeFilters = _fileTypeFilters.concat(MCFFiletypes.filetypes.filter((ft)=>{return ft.name === record.attributes.personalattributes.filetype})); + } + } + if(record.attributes?.personalattributes?.volume && MCFVolumes?.volumes) { + if(_volumeFilters.filter((v)=>{return v.name === record.attributes.personalattributes.volume}).length === 0) { + _volumeFilters = _volumeFilters.concat(MCFVolumes.volumes.filter((v)=>{return v.name === record.attributes.personalattributes.volume})); + } + } + }); + } + setPersonFilters(_personFilters.sort((a, b)=>a.sortorder-b.sortorder)); + setFileTypeFilters(_fileTypeFilters.sort((a, b)=>a.sortorder-b.sortorder)); + setVolumeFilters(_volumeFilters.sort((a, b)=>a.sortorder-b.sortorder)); + }, [recordsObj, MCFPeople, MCFFiletypes, MCFVolumes]); + const conversionFormats = useSelector( (state) => state.foiRequests.conversionFormats ); @@ -328,6 +368,9 @@ export const RecordsLog = ({ }, [recordsTabSelect, conversionFormats]); const divisionFilters = [ + ...personFilters.map((p)=>{p.divisionname=p.name; p.type='person'; return p;}), + ...fileTypeFilters.map((ft)=>{ft.divisionname=ft.name; ft.type='filetype'; return ft;}), + ...volumeFilters.map((v)=>{v.divisionname=v.name; v.type='volume'; return v;}), ...new Map( recordsObj?.records?.reduce( (acc, file) => [ @@ -343,7 +386,7 @@ export const RecordsLog = ({ ) ).values(), ]; - console.log("divisionFilters", divisionFilters); + if (divisionFilters?.length > 0) divisionFilters?.push( { divisionid: -1, divisionname: "All" }, @@ -364,6 +407,7 @@ export const RecordsLog = ({ const [updateAttachment, setUpdateAttachment] = useState({}); const [searchValue, setSearchValue] = useState(""); const [filterValue, setFilterValue] = useState(-1); + const [filterText, setFilterText] = useState(""); const [fullnameList, setFullnameList] = useState(getFullnameList); const [recordsDownloadList, setRecordsDownloadList] = useState(RecordsDownloadList); @@ -1434,15 +1478,17 @@ export const RecordsLog = ({ searchAttachments( _.cloneDeep(recordsObj.records), filterValue, - searchValue + searchValue, + filterText ) ); } else { setFilterValue(-1); + setFilterText(""); } }, [filterValue, searchValue, recordsObj]); - const searchAttachments = (_recordsArray, _filterValue, _keywordValue) => { + const searchAttachments = (_recordsArray, _filterValue, _keywordValue, _filterText) => { var filterFunction = (r) => { var isMatch = ( (r.filename.toLowerCase().includes(_keywordValue?.toLowerCase()) || @@ -1458,6 +1504,12 @@ export const RecordsLog = ({ ? r.attributes?.divisions?.findIndex( (a) => a.divisionid === _filterValue ) > -1 + || + r.attributes?.personalattributes?.person === _filterText + || + r.attributes?.personalattributes?.filetype === _filterText + || + r.attributes?.personalattributes?.volume === _filterText : true) ) if (isMatch) { @@ -2053,6 +2105,11 @@ export const RecordsLog = ({ ? -1 : division.divisionid ); + setFilterText( + division.divisionname === filterText + ? "" + : division.divisionname + ); }} clicked={filterValue == division.divisionid} /> From 935a093527ca2e774606d20f9fa69556333d35cb Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Thu, 18 Apr 2024 23:59:44 -0700 Subject: [PATCH 05/83] personal tagging for ministry --- .../FileUpload/FileUploadForMCFPersonal.js | 46 ++++++++----------- ...1_update_type_for_cfd_personal_sections.py | 23 ++++++++++ .../models/ProgramAreaDivisions.py | 4 +- 3 files changed, 45 insertions(+), 28 deletions(-) create mode 100644 request-management-api/migrations/versions/bd20ff56d1b1_update_type_for_cfd_personal_sections.py diff --git a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js index 39fabdc74..706a101b3 100644 --- a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js +++ b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js @@ -309,10 +309,29 @@ const FileUploadForMCFPersonal = ({ Select the records type, input the tracking id # and select the name of the section of records you are uploading. Once you have selected the section name you will be able to select the respective documents from your computer to add to the records log.
+
+ Select Divisional Tracking: * +
+ {divisions.length > 0 && isMinistryCoordinator && (<> +
+ {divisions.map(tag => + {handleTagChange(tag.name)}} + clicked={tagValue == tag.name} + /> + )} +
+ )} +
Select person file is associated with (IAO will have the ability to edit this when redacting records):
- {isScanningTeamMember && (<>
{people.map(p => {setShowAllPeople(true)}} />)}
- )}
Select volume (if required):
- {isScanningTeamMember && (<>
{volumes.map(v => {setShowAllVolumes(true)}} />)}
- )}
Select type of file:
- {isScanningTeamMember && (<>
{fileTypes.map(f => )}
- )} - {isScanningTeamMember && (<>
- )}
{ @@ -505,23 +517,6 @@ const FileUploadForMCFPersonal = ({ :"Select the name of the section of records you are uploading. Once you have selected the section name you will be able to select the respective documents from your computer." }
- {divisions.length > 0 && isMinistryCoordinator && (<> -
- {divisions.map(tag => - {handleTagChange(tag.name)}} - clicked={tagValue == tag.name} - /> - )} -
- )} - {!isMinistryCoordinator && (<>
{tagList.map(tag => )}
- )}
)} {modalFor === "add" && (

Please drag and drop or add records associated with the section name you have selected above. All records upload will show under the selected section in the redaction application.

diff --git a/request-management-api/migrations/versions/bd20ff56d1b1_update_type_for_cfd_personal_sections.py b/request-management-api/migrations/versions/bd20ff56d1b1_update_type_for_cfd_personal_sections.py new file mode 100644 index 000000000..5a5c9bbdf --- /dev/null +++ b/request-management-api/migrations/versions/bd20ff56d1b1_update_type_for_cfd_personal_sections.py @@ -0,0 +1,23 @@ +"""update type for cfd personal sections + +Revision ID: bd20ff56d1b1 +Revises: f946afec2515 +Create Date: 2024-04-18 22:09:45.416382 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'bd20ff56d1b1' +down_revision = 'f946afec2515' +branch_labels = None +depends_on = None + + +def upgrade(): + op.execute('Update public."ProgramAreaDivisions" set type=\'section\' where issection=TRUE;') + +def downgrade(): + op.execute('Update public."ProgramAreaDivisions" set type=NULL where issection=TRUE;') diff --git a/request-management-api/request_api/models/ProgramAreaDivisions.py b/request-management-api/request_api/models/ProgramAreaDivisions.py index a79afc5eb..120144740 100644 --- a/request-management-api/request_api/models/ProgramAreaDivisions.py +++ b/request-management-api/request_api/models/ProgramAreaDivisions.py @@ -25,7 +25,7 @@ class ProgramAreaDivision(db.Model): @classmethod def getallprogramareadivisons(cls): division_schema = ProgramAreaDivisionSchema(many=True) - query = db.session.query(ProgramAreaDivision).filter_by(isactive=True,issection=False).all() + query = db.session.query(ProgramAreaDivision).filter_by(isactive=True,issection=False,type=None).all() return division_schema.dump(query) @classmethod @@ -49,7 +49,7 @@ def getallprogramareatags(cls,programareaid): @classmethod def getpersonalspecificprogramareadivisions(cls,programareaid): division_schema = ProgramAreaDivisionSchema(many=True) - query = db.session.query(ProgramAreaDivision).filter_by(programareaid=programareaid, isactive=True,issection=False,specifictopersonalrequests=True).order_by(ProgramAreaDivision.name.asc()) + query = db.session.query(ProgramAreaDivision).filter(ProgramAreaDivision.programareaid==programareaid,ProgramAreaDivision.isactive==True,ProgramAreaDivision.issection==False,ProgramAreaDivision.specifictopersonalrequests==True,ProgramAreaDivision.type.is_(None)).order_by(ProgramAreaDivision.name.asc()) return division_schema.dump(query) @classmethod From ba9bc16233963622cb25de470060d5bd5146febc Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Tue, 23 Apr 2024 13:20:00 -0700 Subject: [PATCH 06/83] 1. Update text 2. Fix ministry division and section conflict --- .../Attachments/AttachmentModal.js | 10 +++- .../FileUpload/FileUpload.scss | 13 ++++ .../FileUpload/FileUploadForMCFPersonal.js | 59 ++++++++++--------- .../request_api/schemas/foirecord.py | 1 + 4 files changed, 55 insertions(+), 28 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js index a29993383..f2fce8b77 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js +++ b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js @@ -134,6 +134,7 @@ export default function AttachmentModal({ const [volume, setVolume] = useState({}); const [fileType, setFileType] = useState({}); const [trackingID, setTrackingID] = useState(""); + const [personalTag, setPersonalTag] = useState({}); const attchmentFileNameList = attachmentsArray.map((_file) => _file.filename.toLowerCase() ); @@ -277,6 +278,10 @@ export default function AttachmentModal({ setTrackingID(_tagValue); }; + const handlePersonalTagChange = (_tagValue) => { + setPersonalTag(_tagValue); + } + const handleSave = () => { if (modalFor.toLowerCase() === "delete") { handleModal(true, null, null); @@ -302,7 +307,8 @@ export default function AttachmentModal({ person: person.name, filetype: fileType.name, volume: volume.name, - trackingid: trackingID + trackingid: trackingID, + personaltag: personalTag.name?personalTag.name:"TBD" } } else if ( bcgovcode == "MSD" && @@ -606,6 +612,8 @@ export default function AttachmentModal({ )} handleTagChange={handleTagChange} tagValue={tagValue} + handlePersonalTagChange={handlePersonalTagChange} + personalTag={personalTag} handlePersonChange={handlePersonChange} person={person} handleVolumeChange={handleVolumeChange} diff --git a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUpload.scss b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUpload.scss index 3e29a8a80..90f83320e 100644 --- a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUpload.scss +++ b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUpload.scss @@ -135,11 +135,24 @@ list-style-type: none; } +.pluscircle { + margin: 0px 8px 8px 0px; + position: relative; + display: inline-flex; + align-items: center; + height: 24px; +} + +.taglist-cfdpersonal { + margin: 10px 9% 15px 9%; +} + .taglist { margin: 15px 9% 25px 9%; } .tagtitle { margin-left: 9%; + margin-top: 5px; } .tag-message-container { margin: 5px 0px 0px 9%; diff --git a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js index 706a101b3..9aa41aa67 100644 --- a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js +++ b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js @@ -38,6 +38,8 @@ const FileUploadForMCFPersonal = ({ modalFor, handleTagChange, tagValue, + handlePersonalTagChange, + personalTag, handlePersonChange, person, handleVolumeChange, @@ -306,14 +308,19 @@ const FileUploadForMCFPersonal = ({ {(modalFor === "add" && (uploadFor === "attachment" || uploadFor === 'record')) && (
- Select the records type, input the tracking id # and select the name of the section of records you are uploading. Once you have selected the section name you will be able to select the respective documents from your computer to add to the records log. + + Select the records type, input the tracking id # + and select the name of the section of records you are uploading. + Once you have selected the section name you will be able to + select the respective documents from your computer to add to the records log. +
+ {divisions.length > 0 && isMinistryCoordinator && (<>
Select Divisional Tracking: *
- {divisions.length > 0 && isMinistryCoordinator && (<> -
+
{divisions.map(tag => {handleTagChange(tag.name)}} - clicked={tagValue == tag.name} + clicked={tagValue === tag.name} /> )}
)}
- Select person file is associated with (IAO will have the ability to edit this when redacting records): + Select Person (IAO will have the ability to edit this when redacting records): *
-
+
{people.map(p => {setShowAllPeople(true)}} />)}
- Select volume (if required): + Select Volume:
-
+
{volumes.map(v => {setShowAllVolumes(true)}} />)}
- Select type of file: + Select File Type: *
-
+
{fileTypes.map(f => )}
-
+
-
+
- { - isScanningTeamMember? - "Select Section Name:" - :"Select the name of the section of records you are uploading. Once you have selected the section name you will be able to select the respective documents from your computer." - } + Select Section Name:
-
+
{tagList.map(tag => {handleTagChange(tag.divisionid)}} - clicked={tagValue == tag.divisionid} + onClick={()=>{handlePersonalTagChange(tag);if(isScanningTeamMember){handleTagChange(tag.divisionid)}}} + clicked={personalTag?.name === tag.name} /> )}
-
+
{additionalTagList.map(tag => {handleTagChange(tag.divisionid)}} - clicked={tagValue == tag.divisionid} + onClick={()=>{handlePersonalTagChange(tag);if(isScanningTeamMember){handleTagChange(tag.divisionid)}}} + clicked={personalTag?.name === tag.name} /> )} )} diff --git a/request-management-api/request_api/schemas/foirecord.py b/request-management-api/request_api/schemas/foirecord.py index b18e17964..cbd3c0d8e 100644 --- a/request-management-api/request_api/schemas/foirecord.py +++ b/request-management-api/request_api/schemas/foirecord.py @@ -25,6 +25,7 @@ class Meta: # pylint: disable=too-few-public-methods filetype = fields.Str(data_key="filetype",allow_none=True) volume = fields.Str(data_key="volume",allow_none=True) trackingid = fields.Str(data_key="trackingid",allow_none=True) + personaltag = fields.Str(data_key="personaltag",allow_none=True) divisions = fields.Nested(DivisionSchema, many=True, validate=validate.Length(min=1), required=True,allow_none=False) class CreateRecordAttributeSchema(Schema): From fa84ad45f8da5b3ae31e2fb3c6a1504edfa15023 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Tue, 23 Apr 2024 23:42:27 -0700 Subject: [PATCH 07/83] 1. clear person, vol, filetype, trackingid, personaltag 2. disable fields --- .../Attachments/AttachmentModal.js | 18 +++++++++-- .../FileUpload/FileUploadForMCFPersonal.js | 31 +++++++++++++------ 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js index f2fce8b77..633eec1a2 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js +++ b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js @@ -255,7 +255,14 @@ export default function AttachmentModal({ handleModal(false); parseFileName(attachment); } - if (uploadFor === "record") setTagValue(""); + if (uploadFor === "record") { + setTagValue(""); + setPerson({}); + setVolume({}); + setFileType({}); + setTrackingID(""); + setPersonalTag({}); + } }; const handleTagChange = (_tagValue) => { @@ -339,7 +346,14 @@ export default function AttachmentModal({ handleModal(true, fileInfoList, files); setFiles([]); - if (uploadFor === "record") setTagValue(""); + if (uploadFor === "record") { + setTagValue(""); + setPerson({}); + setVolume({}); + setFileType({}); + setTrackingID(""); + setPersonalTag({}); + } } }; const getMessage = () => { diff --git a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js index 9aa41aa67..33d6082f7 100644 --- a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js +++ b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js @@ -76,6 +76,9 @@ const FileUploadForMCFPersonal = ({ (state) => state.foiRequests.foiPersonalVolumes ); + let recordsObj = useSelector((state) => state.foiRequests.foiRequestRecords); + const [records, setRecords] = useState(recordsObj?.records); + const [allPeople, setAllPeople] = useState( isMinistryCoordinator?MCFPeople?.people.filter((p)=>{return p.name !== 'PERSON 1'}):MCFPeople?.people.filter((p)=>{return p.name !== 'APPLICANT'}) ); @@ -90,7 +93,7 @@ const FileUploadForMCFPersonal = ({ const [additionalFileTypes, setAdditionalFileTypes] = useState([]); const [showAdditionalFileTypes, setShowAdditionalFileTypes] = useState(false); - const [disableInput, setDisableInput] = useState(false); + const [isPersonSelected, setIsPersonSelected] = useState(false); const handleUploadBtnClick = (e) => { e.stopPropagation(); @@ -199,11 +202,11 @@ const FileUploadForMCFPersonal = ({ callUpdateFilesCb(updatedFilesDetails[0]); } } - + const handleNewFileUpload = (e) => { - const { files: newFiles } = e.target; - const totalFiles = Object.entries(files).length + newFiles.length; - validateFiles(newFiles, totalFiles); + const { files: newFiles } = e.target; + const totalFiles = Object.entries(files).length + newFiles.length; + validateFiles(newFiles, totalFiles); }; const fileDrop = (e) => { e.preventDefault(); @@ -257,6 +260,10 @@ const FileUploadForMCFPersonal = ({ return newSectionArray; } + React.useEffect(() => { + setIsPersonSelected(Object.keys(person).length !== 0); + },[person]) + React.useEffect(() => { setAdditionalTagList(searchSections(otherTagList, searchValue, tagValue)); },[searchValue, otherTagList, tagValue]) @@ -376,9 +383,10 @@ const FileUploadForMCFPersonal = ({ size="small" onClick={()=>{handleVolumeChange(v)}} clicked={volume?.name === v.name} + disabled={!isPersonSelected} /> )} - {!showAllVolumes && ({handleFileTypeChange(f)}} clicked={fileType?.name === f.name} + disabled={!isPersonSelected} /> )}
@@ -446,7 +455,7 @@ const FileUploadForMCFPersonal = ({ {setFileTypeSearchValue(e.target.value.trim())}} sx={{ @@ -464,6 +473,7 @@ const FileUploadForMCFPersonal = ({ } fullWidth + disabled={!isPersonSelected} /> @@ -515,7 +525,7 @@ const FileUploadForMCFPersonal = ({ fullWidth onChange={handleTrackingIDUpdate} required={true} - disabled={disableInput} + disabled={!isPersonSelected} />
@@ -533,6 +543,7 @@ const FileUploadForMCFPersonal = ({ size="small" onClick={()=>{handlePersonalTagChange(tag);if(isScanningTeamMember){handleTagChange(tag.divisionid)}}} clicked={personalTag?.name === tag.name} + disabled={!isPersonSelected || trackingID===""} /> )}
@@ -595,6 +606,7 @@ const FileUploadForMCFPersonal = ({ } fullWidth + disabled={!isPersonSelected || trackingID===""} /> @@ -672,11 +684,12 @@ const FileUploadForMCFPersonal = ({ value="" multiple={multipleFiles} accept={mimeTypes} + disabled={!isPersonSelected} />
{(Object.entries(files).length === 0 && !multipleFiles) || multipleFiles ? - : null}
From 2441a57c696a96cfdec41d8b0bd57841006ca18a Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Wed, 24 Apr 2024 00:34:14 -0700 Subject: [PATCH 08/83] add missing scenario 18, 19, 20 --- .../FileUpload/FileUploadForMCFPersonal.js | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js index 33d6082f7..df8335b3a 100644 --- a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js +++ b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js @@ -94,6 +94,7 @@ const FileUploadForMCFPersonal = ({ const [showAdditionalFileTypes, setShowAdditionalFileTypes] = useState(false); const [isPersonSelected, setIsPersonSelected] = useState(false); + const [enableTrackingIDInput, setEnableTrackingIDInput] = useState(true); const handleUploadBtnClick = (e) => { e.stopPropagation(); @@ -310,6 +311,27 @@ const FileUploadForMCFPersonal = ({ setAdditionalFileTypes(searchFileTypes(otherFileTypes, fileTypeSearchValue, fileType)); },[fileTypeSearchValue, otherFileTypes, fileType]) + React.useEffect(() => { + let filtered = []; + filtered = records.filter((record)=>{ + return Object.keys(person).length !== 0 && record.attributes?.personalattributes?.person === person.name; + }); + + filtered = filtered.filter((record)=>{ + return Object.keys(fileType).length !== 0 && record.attributes?.personalattributes?.filetype === fileType.name; + }); + + if(filtered.length > 0) { + if(filtered[0]?.attributes?.personalattributes?.trackingid) { + setEnableTrackingIDInput(false); + handleTrackingIDChange(filtered[0]?.attributes?.personalattributes?.trackingid); + } + } else { + setEnableTrackingIDInput(true); + handleTrackingIDChange(""); + } + },[records, person, fileType]) + return ( <> {(modalFor === "add" && (uploadFor === "attachment" || uploadFor === 'record')) && (
@@ -525,7 +547,8 @@ const FileUploadForMCFPersonal = ({ fullWidth onChange={handleTrackingIDUpdate} required={true} - disabled={!isPersonSelected} + disabled={!isPersonSelected || !enableTrackingIDInput} + helperText={enableTrackingIDInput?"":"If you've made an error with the file number, you will need to use the edit function in the records log."} />
From 90469a19273deca885d186dc4b891c0c4b8b72ca Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Thu, 25 Apr 2024 09:30:45 -0700 Subject: [PATCH 09/83] add tag edit to dropdown menu --- .../FOI/customComponents/Records/index.js | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index a3826e3bf..2d223a692 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -49,6 +49,7 @@ import { getFullnameList, ConditionalComponent, isrecordtimeout, + isMinistryCoordinator, } from "../../../../helper/FOI/helper"; import Grid from "@material-ui/core/Grid"; import { makeStyles } from "@material-ui/core/styles"; @@ -2282,6 +2283,8 @@ export const RecordsLog = ({ ministryId={ministryId} classes={classes} handleSelectRecord={handleSelectRecord} + setDivisionsModalOpen={setDivisionsModalOpen} + isMCFPersonal={isMCFPersonal} /> )) ) : ( @@ -2599,6 +2602,8 @@ const Attachment = React.memo( isMinistryCoordinator, ministryId, handleSelectRecord, + setDivisionsModalOpen, + isMCFPersonal }) => { const classes = useStyles(); const [disabled, setDisabled] = useState(false); @@ -2797,6 +2802,9 @@ const Attachment = React.memo( disabled={disabled} ministryId={ministryId} setRetry={setRetry} + setDivisionsModalOpen={setDivisionsModalOpen} + isMCFPersonal={isMCFPersonal} + isMinistryCoordinator={isMinistryCoordinator} /> @@ -2876,6 +2884,7 @@ const Attachment = React.memo( ministryId={ministryId} classes={classes} handleSelectRecord={handleSelectRecord} + isMCFPersonal={isMCFPersonal} /> ))} @@ -2897,6 +2906,9 @@ const AttachmentPopup = React.memo( disabled, ministryId, setRetry, + setDivisionsModalOpen, + isMCFPersonal, + isMinistryCoordinator }) => { const ref = React.useRef(); const closeTooltip = () => (ref.current && ref ? ref.current.close() : {}); @@ -2993,7 +3005,7 @@ const AttachmentPopup = React.memo( // return () // } - const ActionsPopover = ({ RestrictViewInBrowser, record }) => { + const ActionsPopover = ({ RestrictViewInBrowser, record, setDivisionsModalOpen, isMCFPersonal, isMinistryCoordinator }) => { return ( setPopoverOpen(false)} > + {(isMCFPersonal && !isMinistryCoordinator) && ( + { + setDivisionsModalOpen(true); + setPopoverOpen(false); + }} + > + Edit Tags + + )} {!RestrictViewInBrowser ? ( { @@ -3124,7 +3146,7 @@ const AttachmentPopup = React.memo( > - + ); } From 50b3313da5240ba39b400745b4feb2a9a87e721e Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Mon, 6 May 2024 16:15:18 -0700 Subject: [PATCH 10/83] 1. seperate cfd tag edit modal for iao and ministry 2. backend for update individual (apply all tag changes) 3. backend for update all (apply person & filetype changes to all) --- .../src/apiManager/endpoints/index.js | 1 + .../services/FOI/foiRecordServices.js | 15 + .../customComponents/Records/MCFPersonal.js | 670 ++++++++++++++---- .../Records/MCFPersonalMinistry.js | 207 ++++++ .../FOI/customComponents/Records/index.js | 156 +++- .../request_api/resources/foirecord.py | 24 +- .../request_api/schemas/foirecord.py | 9 +- .../request_api/services/recordservice.py | 28 +- 8 files changed, 947 insertions(+), 163 deletions(-) create mode 100644 forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonalMinistry.js diff --git a/forms-flow-web/src/apiManager/endpoints/index.js b/forms-flow-web/src/apiManager/endpoints/index.js index 40eab5b9a..adc3f079a 100644 --- a/forms-flow-web/src/apiManager/endpoints/index.js +++ b/forms-flow-web/src/apiManager/endpoints/index.js @@ -111,6 +111,7 @@ const API = { FOI_REPLACE_RECORDS: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//record//replace`, FOI_POST_RECORDS: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest/`, FOI_UPDATE_RECORDS: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//update`, + FOI_UPDATE_PERSONAL_ATTRIBUTES: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//updatepersonalattributes`, DOC_REVIEWER_DELETE_RECORDS: `${DOC_REVIEWER_BASE_API_URL}/api/document/delete`, DOC_REVIEWER_REDACTED_SECTIONS: `${DOC_REVIEWER_BASE_API_URL}/api/redactedsections/ministryrequest/`, diff --git a/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js b/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js index 8c2d48a61..6a7f60629 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js @@ -254,6 +254,21 @@ export const updateFOIRecords = (requestId, ministryId, data, ...rest) => { }; }; +export const editPersonalAttributes = (requestId, ministryId, data, ...rest) => { + if (!ministryId) { + return () => {}; + } + const done = fnDone(rest); + let apiUrl = replaceUrl( + replaceUrl(API.FOI_UPDATE_PERSONAL_ATTRIBUTES, "", ministryId), + "", + requestId + ); + return (dispatch) => { + postRecord(dispatch, apiUrl, data, "Error in updating records", rest); + }; +}; + export const deleteReviewerRecords = (filepaths, ...rest) => { const done = fnDone(rest); let apiUrl = API.DOC_REVIEWER_DELETE_RECORDS; diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js b/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js index 371abf4dc..4203214b5 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js @@ -4,39 +4,161 @@ import { ClickableChip } from "../../Dashboard/utils"; import "../FileUpload/FileUpload.scss"; +import Dialog from "@material-ui/core/Dialog"; +import DialogActions from "@material-ui/core/DialogActions"; +import DialogContent from "@material-ui/core/DialogContent"; +import DialogContentText from "@material-ui/core/DialogContentText"; +import DialogTitle from "@material-ui/core/DialogTitle"; +import CloseIcon from "@material-ui/icons/Close"; import Grid from "@material-ui/core/Grid"; import Paper from "@mui/material/Paper"; import SearchIcon from "@material-ui/icons/Search"; import InputAdornment from "@mui/material/InputAdornment"; import InputBase from "@mui/material/InputBase"; import IconButton from "@material-ui/core/IconButton"; +import TextField from '@material-ui/core/TextField'; +import AddCircleIcon from '@mui/icons-material/AddCircle'; import _ from 'lodash'; import { MCFPopularSections } from "../../../../constants/FOI/enum"; const MCFPersonal = ({ + editTagModalOpen, + setEditTagModalOpen, setNewDivision, - tagValue, - divisionModalTagValue, - divisions=[], - isMinistryCoordinator + // tagValue, + curPersonalAttributes, + setNewPersonalAttributes, + updatePersonalAttributes, + updatePersonalAttributesForAll }) => { + const [personalAttributes, setPersonalAttributes] = useState(); + useEffect(() => { + if(!personalAttributes || (personalAttributes?.person === "" && curPersonalAttributes?.person !== "")) { + setPersonalAttributes(curPersonalAttributes); + } + },[curPersonalAttributes]) const [searchValue, setSearchValue] = useState(""); const [showAdditionalTags, setShowAdditionalTags] = useState(false); const [additionalTagList, setAdditionalTagList] = useState([]); const MCFSections = useSelector((state) => state.foiRequests.foiPersonalSections); - const [tagList, setTagList] = useState(MCFSections?.sections?.slice(0, MCFPopularSections-1)); - const [otherTagList, setOtherTagList] = useState(MCFSections?.sections?.slice(MCFPopularSections)); - + const [tagList, setTagList] = useState([]); + const [otherTagList, setOtherTagList] = useState([]); + + const MCFPeople = useSelector( + (state) => state.foiRequests.foiPersonalPeople + ); + const MCFFiletypes = useSelector( + (state) => state.foiRequests.foiPersonalFiletypes + ); + const MCFVolumes = useSelector( + (state) => state.foiRequests.foiPersonalVolumes + ); + + const [allPeople, setAllPeople] = useState([]); + const [people, setPeople] = useState([]); + + const [allVolumes, setAllVolumes] = useState([]); + const [volumes, setVolumes] = useState([]); + + const [fileTypes, setFileTypes] = useState([]); + const [otherFileTypes, setOtherFileTypes] = useState([]); + + + const [showAllPeople, setShowAllPeople] = useState(false); + const [showAllVolumes, setShowAllVolumes] = useState(false); + const [fileTypeSearchValue, setFileTypeSearchValue] = useState(""); + const [additionalFileTypes, setAdditionalFileTypes] = useState([]); + const [showAdditionalFileTypes, setShowAdditionalFileTypes] = useState(false); + useEffect(() => { - setTagList(MCFSections?.sections?.slice(0, MCFPopularSections-1)); - setOtherTagList(MCFSections?.sections?.slice(MCFPopularSections)); + if(MCFSections?.sections) { + if(MCFSections.sections.length > MCFPopularSections-1) { + setTagList(MCFSections.sections.slice(0, MCFPopularSections-1)); + setOtherTagList(MCFSections.sections.slice(MCFPopularSections)); + } else { + setTagList(MCFSections.sections); + setOtherTagList([]); + } + } },[MCFSections]) useEffect(() => { - setAdditionalTagList(searchSections(otherTagList, searchValue, tagValue)); - },[searchValue, otherTagList, tagValue]) + if(MCFPeople?.people) { + setAllPeople(MCFPeople.people); + if(MCFPeople.people.length > 5) { + setPeople(MCFPeople.people.slice(0, 5)); + } else { + setPeople(MCFPeople.people); + } + } + },[MCFPeople]) + + useEffect(() => { + if(MCFVolumes?.volumes) { + setAllVolumes(MCFVolumes.volumes); + if(MCFFiletypes.filetypes.length > 5) { + setVolumes(MCFVolumes.volumes.slice(0, 5)); + } else { + setVolumes(MCFVolumes.volumes); + } + } + },[MCFVolumes]) + + useEffect(() => { + if(MCFFiletypes?.filetypes) { + if(MCFFiletypes.filetypes.length > 6) { + setFileTypes(MCFFiletypes.filetypes.slice(0, 6)); + setOtherFileTypes(MCFFiletypes.filetypes.slice(6, MCFFiletypes.filetypes.length)) + } else { + setFileTypes(MCFFiletypes.filetypes); + setOtherFileTypes([]) + } + } + },[MCFFiletypes]) + + const searchFileTypes = (_fileTypeArray, _keyword, _selectedFileType) => { + let newFileTypeArray = []; + if(_keyword || _selectedFileType) { + _fileTypeArray.map((section) => { + if(_keyword && section.name.toLowerCase().includes(_keyword.toLowerCase())) { + newFileTypeArray.push(section); + } else if(section.name === _selectedFileType) { + newFileTypeArray.unshift(section); + } + }); + } + + if(newFileTypeArray.length > 0) { + setShowAdditionalFileTypes(true); + } else { + setShowAdditionalFileTypes(false); + } + + return newFileTypeArray; + } + + React.useEffect(() => { + if(showAllPeople) { + setPeople(allPeople) + } else { + setPeople(allPeople.slice(0, 5)) + } + if(showAllVolumes) { + setVolumes(allVolumes) + } else { + setVolumes(allVolumes.slice(0, 5)) + } + },[showAllPeople, showAllVolumes]) + + React.useEffect(() => { + setAdditionalFileTypes(searchFileTypes(otherFileTypes, fileTypeSearchValue, personalAttributes?.filetype)); + },[fileTypeSearchValue, otherFileTypes, personalAttributes]) + + useEffect(() => { + setAdditionalTagList(searchSections(otherTagList, searchValue, personalAttributes?.personaltag)); + },[searchValue, otherTagList, personalAttributes]) const searchSections = (_sectionArray, _keyword, _selectedSectionValue) => { let newSectionArray = []; @@ -58,149 +180,405 @@ const MCFPersonal = ({ return newSectionArray; } + const handlePersonalAttributesChange = (_attribute, _type) => { + let _newPersonalAttributes = { + person: personalAttributes.person, + filetype: personalAttributes.filetype, + volume: personalAttributes.volume, + trackingid: personalAttributes.trackingid, + personaltag: personalAttributes.personaltag + }; + + switch(_type) { + case "person": + _newPersonalAttributes.person = _attribute.name; + setPersonalAttributes(_newPersonalAttributes); + setNewPersonalAttributes(_newPersonalAttributes); + return; + case "filetype": + _newPersonalAttributes.filetype = _attribute.name; + setPersonalAttributes(_newPersonalAttributes); + setNewPersonalAttributes(_newPersonalAttributes); + return; + case "volume": + _newPersonalAttributes.volume = _attribute.name; + setPersonalAttributes(_newPersonalAttributes); + setNewPersonalAttributes(_newPersonalAttributes); + return; + case "trackingid": + _newPersonalAttributes.trackingid = _attribute; + setPersonalAttributes(_newPersonalAttributes); + setNewPersonalAttributes(_newPersonalAttributes); + return; + case "personaltag": + _newPersonalAttributes.personaltag = _attribute.name; + setPersonalAttributes(_newPersonalAttributes); + setNewPersonalAttributes(_newPersonalAttributes); + return; + default: + return; + } + }; + return ( - <> -
- - {isMinistryCoordinator && divisions.length > 0 && divisions.filter(div => div.divisionid !== tagValue).length > 0 && (<> -
- {divisions.filter(div => { - return div.divisionid !== tagValue; - }).map(tag => - {setNewDivision(tag.divisionid)}} - clicked={divisionModalTagValue == tag.divisionid} - /> - )} -
- )} - - {!isMinistryCoordinator && (<> -
- {tagList.filter(div => { - return div.divisionid !== tagValue; - }).map(tag => - {setNewDivision(tag.divisionid)}} - clicked={divisionModalTagValue == tag.divisionid} - /> - )} -
-
- - + setEditTagModalOpen(false)} + aria-labelledby="state-change-dialog-title" + aria-describedby="state-change-dialog-description" + maxWidth={"md"} + fullWidth={true} + // id="state-change-dialog" + > + +

Edit Tags

+ setEditTagModalOpen(false)} > - - - {setSearchValue(e.target.value.trim())}} + Close + + +
+ + +
+ + Update the tag you want to associate with the selected record. + If this record is from the applicant's file, please select + applicant otherwise you can associate it to a different person. + If you choose to update all by ID, this edit will apply to all + records associated with the tracking ID and file type associated + to that person. + +
+ +
+ Select Person: * +
+
+ {people.map(p => + {handlePersonalAttributesChange(p, "person")}} + clicked={personalAttributes?.person === p.name} + /> + )} + {!showAllPeople && ({setShowAllPeople(true)}} + />)} +
+ +
+ Select Volume: +
+
+ {volumes.map(v => + {handlePersonalAttributesChange(v, "volume")}} + clicked={personalAttributes?.volume === v.name} + /> + )} + {!showAllVolumes && ({setShowAllVolumes(true)}} + />)} +
+ +
+ Select File Type: * +
+
+ {fileTypes.map(f => + {handlePersonalAttributesChange(f, "filetype")}} + clicked={personalAttributes?.filetype === f.name} + /> + )} +
+
+ - - Search any additional sections here - - - - } + > + + + + {setFileTypeSearchValue(e.target.value.trim())}} + sx={{ + color: "#38598A", + }} + startAdornment={ + + + Search any additional filetypes here + + + + } + fullWidth + /> + + + {showAdditionalFileTypes === true && ( + {additionalFileTypes.map(f => + {handlePersonalAttributesChange(f, "filetype")}} + clicked={personalAttributes?.filetype === f.name} + /> + )} + )} + +
+ +
+ {handlePersonalAttributesChange(e.target.value.trim(), "trackingid")}} + required={true} /> - - - {showAdditionalTags === true && ( + +
+ Select Section Name: +
+
+ {tagList.map(tag => + {handlePersonalAttributesChange(tag, "personaltag");setNewDivision(tag.divisionid);}} + clicked={personalAttributes?.personaltag === tag.name} + /> + )} +
+
+ + + + + {setSearchValue(e.target.value.trim())}} + sx={{ + color: "#38598A", + }} + startAdornment={ + + + Search any additional sections here + + + + } + fullWidth + /> + + + {showAdditionalTags === true && ( + {additionalTagList.map(tag => + {handlePersonalAttributesChange(tag, "personaltag");setNewDivision(tag.divisionid);}} + clicked={personalAttributes?.personaltag === tag.name} + /> + )} + )} + +
+ + + +
- )} + Update for Individual + + + + +
- ); }; diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonalMinistry.js b/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonalMinistry.js new file mode 100644 index 000000000..d08427189 --- /dev/null +++ b/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonalMinistry.js @@ -0,0 +1,207 @@ +import React, { useState, useEffect } from "react"; +import { useSelector } from "react-redux"; +import { + ClickableChip +} from "../../Dashboard/utils"; +import "../FileUpload/FileUpload.scss"; +import Grid from "@material-ui/core/Grid"; +import Paper from "@mui/material/Paper"; +import SearchIcon from "@material-ui/icons/Search"; +import InputAdornment from "@mui/material/InputAdornment"; +import InputBase from "@mui/material/InputBase"; +import IconButton from "@material-ui/core/IconButton"; +import _ from 'lodash'; +import { MCFPopularSections } from "../../../../constants/FOI/enum"; + +const MCFPersonalMinistry = ({ + setNewDivision, + tagValue, + divisionModalTagValue, + divisions=[], + isMinistryCoordinator +}) => { + + const [searchValue, setSearchValue] = useState(""); + const [showAdditionalTags, setShowAdditionalTags] = useState(false); + const [additionalTagList, setAdditionalTagList] = useState([]); + + const MCFSections = useSelector((state) => state.foiRequests.foiPersonalSections); + const [tagList, setTagList] = useState(MCFSections?.sections?.slice(0, MCFPopularSections-1)); + const [otherTagList, setOtherTagList] = useState(MCFSections?.sections?.slice(MCFPopularSections)); + + useEffect(() => { + setTagList(MCFSections?.sections?.slice(0, MCFPopularSections-1)); + setOtherTagList(MCFSections?.sections?.slice(MCFPopularSections)); + },[MCFSections]) + + useEffect(() => { + setAdditionalTagList(searchSections(otherTagList, searchValue, tagValue)); + },[searchValue, otherTagList, tagValue]) + + const searchSections = (_sectionArray, _keyword, _selectedSectionValue) => { + let newSectionArray = []; + if(_keyword || _selectedSectionValue) { + _sectionArray.map((section) => { + if(_keyword && section.name.toLowerCase().includes(_keyword.toLowerCase())) { + newSectionArray.push(section); + } else if(section.divisionid === _selectedSectionValue) { + newSectionArray.unshift(section); + } + }); + } + + if(newSectionArray.length > 0) { + setShowAdditionalTags(true); + } else { + setShowAdditionalTags(false); + } + return newSectionArray; + } + + return ( + <> +
+ + {isMinistryCoordinator && divisions.length > 0 && divisions.filter(div => div.divisionid !== tagValue).length > 0 && (<> +
+ {divisions.filter(div => { + return div.divisionid !== tagValue; + }).map(tag => + {setNewDivision(tag.divisionid)}} + clicked={divisionModalTagValue == tag.divisionid} + /> + )} +
+ )} + + {!isMinistryCoordinator && (<> +
+ {tagList.filter(div => { + return div.divisionid !== tagValue; + }).map(tag => + {setNewDivision(tag.divisionid)}} + clicked={divisionModalTagValue == tag.divisionid} + /> + )} +
+
+ + + + + {setSearchValue(e.target.value.trim())}} + sx={{ + color: "#38598A", + }} + startAdornment={ + + + Search any additional sections here + + + + } + fullWidth + /> + + + {showAdditionalTags === true && ( + {additionalTagList.filter(div => { + return div.divisionid !== tagValue; + }).map(tag => + {setNewDivision(tag.divisionid)}} + clicked={divisionModalTagValue == tag.divisionid} + /> + )} + )} + +
+ )} +
+ + ); +}; + +export default MCFPersonalMinistry; \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 2d223a692..f6c2b93c6 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -33,6 +33,7 @@ import { fetchPDFStitchedRecordForOIPCRedline, fetchPDFStitchedRecordForOIPCRedlineReview, checkForRecordsChange, + editPersonalAttributes, } from "../../../../apiManager/services/FOI/foiRecordServices"; import { StateTransitionCategories, @@ -119,6 +120,7 @@ import { MinistryNeedsScanning } from "../../../../constants/FOI/enum"; import FOI_COMPONENT_CONSTANTS from "../../../../constants/FOI/foiComponentConstants"; import MCFPersonal from "./MCFPersonal"; import MSDPersonal from "./MSDPersonal"; +import MCFPersonalMinistry from "./MCFPersonalMinistry"; const useStyles = makeStyles((_theme) => ({ createButton: { @@ -225,6 +227,7 @@ export const RecordsLog = ({ const userGroups = user?.groups?.map((group) => group.slice(1)); let recordsObj = useSelector((state) => state.foiRequests.foiRequestRecords); + console.log("recordsObj: ", recordsObj); let pdfStitchStatus = useSelector( (state) => state.foiRequests.foiPDFStitchStatusForHarms @@ -306,7 +309,22 @@ export const RecordsLog = ({ dispatch(getRecordFormats()); }, []); + const [currentEditRecord, setCurrentEditRecord] = useState(); + const [editTagModalOpen, setEditTagModalOpen] = useState(false); + const [curPersonalAttributes, setCurPersonalAttributes] = useState({ + person: "", + filetype: "", + volume: "", + trackingid: "", + personaltag: "TBD" + }); + const [newPersonalAttributes, setNewPersonalAttributes] = useState(); + useEffect(() => { + if(currentEditRecord?.attributes?.personalattributes) + setCurPersonalAttributes(currentEditRecord.attributes.personalattributes); + },[currentEditRecord]) + const MCFPeople = useSelector( (state) => state.foiRequests.foiPersonalPeople ); @@ -659,7 +677,6 @@ export const RecordsLog = ({ (fileInfo) => fileInfo.filename === header.filename ); var documentDetails; - let personalAttributes; if (modalFor === "replace") { documentDetails = { filename: header.filename, @@ -690,7 +707,6 @@ export const RecordsLog = ({ documentDetails.attributes.trigger = "recordreplace"; delete documentDetails.outputdocumentmasterid; } else { - personalAttributes = {divisions: [{ divisionid: _fileInfo.divisionid }], ..._fileInfo.personalattributes} documentDetails = { s3uripath: header.filepathdb, filename: header.filename, @@ -700,7 +716,7 @@ export const RecordsLog = ({ ? _file.lastModifiedDate : new Date(_file.lastModified), filesize: _file.size, - personalattributes: personalAttributes, + personalattributes: _fileInfo.personalattributes, }, }; } @@ -1787,6 +1803,77 @@ export const RecordsLog = ({ ); }; + const comparePersonalAttributes = (a, b) => { + return a?.person === b?.person && a?.volume === b?.volume + && a?.filetype === b?.filetype && a?.personaltag === b?.personaltag; + }; + + const updatePersonalAttributes = (_all = false) => { + setDivisionsModalOpen(false); + // newPersonalAttributes + // currentEditRecord + var updateRecords = []; + + if(_all) { + for (let record of records) { + if(record.attributes?.personalattributes?.person + && record.attributes?.personalattributes?.person === currentEditRecord.attributes?.personalattributes?.person + && record.attributes?.personalattributes?.filetype + && record.attributes?.personalattributes?.filetype === currentEditRecord.attributes?.personalattributes?.filetype + ) { + updateRecords.push( + (({ recordid, documentmasterid, s3uripath }) => ({ + recordid, + documentmasterid, + filepath: s3uripath, + }))(record) + ); + } + + if(record.attachments) { + for (let attachment of record.attachments) { + if(attachment.attributes?.personalattributes?.person + && attachment.attributes?.personalattributes?.person === currentEditRecord.attributes?.personalattributes?.person + && attachment.attributes?.personalattributes?.filetype + && attachment.attributes?.personalattributes?.filetype === currentEditRecord.attributes?.personalattributes?.filetype + ) { + updateRecords.push( + (({ documentmasterid, s3uripath }) => ({ + documentmasterid, + filepath: s3uripath, + }))(attachment) + ); + } + } + } + } + } else { + updateRecords.push( + { + recordid: currentEditRecord.recordid, + documentmasterid: currentEditRecord.documentmasterid, + filepath: currentEditRecord.s3uripath + } + ); + } + + if(currentEditRecord && !comparePersonalAttributes(newPersonalAttributes, curPersonalAttributes)) { + dispatch( + editPersonalAttributes( + requestId, + ministryId, + { + records: updateRecords, + newpersonalattributes: newPersonalAttributes + }, + (err, _res) => { + dispatchRequestAttachment(err); + } + ) + ); + } + }; + //function to manage download for harms option const enableHarmsDonwnload = () => { return !recordsObj.records.every( @@ -2285,6 +2372,8 @@ export const RecordsLog = ({ handleSelectRecord={handleSelectRecord} setDivisionsModalOpen={setDivisionsModalOpen} isMCFPersonal={isMCFPersonal} + setEditTagModalOpen={setEditTagModalOpen} + setCurrentEditRecord={setCurrentEditRecord} /> )) ) : ( @@ -2385,7 +2474,26 @@ export const RecordsLog = ({
-
+ + {isMCFPersonal?( + r.isselected)[0]?.attributes + // .divisions[0].divisionid + // } + // divisionModalTagValue={divisionModalTagValue} + // divisions={divisions} + curPersonalAttributes={curPersonalAttributes} + setNewPersonalAttributes={setNewPersonalAttributes} + updatePersonalAttributes={updatePersonalAttributes} + updatePersonalAttributesForAll={updatePersonalAttributes} + /> + ):( +
setDivisionsModalOpen(false)} @@ -2436,8 +2544,8 @@ export const RecordsLog = ({ {requestType == FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_PERSONAL ? ( - bcgovcode == "MCF" ? ( - r.isselected)[0]?.attributes @@ -2587,6 +2695,7 @@ export const RecordsLog = ({
+ )} )}
@@ -2603,7 +2712,9 @@ const Attachment = React.memo( ministryId, handleSelectRecord, setDivisionsModalOpen, - isMCFPersonal + isMCFPersonal, + setEditTagModalOpen, + setCurrentEditRecord }) => { const classes = useStyles(); const [disabled, setDisabled] = useState(false); @@ -2802,9 +2913,10 @@ const Attachment = React.memo( disabled={disabled} ministryId={ministryId} setRetry={setRetry} - setDivisionsModalOpen={setDivisionsModalOpen} + setEditTagModalOpen={setEditTagModalOpen} isMCFPersonal={isMCFPersonal} isMinistryCoordinator={isMinistryCoordinator} + setCurrentEditRecord={setCurrentEditRecord} /> @@ -2885,6 +2997,8 @@ const Attachment = React.memo( classes={classes} handleSelectRecord={handleSelectRecord} isMCFPersonal={isMCFPersonal} + setEditTagModalOpen={setEditTagModalOpen} + setCurrentEditRecord={setCurrentEditRecord} /> ))} @@ -2906,9 +3020,10 @@ const AttachmentPopup = React.memo( disabled, ministryId, setRetry, - setDivisionsModalOpen, + setEditTagModalOpen, isMCFPersonal, - isMinistryCoordinator + isMinistryCoordinator, + setCurrentEditRecord }) => { const ref = React.useRef(); const closeTooltip = () => (ref.current && ref ? ref.current.close() : {}); @@ -3005,7 +3120,14 @@ const AttachmentPopup = React.memo( // return () // } - const ActionsPopover = ({ RestrictViewInBrowser, record, setDivisionsModalOpen, isMCFPersonal, isMinistryCoordinator }) => { + const ActionsPopover = ({ + RestrictViewInBrowser, + record, + setEditTagModalOpen, + isMCFPersonal, + isMinistryCoordinator, + setCurrentEditRecord + }) => { return ( { - setDivisionsModalOpen(true); + setCurrentEditRecord(record); + setEditTagModalOpen(true); setPopoverOpen(false); }} > @@ -3146,7 +3269,14 @@ const AttachmentPopup = React.memo( > - + ); } diff --git a/request-management-api/request_api/resources/foirecord.py b/request-management-api/request_api/resources/foirecord.py index 71fa93b55..6e5f1f66b 100644 --- a/request-management-api/request_api/resources/foirecord.py +++ b/request-management-api/request_api/resources/foirecord.py @@ -22,7 +22,7 @@ from request_api.utils.util import cors_preflight, allowedorigins, getrequiredmemberships from request_api.exceptions import BusinessException, Error from request_api.services.recordservice import recordservice -from request_api.schemas.foirecord import FOIRequestBulkCreateRecordSchema, FOIRequestBulkRetryRecordSchema, FOIRequestRecordDownloadSchema, FOIRequestReplaceRecordSchema, FOIRequestRecordUpdateSchema +from request_api.schemas.foirecord import FOIRequestBulkCreateRecordSchema, FOIRequestBulkRetryRecordSchema, FOIRequestRecordDownloadSchema, FOIRequestReplaceRecordSchema, FOIRequestRecordUpdateSchema, FOIRequestPersonalAttributesUpdateSchema from marshmallow import INCLUDE import json from flask_cors import cross_origin @@ -97,6 +97,28 @@ def post(requestid, ministryrequestid): except BusinessException as exception: return {'status': exception.status_code, 'message':exception.message}, 500 +@cors_preflight('POST,OPTIONS') +@API.route('/foirecord//ministryrequest//updatepersonalattributes') +class UpdateFOIDocument(Resource): + """Resource for soft delete FOI requests.""" + + + @staticmethod + @TRACER.trace() + @cross_origin(origins=allowedorigins()) + @auth.require + @auth.ismemberofgroups(getrequiredmemberships()) + def post(requestid, ministryrequestid): + try: + requestjson = request.get_json() + data = FOIRequestPersonalAttributesUpdateSchema().load(requestjson) + result = recordservice().updatepersonalattributes(requestid, ministryrequestid, data, AuthHelper.getuserid()) + return {'status': result.success, 'message':result.message,'id':result.identifier} , 200 + except KeyError as error: + return {'status': False, 'message': CUSTOM_KEYERROR_MESSAGE + str(error)}, 400 + except BusinessException as exception: + return {'status': exception.status_code, 'message':exception.message}, 500 + @cors_preflight('POST,OPTIONS') @API.route('/foirecord//ministryrequest//retry') class RetryFOIDocument(Resource): diff --git a/request-management-api/request_api/schemas/foirecord.py b/request-management-api/request_api/schemas/foirecord.py index cbd3c0d8e..e1ab031e8 100644 --- a/request-management-api/request_api/schemas/foirecord.py +++ b/request-management-api/request_api/schemas/foirecord.py @@ -26,7 +26,7 @@ class Meta: # pylint: disable=too-few-public-methods volume = fields.Str(data_key="volume",allow_none=True) trackingid = fields.Str(data_key="trackingid",allow_none=True) personaltag = fields.Str(data_key="personaltag",allow_none=True) - divisions = fields.Nested(DivisionSchema, many=True, validate=validate.Length(min=1), required=True,allow_none=False) + # divisions = fields.Nested(DivisionSchema, many=True, validate=validate.Length(min=1), required=True,allow_none=False) class CreateRecordAttributeSchema(Schema): class Meta: # pylint: disable=too-few-public-methods @@ -175,5 +175,10 @@ class Meta: # pylint: disable=too-few-public-methods divisions = fields.Nested(DivisionSchema,many=True,validate=validate.Length(min=1),required=False,allow_none=True) isdelete = fields.Boolean(required=True,allow_none=False) - +class FOIRequestPersonalAttributesUpdateSchema(Schema): + class Meta: # pylint: disable=too-few-public-methods + """Exclude unknown fields in the deserialized output.""" + unknown = EXCLUDE + records = fields.Nested(RecordSchema,many=True,data_key="records",required=True) + newpersonalattributes = fields.Nested(PersonalAttributesSchema,required=True) diff --git a/request-management-api/request_api/services/recordservice.py b/request-management-api/request_api/services/recordservice.py index 7ec6df6c3..65d96564b 100644 --- a/request-management-api/request_api/services/recordservice.py +++ b/request-management-api/request_api/services/recordservice.py @@ -67,7 +67,33 @@ def update(self, requestid, ministryrequestid, requestdata, userid): return DefaultMethodResult(True,'Record updated in Doc Reviewer DB', -1, [record['documentmasterid'] for record in requestdata['records']]) else: return DefaultMethodResult(False,'Error in updating Record', -1, [record['documentmasterid'] for record in requestdata['records']]) - + + def updatepersonalattributes(self, requestid, ministryrequestid, requestdata, userid): + newrecords = [] + recordids = [r['recordid'] for r in requestdata['records'] if r.get('recordid') is not None] + response = DefaultMethodResult(True, 'No recordids') + # divisions = [] + if(len(recordids) > 0): + records = FOIRequestRecord.getrecordsbyid(recordids) + for record in records: + record['attributes'] = json.loads(record['attributes']) + record['attributes']['personalattributes'] = requestdata['newpersonalattributes'] + # divisions = divisions + [div for div in record['attributes']['divisions'] if div not in divisions] + record.update({'updated_at': datetime.now(), 'updatedby': userid}) + record['version'] += 1 + newrecord = FOIRequestRecord() + newrecord.__dict__.update(record) + newrecords.append(newrecord) + response = FOIRequestRecord.create(newrecords) + if response.success: + # _apiresponse, err = self.makedocreviewerrequest('POST', '/api/document/update', {'ministryrequestid': ministryrequestid, 'documentmasterids': [record['documentmasterid'] for record in requestdata['records']], 'divisions': divisions, 'personalattributes': requestdata['newpersonalattributes']}) + _apiresponse, err = self.makedocreviewerrequest('POST', '/api/document/update/personal', {'ministryrequestid': ministryrequestid, 'documentmasterids': [record['documentmasterid'] for record in requestdata['records']], 'personalattributes': requestdata['newpersonalattributes']}) + if err: + return DefaultMethodResult(False,'Error in contacting Doc Reviewer API', -1, [record['documentmasterid'] for record in requestdata['records']]) + return DefaultMethodResult(True,'Record updated in Doc Reviewer DB', -1, [record['documentmasterid'] for record in requestdata['records']]) + else: + return DefaultMethodResult(False,'Error in updating Record', -1, [record['documentmasterid'] for record in requestdata['records']]) + def retry(self, _requestid, ministryrequestid, data): _ministryrequest = FOIMinistryRequest.getrequestbyministryrequestid(ministryrequestid) for record in data['records']: From 5289636573f62a627cbb15cde339c9255cbd9967 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Mon, 6 May 2024 22:46:21 -0700 Subject: [PATCH 11/83] bug fix --- .../customComponents/Records/MCFPersonal.js | 28 +++++++---- .../FOI/customComponents/Records/index.js | 48 +++++++------------ 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js b/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js index 4203214b5..59889bf3c 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js @@ -29,13 +29,12 @@ const MCFPersonal = ({ curPersonalAttributes, setNewPersonalAttributes, updatePersonalAttributes, - updatePersonalAttributesForAll + setCurPersonalAttributes, + setCurrentEditRecord }) => { const [personalAttributes, setPersonalAttributes] = useState(); useEffect(() => { - if(!personalAttributes || (personalAttributes?.person === "" && curPersonalAttributes?.person !== "")) { - setPersonalAttributes(curPersonalAttributes); - } + setPersonalAttributes(curPersonalAttributes); },[curPersonalAttributes]) const [searchValue, setSearchValue] = useState(""); @@ -220,12 +219,25 @@ const MCFPersonal = ({ } }; + const handleClose = () => { + setCurrentEditRecord(); + setCurPersonalAttributes({ + person: "", + filetype: "", + volume: "", + trackingid: "", + personaltag: "TBD" + }); + setNewPersonalAttributes(); + setEditTagModalOpen(false); + }; + return (
setEditTagModalOpen(false)} + onClose={() => handleClose()} aria-labelledby="state-change-dialog-title" aria-describedby="state-change-dialog-description" maxWidth={"md"} @@ -236,7 +248,7 @@ const MCFPersonal = ({

Edit Tags

setEditTagModalOpen(false)} + onClick={() => handleClose()} > Close @@ -566,13 +578,13 @@ const MCFPersonal = ({ diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index f6c2b93c6..8ccf24042 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -227,7 +227,6 @@ export const RecordsLog = ({ const userGroups = user?.groups?.map((group) => group.slice(1)); let recordsObj = useSelector((state) => state.foiRequests.foiRequestRecords); - console.log("recordsObj: ", recordsObj); let pdfStitchStatus = useSelector( (state) => state.foiRequests.foiPDFStitchStatusForHarms @@ -1809,9 +1808,7 @@ export const RecordsLog = ({ }; const updatePersonalAttributes = (_all = false) => { - setDivisionsModalOpen(false); - // newPersonalAttributes - // currentEditRecord + setEditTagModalOpen(false); var updateRecords = []; if(_all) { @@ -1871,6 +1868,16 @@ export const RecordsLog = ({ } ) ); + + setCurrentEditRecord(); + setCurPersonalAttributes({ + person: "", + filetype: "", + volume: "", + trackingid: "", + personaltag: "TBD" + }); + setNewPersonalAttributes(); } }; @@ -1912,17 +1919,6 @@ export const RecordsLog = ({ id="download" label={currentDownload === 0 ? "Download" : ""} inputProps={{ "aria-labelledby": "download-label" }} - // InputProps={{ - // startAdornment: isDownloadInProgress && - // {/* */} - // {/* */} - // record.isredactionready ? - // : - // record.failed ? - // : - // - // - // }} InputLabelProps={{ shrink: false }} select name="download" @@ -2481,16 +2477,12 @@ export const RecordsLog = ({ setEditTagModalOpen={setEditTagModalOpen} record={currentEditRecord} setNewDivision={setDivisionModalTagValue} - // tagValue={ - // records.filter((r) => r.isselected)[0]?.attributes - // .divisions[0].divisionid - // } - // divisionModalTagValue={divisionModalTagValue} - // divisions={divisions} + curPersonalAttributes={curPersonalAttributes} setNewPersonalAttributes={setNewPersonalAttributes} updatePersonalAttributes={updatePersonalAttributes} - updatePersonalAttributesForAll={updatePersonalAttributes} + setCurrentEditRecord={setCurrentEditRecord} + setCurPersonalAttributes={setCurPersonalAttributes} /> ):(
@@ -3114,19 +3106,12 @@ const AttachmentPopup = React.memo( ); }; - // const AddMenuItems = () => { - // if (showReplace(record.category)) - // return () - // return () - // } - const ActionsPopover = ({ RestrictViewInBrowser, record, setEditTagModalOpen, isMCFPersonal, - isMinistryCoordinator, - setCurrentEditRecord + isMinistryCoordinator }) => { return ( { - setCurrentEditRecord(record); setEditTagModalOpen(true); setPopoverOpen(false); }} @@ -3263,6 +3247,7 @@ const AttachmentPopup = React.memo( color="primary" disabled={disabled} onClick={(e) => { + setCurrentEditRecord(record); setPopoverOpen(true); setAnchorPosition(e?.currentTarget?.getBoundingClientRect()); }} @@ -3275,7 +3260,6 @@ const AttachmentPopup = React.memo( setEditTagModalOpen={setEditTagModalOpen} isMCFPersonal={isMCFPersonal} isMinistryCoordinator={isMinistryCoordinator} - setCurrentEditRecord={setCurrentEditRecord} /> ); From bd8200286dc7acded013f30167512ff82cf13884 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Mon, 6 May 2024 23:33:09 -0700 Subject: [PATCH 12/83] fix tag chip sort order --- .../FOI/customComponents/Records/index.js | 72 +++++++++++++++---- 1 file changed, 58 insertions(+), 14 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 889335159..7afa89a63 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -2717,7 +2717,6 @@ const Attachment = React.memo( const classes = useStyles(); const [disabled, setDisabled] = useState(false); const [isRetry, setRetry] = useState(false); - const [personalAtts, setPersonalAtts] = useState([]); // useEffect(() => { // if(record && record.filename) { @@ -2735,18 +2734,6 @@ const Attachment = React.memo( handleSelectRecord(record, e); }; - useEffect(() => { - let _personalAtts = []; - if(record.attributes?.personalattributes) { - Object.entries(record.attributes?.personalattributes).forEach(([key, value]) => { - if(key !== "divisions" && value) { - _personalAtts.push({divisionname: value}); - } - }); - } - setPersonalAtts(_personalAtts); - }, [record]) - const recordtitle = () => { if (disabled) { return ( @@ -2927,7 +2914,7 @@ const Attachment = React.memo( alignItems="flex-start" > - {[...record.attributes?.divisions, ...personalAtts].map((division, i) => ( + {record.attributes?.divisions.map((division, i) => ( ))} + {record.attributes?.personalattributes?.person && + + } + {record.attributes?.personalattributes?.filetype && + + } + {record.attributes?.personalattributes?.volume && + + } + {record.attributes?.personalattributes?.trackingid && + + } Date: Tue, 14 May 2024 22:31:55 -0700 Subject: [PATCH 13/83] update api for docreviewer cfd personal --- .../request_api/resources/foiflowmasterdata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/request-management-api/request_api/resources/foiflowmasterdata.py b/request-management-api/request_api/resources/foiflowmasterdata.py index d21456dc3..207a943f8 100644 --- a/request-management-api/request_api/resources/foiflowmasterdata.py +++ b/request-management-api/request_api/resources/foiflowmasterdata.py @@ -186,7 +186,7 @@ def get(bcgovcode,specifictopersonalrequests=None, fetchmode = None): match fetchmode: case 'divisions': data = divisionstageservice().getpersonalspecificdivisionandstages(bcgovcode) - case 'sections': + case 'sections' | 'personaltag': data = divisionstageservice().getpersonalspecificprogramareasections(bcgovcode) case 'divisionsandsections': data = divisionstageservice().getpersonalspecificdivisionsandsections(bcgovcode) From 647f27d5b5a6d3b4284d5851057b2f9f0915890e Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Tue, 14 May 2024 22:44:03 -0700 Subject: [PATCH 14/83] fix migration conflicts --- .../migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py b/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py index 185ef612c..92b7887fc 100644 --- a/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py +++ b/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py @@ -1,7 +1,7 @@ """CFD Personal Tags Revision ID: d1d4f6cdfd68 -Revises: 6646acda32fe +Revises: 10aa6d69aeed Create Date: 2024-04-15 22:36:29.501289 """ @@ -11,7 +11,7 @@ # revision identifiers, used by Alembic. revision = 'd1d4f6cdfd68' -down_revision = '6646acda32fe' +down_revision = '10aa6d69aeed' branch_labels = None depends_on = None From 6192a08865ee40166e0bf1cb9bfb04573a9ee715 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Fri, 17 May 2024 13:30:53 -0700 Subject: [PATCH 15/83] handle the case that type is an empty string --- .../request_api/models/ProgramAreaDivisions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/request-management-api/request_api/models/ProgramAreaDivisions.py b/request-management-api/request_api/models/ProgramAreaDivisions.py index 120144740..038959839 100644 --- a/request-management-api/request_api/models/ProgramAreaDivisions.py +++ b/request-management-api/request_api/models/ProgramAreaDivisions.py @@ -49,7 +49,7 @@ def getallprogramareatags(cls,programareaid): @classmethod def getpersonalspecificprogramareadivisions(cls,programareaid): division_schema = ProgramAreaDivisionSchema(many=True) - query = db.session.query(ProgramAreaDivision).filter(ProgramAreaDivision.programareaid==programareaid,ProgramAreaDivision.isactive==True,ProgramAreaDivision.issection==False,ProgramAreaDivision.specifictopersonalrequests==True,ProgramAreaDivision.type.is_(None)).order_by(ProgramAreaDivision.name.asc()) + query = db.session.query(ProgramAreaDivision).filter(ProgramAreaDivision.programareaid==programareaid,ProgramAreaDivision.isactive==True,ProgramAreaDivision.issection==False,ProgramAreaDivision.specifictopersonalrequests==True,or_(ProgramAreaDivision.type.is_(None), ProgramAreaDivision.type=="")).order_by(ProgramAreaDivision.name.asc()) return division_schema.dump(query) @classmethod From 3b04a38070295c88976d71e30797aefdaf237536 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Tue, 21 May 2024 14:43:49 -0700 Subject: [PATCH 16/83] bug fix missing comparing trackingid --- .../src/components/FOI/customComponents/Records/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 7afa89a63..9927ccff0 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -1806,7 +1806,9 @@ export const RecordsLog = ({ const comparePersonalAttributes = (a, b) => { return a?.person === b?.person && a?.volume === b?.volume - && a?.filetype === b?.filetype && a?.personaltag === b?.personaltag; + && a?.filetype === b?.filetype + && a?.personaltag === b?.personaltag + && a?.trackingid === b?.trackingid; }; const updatePersonalAttributes = (_all = false) => { From 0bb689da7c721bbc614345ac7b24c8e6d2e6370f Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Tue, 21 May 2024 17:16:46 -0700 Subject: [PATCH 17/83] bug fix, for ministry to edit division for cfd personal record --- .../src/components/FOI/customComponents/Records/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 9927ccff0..5a459b06d 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -2479,7 +2479,7 @@ export const RecordsLog = ({
- {isMCFPersonal?( + {isMCFPersonal&&!isMinistryCoordinator?( Date: Wed, 29 May 2024 10:01:08 -0700 Subject: [PATCH 18/83] estimated page count for records --- .../FOI/customComponents/Records/index.js | 143 +++++++++++++++++- .../request_api/models/FOIMinistryRequests.py | 6 +- .../request_api/schemas/foirequestwrapper.py | 3 + .../foirequest/requestservicebuilder.py | 2 + .../foirequest/requestservicegetter.py | 2 + 5 files changed, 153 insertions(+), 3 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 5a459b06d..3e94874ed 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -35,6 +35,10 @@ import { checkForRecordsChange, editPersonalAttributes, } from "../../../../apiManager/services/FOI/foiRecordServices"; +import { + saveRequestDetails, + openRequestDetails +} from "../../../../apiManager/services/FOI/foiRequestServices"; import { StateTransitionCategories, AttachmentCategories, @@ -222,6 +226,7 @@ export const RecordsLog = ({ setRecordsUploading, recordsTabSelect, requestType, + handleSaveRequest }) => { const user = useSelector((state) => state.user.userDetail); const userGroups = user?.groups?.map((group) => group.slice(1)); @@ -264,6 +269,10 @@ export const RecordsLog = ({ (state) => state.foiRequests.isRecordsLoading ); + let requestDetails = useSelector( + (state) => state.foiRequests.foiRequestDetail + ); + const tagList = divisions .filter((d) => d.divisionname.toLowerCase() !== "communications") .map((division) => { @@ -275,6 +284,8 @@ export const RecordsLog = ({ const classes = useStyles(); const [records, setRecords] = useState(recordsObj?.records); + const [estimatedPageCount, setEstimatedPageCount] = useState(0); + const [estimatedTaggedPageCount, setEstimatedTaggedPageCount] = useState(0); const [totalUploadedRecordSize, setTotalUploadedRecordSize] = useState(0); const [isScanningTeamMember, setIsScanningTeamMember] = useState( isScanningTeam(userGroups) @@ -363,6 +374,11 @@ export const RecordsLog = ({ setFileTypeFilters(_fileTypeFilters.sort((a, b)=>a.sortorder-b.sortorder)); setVolumeFilters(_volumeFilters.sort((a, b)=>a.sortorder-b.sortorder)); }, [recordsObj, MCFPeople, MCFFiletypes, MCFVolumes]); + + useEffect(() => { + setEstimatedPageCount(requestDetails.estimatedpagecount) + setEstimatedTaggedPageCount(requestDetails.estimatedtaggedpagecount) + }, [requestDetails]); const conversionFormats = useSelector( (state) => state.foiRequests.conversionFormats @@ -1895,6 +1911,47 @@ export const RecordsLog = ({ ); }; + const saveEstimates = (e) => { + requestDetails.estimatedpagecount = estimatedPageCount + requestDetails.estimatedtaggedpagecount = estimatedTaggedPageCount + dispatch( + saveRequestDetails( + requestDetails, + -1, + requestId, + ministryId, + (err, res) => { + if (!err) { + toast.success("The request has been saved successfully.", { + position: "top-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + }); + handleSaveRequest(requestDetails.currentState, false, res.id); + } else { + toast.error( + "Temporarily unable to save your request. Please try again in a few minutes.", + { + position: "top-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + } + ); + handleSaveRequest(requestDetails.currentState, true, ""); + } + } + ) + ); + } + return (
{isAttachmentLoading ? ( @@ -1910,7 +1967,7 @@ export const RecordsLog = ({ alignItems="flex-start" spacing={1} > - +

{getRequestNumber()}

@@ -2027,16 +2084,98 @@ export const RecordsLog = ({ > -
+
Total Uploaded Size :{" "} {getReadableFileSize(totalUploadedRecordSize)}
+ + + + +
+ Estimated Physical Pages:{" "} +
+
+
+ + +
+ setEstimatedPageCount(e.target.value)} + > +
+
+
+ + +
Total Upload Limit :{" "} {getReadableFileSize(TOTAL_RECORDS_UPLOAD_LIMIT)}
+ + +
+ Estimated Pages After Tagging:{" "} + {/* */} +
+
+
+ + setEstimatedTaggedPageCount(e.target.value)} + > + + + {/* + */} Date: Thu, 30 May 2024 11:45:51 -0700 Subject: [PATCH 19/83] hide estimate page count if not mcf personal add migration script --- .../FOI/customComponents/Records/index.js | 74 ++++++++++--------- .../migrations/versions/9638286dc851_.py | 26 +++++++ 2 files changed, 65 insertions(+), 35 deletions(-) create mode 100644 request-management-api/migrations/versions/9638286dc851_.py diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 3e94874ed..3e3483d86 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -2092,14 +2092,14 @@ export const RecordsLog = ({ -
+ {isMCFPersonal &&
Estimated Physical Pages:{" "} -
+
}
- -
+ {isMCFPersonal && +
setEstimatedPageCount(e.target.value)} >
-
+ } @@ -2126,7 +2126,7 @@ export const RecordsLog = ({ -
+ {isMCFPersonal &&
Estimated Pages After Tagging:{" "} {/* */} -
+
}
- setEstimatedTaggedPageCount(e.target.value)} - > - + {isMCFPersonal && + <> + setEstimatedTaggedPageCount(e.target.value)} + > + + + } {/* */} diff --git a/request-management-api/migrations/versions/9638286dc851_.py b/request-management-api/migrations/versions/9638286dc851_.py new file mode 100644 index 000000000..5e554f502 --- /dev/null +++ b/request-management-api/migrations/versions/9638286dc851_.py @@ -0,0 +1,26 @@ +"""empty message + +Revision ID: 9638286dc851 +Revises: bd20ff56d1b1 +Create Date: 2022-12-19 15:54:32.701667 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '9638286dc851' +down_revision = 'bd20ff56d1b1' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column('FOIMinistryRequests', sa.Column('estimatedpagecount', sa.Integer, nullable=True, server_default=0)) + op.add_column('FOIMinistryRequests', sa.Column('estimatedtaggedpagecount', sa.Integer, nullable=True, server_default=0)) + + +def downgrade(): + op.drop_column('FOIMinistryRequests', 'estimatedpagecount') + op.drop_column('FOIMinistryRequests', 'estimatedtaggedpagecount') From 707c8b54279631c7976562fc564593b0bd3b4e2e Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 30 May 2024 13:35:49 -0700 Subject: [PATCH 20/83] fix migration script error --- request-management-api/migrations/versions/9638286dc851_.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/request-management-api/migrations/versions/9638286dc851_.py b/request-management-api/migrations/versions/9638286dc851_.py index 5e554f502..033edf599 100644 --- a/request-management-api/migrations/versions/9638286dc851_.py +++ b/request-management-api/migrations/versions/9638286dc851_.py @@ -17,8 +17,8 @@ def upgrade(): - op.add_column('FOIMinistryRequests', sa.Column('estimatedpagecount', sa.Integer, nullable=True, server_default=0)) - op.add_column('FOIMinistryRequests', sa.Column('estimatedtaggedpagecount', sa.Integer, nullable=True, server_default=0)) + op.add_column('FOIMinistryRequests', sa.Column('estimatedpagecount', sa.Integer, nullable=True)) + op.add_column('FOIMinistryRequests', sa.Column('estimatedtaggedpagecount', sa.Integer, nullable=True)) def downgrade(): From 628c4c765f20932c0e0dcc6b4b8f909bc669162a Mon Sep 17 00:00:00 2001 From: Aparna Date: Thu, 30 May 2024 17:16:41 -0700 Subject: [PATCH 21/83] Migration script update for new template document type --- .../migrations/versions/aa2691fa6c3c_.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 request-management-api/migrations/versions/aa2691fa6c3c_.py diff --git a/request-management-api/migrations/versions/aa2691fa6c3c_.py b/request-management-api/migrations/versions/aa2691fa6c3c_.py new file mode 100644 index 000000000..16e450afb --- /dev/null +++ b/request-management-api/migrations/versions/aa2691fa6c3c_.py @@ -0,0 +1,27 @@ +"""empty message + +Revision ID: aa2691fa6c3c +Revises: 9638286dc851 +Create Date: 2024-05-30 17:07:19.236044 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = 'aa2691fa6c3c' +down_revision = '9638286dc851' +branch_labels = None +depends_on = None + + +def upgrade(): + op.execute('insert into public."DocumentTypes"(document_type_name, description) VALUES (\'CFD_responsepackage_redaction_summary\', \'Word template for CFD response package redaction summary pdf\');commit;') + + op.execute('insert into public."DocumentTemplates"(extension, document_type_id) VALUES (\'docx\', (select document_type_id from public."DocumentTypes" where document_type_name=\'CFD_responsepackage_redaction_summary\'));commit;') + +def downgrade(): + op.execute('delete from public."DocumentTypes" where document_type_name = \'CFD_responsepackage_redaction_summary\'') + + op.execute('delete from public."DocumentTemplates" where document_type_id = (select document_type_id from public."DocumentTypes" where document_type_name=\'CFD_responsepackage_redaction_summary\')') \ No newline at end of file From daeb87736e73f0f16dcb606b5284feb038bbecbd Mon Sep 17 00:00:00 2001 From: JieunSon96 Date: Tue, 4 Jun 2024 14:09:28 -0700 Subject: [PATCH 22/83] fixed selecting distinct queries based on id --- .../request_api/models/FOIMinistryRequests.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/request-management-api/request_api/models/FOIMinistryRequests.py b/request-management-api/request_api/models/FOIMinistryRequests.py index cb41b37c9..af3a27201 100644 --- a/request-management-api/request_api/models/FOIMinistryRequests.py +++ b/request-management-api/request_api/models/FOIMinistryRequests.py @@ -408,12 +408,12 @@ def getrequestssubquery(cls, groups, filterfields, keyword, additionalfilter, us #subquery for getting extension count subquery_extension_count = _session.query(FOIRequestExtension.foiministryrequest_id, func.count(distinct(FOIRequestExtension.foirequestextensionid)).filter(FOIRequestExtension.isactive == True).label('extensions')).group_by(FOIRequestExtension.foiministryrequest_id).subquery() - - #subquery for getting all, distinct oipcs for foiministry request + + #subquery for selecting distinct records based on foiministryrequest_id, grouping by foiministryrequestversion_id subquery_with_oipc_sql = """ - SELECT distinct on (foiministryrequest_id) foiministryrequest_id, foiministryrequestversion_id, outcomeid - FROM "FOIRequestOIPC" fo - order by foiministryrequest_id, foiministryrequestversion_id desc + SELECT distinct on (foiministryrequest_id) foiministryrequest_id, foiministryrequestversion_id, + CASE WHEN COUNT(outcomeid) = COUNT(*) THEN MAX(outcomeid) ELSE NULL END AS outcomeid FROM "FOIRequestOIPC" fo GROUP BY foiministryrequest_id, foiministryrequestversion_id + ORDER BY foiministryrequest_id, foiministryrequestversion_id DESC """ subquery_with_oipc = text(subquery_with_oipc_sql).columns(FOIRequestOIPC.foiministryrequest_id, FOIRequestOIPC.foiministryrequestversion_id, FOIRequestOIPC.outcomeid).alias("oipcnoneoutcomes") joincondition_oipc = [ @@ -662,7 +662,7 @@ def getrequestssubquery(cls, groups, filterfields, keyword, additionalfilter, us ), isouter=True ).filter(or_(FOIMinistryRequest.requeststatuslabel != StateName.closed.name, - and_(FOIMinistryRequest.isoipcreview == True, FOIMinistryRequest.requeststatusid == 3))) + and_(FOIMinistryRequest.isoipcreview == True, FOIMinistryRequest.requeststatusid == 3,subquery_with_oipc.c.outcomeid == None))) From 8664d637941e809084a17cbf5f1c911ce9760b74 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Tue, 4 Jun 2024 15:08:25 -0700 Subject: [PATCH 23/83] hide "Update Divisions" button for iao - cfd personal --- .../src/components/FOI/customComponents/Records/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 5a459b06d..f16cdf28d 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -2255,6 +2255,7 @@ export const RecordsLog = ({ + {(!isMCFPersonal || (isMCFPersonal && isMinistryCoordinator)) && ( + )} Delete
}> +
: null + } {(isMinistryCoordinator == false && records?.length > 0 && From e5c4f00dd07660dbfeba50c31d4d9fe4cca49895 Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Tue, 9 Jul 2024 14:45:22 -0700 Subject: [PATCH 33/83] Added migration script, and adjust FE code to follow new logic. WIP IAO Users --- .../components/FOI/FOIRequest/FOIRequest.js | 36 +++++++++++-------- .../migrations/versions/5fdd5df3d642_.py | 24 +++++++++++++ .../request_api/models/FOIMinistryRequests.py | 1 + 3 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 request-management-api/migrations/versions/5fdd5df3d642_.py diff --git a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js index c9e29a68b..95f3ac238 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js @@ -273,22 +273,10 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { const {oipcData, addOIPC, removeOIPC, updateOIPC, isOIPCReview, setIsOIPCReview, removeAllOIPCs} = useOIPCHook(); const [oipcDataInitial, setOipcDataInitial] = useState(oipcData); const [lockRecordsTab, setLockRecordsTab] = useState(false); - - //Update disableInput + lockRecords when requestState changes + + //Update disableInput when requestState changes useEffect(() => { - const updateRecordsTabAccess = () => { - return ( - requestState === StateEnum.recordsreadyforreview || - requestState === StateEnum.review || - requestState === StateEnum.consult || - requestState === StateEnum.peerreview || - requestState === StateEnum.signoff || - requestState === StateEnum.response || - requestState === StateEnum.closed - ); - } setDisableInput(requestState?.toLowerCase() === StateEnum.closed.name.toLowerCase() && !isOIPCReview); - setLockRecordsTab(updateRecordsTabAccess()); }, [requestState, isOIPCReview]) useEffect(() => { @@ -1045,6 +1033,25 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { requestDetails?.requestType === FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_GENERAL) } + const updateRecordsTabAccess = () => { + if(requestDetails.lockRecords === null) { + return ( + requestState === StateEnum.recordsreadyforreview.name || + requestState === StateEnum.review.name || + requestState === StateEnum.consult.name || + requestState === StateEnum.peerreview.name || + requestState === StateEnum.signoff.name || + requestState === StateEnum.response.name || + requestState === StateEnum.closed.name + ); + } + } + + const handleLockRecords = () => { + setLockRecordsTab(!lockRecordsTab); + console.log("API CALL TO LOCK RECORDS ENDPOINT") // OR JUST REUQEST UPDATE END POINT AND NO NEED FOR NEW ONE? + } + return (!isLoading && requestDetails && Object.keys(requestDetails).length !== 0) || @@ -1653,6 +1660,7 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { recordsTabSelect={tabLinksStatuses.Records.active} requestType={requestDetails?.requestType} lockRecords={lockRecordsTab} + handleLockRecords={handleLockRecords} /> )} diff --git a/request-management-api/migrations/versions/5fdd5df3d642_.py b/request-management-api/migrations/versions/5fdd5df3d642_.py new file mode 100644 index 000000000..e943559dc --- /dev/null +++ b/request-management-api/migrations/versions/5fdd5df3d642_.py @@ -0,0 +1,24 @@ +"""ADD LOCKRECORDS Column to FOIMinistryRequest + +Revision ID: 5fdd5df3d642 +Revises: e698b39da6bd +Create Date: 2024-07-09 13:18:22.022002 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +# revision identifiers, used by Alembic. +revision = '5fdd5df3d642' +down_revision = 'e698b39da6bd' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column('FOIMinistryRequests', sa.Column('lockrecords', sa.Boolean(), nullable=True)) + + +def downgrade(): + op.drop_column('FOIMinistryRequests', 'lockrecords') diff --git a/request-management-api/request_api/models/FOIMinistryRequests.py b/request-management-api/request_api/models/FOIMinistryRequests.py index b31d1fc23..9697668a2 100644 --- a/request-management-api/request_api/models/FOIMinistryRequests.py +++ b/request-management-api/request_api/models/FOIMinistryRequests.py @@ -72,6 +72,7 @@ class FOIMinistryRequest(db.Model): identityverified = db.Column(JSON, unique=False, nullable=True) ministrysignoffapproval = db.Column(JSON, unique=False, nullable=True) requeststatuslabel = db.Column(db.String(50), nullable=False) + lockrecords = db.Column(db.Boolean, nullable=True) #ForeignKey References From 8a5a087e4cd488938cd4dd1abedf87c457678637 Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Tue, 9 Jul 2024 15:29:13 -0700 Subject: [PATCH 34/83] Backend work completed to get lockrecords value from DB --- .../request_api/models/FOIMinistryRequests.py | 2 +- .../request_api/services/foirequest/requestservicegetter.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/request-management-api/request_api/models/FOIMinistryRequests.py b/request-management-api/request_api/models/FOIMinistryRequests.py index 9697668a2..bae906e4a 100644 --- a/request-management-api/request_api/models/FOIMinistryRequests.py +++ b/request-management-api/request_api/models/FOIMinistryRequests.py @@ -1588,5 +1588,5 @@ class Meta: 'foirequest.receivedmodeid','requeststatus.requeststatusid','requeststatuslabel','requeststatus.name','programarea.bcgovcode', 'programarea.name','foirequest_id','foirequestversion_id','created_at','updated_at','createdby','assignedministryperson', 'assignedministrygroup','cfrduedate','closedate','closereasonid','closereason.name', - 'assignee.firstname','assignee.lastname','ministryassignee.firstname','ministryassignee.lastname', 'axisrequestid', 'axissyncdate', 'axispagecount', 'axislanpagecount', 'linkedrequests', 'ministrysignoffapproval', 'identityverified','originalldd','isoipcreview', 'recordspagecount') + 'assignee.firstname','assignee.lastname','ministryassignee.firstname','ministryassignee.lastname', 'axisrequestid', 'axissyncdate', 'axispagecount', 'axislanpagecount', 'linkedrequests', 'ministrysignoffapproval', 'identityverified','originalldd','isoipcreview', 'recordspagecount', 'lockrecords') diff --git a/request-management-api/request_api/services/foirequest/requestservicegetter.py b/request-management-api/request_api/services/foirequest/requestservicegetter.py index acfaa297f..5e934572e 100644 --- a/request-management-api/request_api/services/foirequest/requestservicegetter.py +++ b/request-management-api/request_api/services/foirequest/requestservicegetter.py @@ -191,6 +191,7 @@ def __preparebaseinfo(self,request,foiministryrequestid,requestministry,requestm 'isofflinepayment': FOIMinistryRequest.getofflinepaymentflag(foiministryrequestid), 'linkedRequests' : linkedministryrequests, 'identityVerified':requestministry['identityverified'], + 'lockrecords': requestministry['lockrecords'], } if requestministry['cfrduedate'] is not None: From 669c6a01c4941e36f87a270dbc0c76d26838bf5c Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Tue, 9 Jul 2024 16:22:38 -0700 Subject: [PATCH 35/83] adjusted section end point to consume lock records + adjusted FE initial state load to handle lock records --- .../components/FOI/FOIRequest/FOIRequest.js | 29 ++++++++++--------- .../request_api/schemas/foirequestwrapper.py | 1 + 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js index 95f3ac238..875934042 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js @@ -296,6 +296,21 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { } else if (window.location.href.indexOf("records") > -1) { tabclick("Records"); } + //Adjust lockRecords tab state if there is no BE/DB data recorded + const updateRecordsTabAccess = () => { + if(requestDetails.lockrecords === null) { + return ( + requestState === StateEnum.recordsreadyforreview.name || + requestState === StateEnum.review.name || + requestState === StateEnum.consult.name || + requestState === StateEnum.peerreview.name || + requestState === StateEnum.signoff.name || + requestState === StateEnum.response.name || + requestState === StateEnum.closed.name + ); + } + } + setLockRecordsTab(updateRecordsTabAccess()) }, []); useEffect(async () => { @@ -1033,20 +1048,6 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { requestDetails?.requestType === FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_GENERAL) } - const updateRecordsTabAccess = () => { - if(requestDetails.lockRecords === null) { - return ( - requestState === StateEnum.recordsreadyforreview.name || - requestState === StateEnum.review.name || - requestState === StateEnum.consult.name || - requestState === StateEnum.peerreview.name || - requestState === StateEnum.signoff.name || - requestState === StateEnum.response.name || - requestState === StateEnum.closed.name - ); - } - } - const handleLockRecords = () => { setLockRecordsTab(!lockRecordsTab); console.log("API CALL TO LOCK RECORDS ENDPOINT") // OR JUST REUQEST UPDATE END POINT AND NO NEED FOR NEW ONE? diff --git a/request-management-api/request_api/schemas/foirequestwrapper.py b/request-management-api/request_api/schemas/foirequestwrapper.py index a5d528bde..7ebc88f32 100644 --- a/request-management-api/request_api/schemas/foirequestwrapper.py +++ b/request-management-api/request_api/schemas/foirequestwrapper.py @@ -155,6 +155,7 @@ class Meta: # pylint: disable=too-few-public-methods identityVerified = fields.Str(data_key="identityVerified",allow_none=True) oipcdetails = fields.Nested(FOIMinistryRequestOIPCSchema, many=True,allow_none=True) + lockrecords = fields.Bool(data_key="isofflinepayment") recordspagecount = fields.Int(data_key="recordspagecount",allow_none=True) axislanpagecount = fields.Int(data_key="axislanpagecount",allow_none=True) From 94838571994eded9528efe7e1079d0c0c26b5b4a Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Tue, 9 Jul 2024 17:42:00 -0700 Subject: [PATCH 36/83] resolve migration conflict --- .../migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py b/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py index 92b7887fc..ea55ebf15 100644 --- a/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py +++ b/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py @@ -11,7 +11,7 @@ # revision identifiers, used by Alembic. revision = 'd1d4f6cdfd68' -down_revision = '10aa6d69aeed' +down_revision = 'e698b39da6bd' branch_labels = None depends_on = None From ea57ee383b696653b5060a3886d8e919e283a2bc Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Wed, 10 Jul 2024 17:13:03 -0700 Subject: [PATCH 37/83] FE and backend logic for state changing for IAO users connected. WIP Debugging + button / manual lock feautre + ministry user work --- .../FOI/FOIRequest/BottomButtonGroup/index.js | 11 ++++++ .../components/FOI/FOIRequest/FOIRequest.js | 38 +++++++++++-------- .../request_api/schemas/foirequestwrapper.py | 2 +- 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js index dd2344981..38221aab5 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js @@ -77,6 +77,7 @@ const BottomButtonGroup = React.memo( axisMessage, attachmentsArray, oipcData, + validLockRecordsState, }) => { /** * Bottom Button Group of Review request Page @@ -120,6 +121,14 @@ const BottomButtonGroup = React.memo( setIsAddRequest(false); } + //Logic to change lock records to null (and have FE useEffect in FOIRequest.js/MinistryView.js logic takeover) if request goes back to open, cfr, harms, fee estimate, dedup + if (!validLockRecordsState(currentSelectedStatus)) { + console.log("GOT YA") + saveRequestObject.lockrecords = true; + } + + console.log("SAVE PBJ", saveRequestObject) + //add oipc Data to save request object and sync/validate isoipcreview attribute if (requestState.toLowerCase() !== StateEnum.intakeinprogress.name.toLowerCase() && requestState.toLowerCase() !== StateEnum.unopened.name.toLowerCase()) { saveRequestObject.oipcdetails = oipcData ? oipcData : []; @@ -253,6 +262,7 @@ const BottomButtonGroup = React.memo( }; const handleModal = (value) => { + console.log("LIQUIDD") setOpenModal(false); if (!value) { handleOpenRequest("", "", true); @@ -389,6 +399,7 @@ const BottomButtonGroup = React.memo( const handleSaveModal = (value, fileInfoList, files) => { setsaveModal(false); setFileCount(files?.length); + console.log("SNAKEE") if (!value) { handleSaveRequest(requestState, true, ""); diff --git a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js index 875934042..a241fc4b5 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js @@ -296,21 +296,6 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { } else if (window.location.href.indexOf("records") > -1) { tabclick("Records"); } - //Adjust lockRecords tab state if there is no BE/DB data recorded - const updateRecordsTabAccess = () => { - if(requestDetails.lockrecords === null) { - return ( - requestState === StateEnum.recordsreadyforreview.name || - requestState === StateEnum.review.name || - requestState === StateEnum.consult.name || - requestState === StateEnum.peerreview.name || - requestState === StateEnum.signoff.name || - requestState === StateEnum.response.name || - requestState === StateEnum.closed.name - ); - } - } - setLockRecordsTab(updateRecordsTabAccess()) }, []); useEffect(async () => { @@ -357,6 +342,20 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { if (bcgovcode) dispatch(fetchFOIMinistryAssignedToList(bcgovcode)); }, [requestId, ministryId, comment, attachments]); + const validLockRecordsState = (currentState) => { + console.log("MIQU", requestDetails) + console.log("BOOM", currentState) + return ( + currentState === StateEnum.recordsreadyforreview.name || + currentState === StateEnum.review.name || + currentState === StateEnum.consult.name || + currentState === StateEnum.peerreview.name || + currentState === StateEnum.signoff.name || + currentState === StateEnum.response.name || + currentState === StateEnum.closed.name + ); + } + useEffect(() => { const requestDetailsValue = requestDetails; setSaveRequestObject(requestDetailsValue); @@ -392,6 +391,14 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { } else { setIsOIPCReview(false); } + + //Adjust lockRecords value based on requestState if there is no manual user lockrecords value present in requestDetails from DB + const updateRecordsTabAccess = () => { + if(requestDetails.lockrecords === null) { + return validLockRecordsState(requestDetails.currentState) + } + } + setLockRecordsTab(updateRecordsTabAccess()); }, [requestDetails]); //useEffect to manage isoipcreview attribute for requestdetails state @@ -1416,6 +1423,7 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { axisMessage={axisMessage} attachmentsArray={requestAttachments} oipcData={oipcData} + validLockRecordsState={validLockRecordsState} /> diff --git a/request-management-api/request_api/schemas/foirequestwrapper.py b/request-management-api/request_api/schemas/foirequestwrapper.py index 7ebc88f32..0d8925d1a 100644 --- a/request-management-api/request_api/schemas/foirequestwrapper.py +++ b/request-management-api/request_api/schemas/foirequestwrapper.py @@ -155,7 +155,7 @@ class Meta: # pylint: disable=too-few-public-methods identityVerified = fields.Str(data_key="identityVerified",allow_none=True) oipcdetails = fields.Nested(FOIMinistryRequestOIPCSchema, many=True,allow_none=True) - lockrecords = fields.Bool(data_key="isofflinepayment") + lockrecords = fields.Bool(data_key="lockrecords", allow_none=True) recordspagecount = fields.Int(data_key="recordspagecount",allow_none=True) axislanpagecount = fields.Int(data_key="axislanpagecount",allow_none=True) From 94a9b0130cb05aa6407e9f0010ce37e2edc2d95e Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Thu, 11 Jul 2024 14:16:28 -0700 Subject: [PATCH 38/83] IAO Workflow for state lock records completed. WIP manual lock records for IAO --- .../src/components/FOI/FOIRequest/BottomButtonGroup/index.js | 5 +---- forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js | 4 +++- .../request_api/services/foirequest/requestservicebuilder.py | 1 + 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js index 38221aab5..5d1f0d991 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js @@ -123,12 +123,9 @@ const BottomButtonGroup = React.memo( //Logic to change lock records to null (and have FE useEffect in FOIRequest.js/MinistryView.js logic takeover) if request goes back to open, cfr, harms, fee estimate, dedup if (!validLockRecordsState(currentSelectedStatus)) { - console.log("GOT YA") - saveRequestObject.lockrecords = true; + saveRequestObject.lockrecords = null; } - console.log("SAVE PBJ", saveRequestObject) - //add oipc Data to save request object and sync/validate isoipcreview attribute if (requestState.toLowerCase() !== StateEnum.intakeinprogress.name.toLowerCase() && requestState.toLowerCase() !== StateEnum.unopened.name.toLowerCase()) { saveRequestObject.oipcdetails = oipcData ? oipcData : []; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js index a241fc4b5..4c41534f8 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js @@ -395,7 +395,9 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { //Adjust lockRecords value based on requestState if there is no manual user lockrecords value present in requestDetails from DB const updateRecordsTabAccess = () => { if(requestDetails.lockrecords === null) { - return validLockRecordsState(requestDetails.currentState) + return validLockRecordsState(requestDetails.currentState); + } else { + return requestDetails.lockrecords; } } setLockRecordsTab(updateRecordsTabAccess()); diff --git a/request-management-api/request_api/services/foirequest/requestservicebuilder.py b/request-management-api/request_api/services/foirequest/requestservicebuilder.py index 31dca5387..2da57d7bd 100644 --- a/request-management-api/request_api/services/foirequest/requestservicebuilder.py +++ b/request-management-api/request_api/services/foirequest/requestservicebuilder.py @@ -54,6 +54,7 @@ def createministry(self, requestschema, ministry, activeversion, userid, filenum foiministryrequest.startdate = startdate foiministryrequest.createdby = userid requeststatuslabel = self.getpropertyvaluefromschema(requestschema, 'requeststatuslabel') + foiministryrequest.lockrecords = requestschema.get("lockrecords") if requeststatuslabel is not None: status = self.getstatusname(requeststatuslabel) if self.isNotBlankorNone(requestschema,"fromDate","main") == True: From 25ef0c80c6d44b6d61dc2e9a1697d8cccd8470b3 Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Thu, 11 Jul 2024 14:34:11 -0700 Subject: [PATCH 39/83] renamed lockrecords to userlockedrecords for DB/models --- .../components/FOI/FOIRequest/BottomButtonGroup/index.js | 2 +- forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js | 6 +++--- request-management-api/migrations/versions/5fdd5df3d642_.py | 6 +++--- .../request_api/models/FOIMinistryRequests.py | 4 ++-- .../request_api/schemas/foirequestwrapper.py | 2 +- .../services/foirequest/requestservicebuilder.py | 2 +- .../request_api/services/foirequest/requestservicegetter.py | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js index 5d1f0d991..edcd53002 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js @@ -123,7 +123,7 @@ const BottomButtonGroup = React.memo( //Logic to change lock records to null (and have FE useEffect in FOIRequest.js/MinistryView.js logic takeover) if request goes back to open, cfr, harms, fee estimate, dedup if (!validLockRecordsState(currentSelectedStatus)) { - saveRequestObject.lockrecords = null; + saveRequestObject.userlockedrecords = null; } //add oipc Data to save request object and sync/validate isoipcreview attribute diff --git a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js index 4c41534f8..562ad749e 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js @@ -392,12 +392,12 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { setIsOIPCReview(false); } - //Adjust lockRecords value based on requestState if there is no manual user lockrecords value present in requestDetails from DB + //Adjust lockRecords value based on requestState if there is no manual user lockedrecords value present in requestDetails from DB const updateRecordsTabAccess = () => { - if(requestDetails.lockrecords === null) { + if(requestDetails.userlockedrecords === null) { return validLockRecordsState(requestDetails.currentState); } else { - return requestDetails.lockrecords; + return requestDetails.userlockedrecords; } } setLockRecordsTab(updateRecordsTabAccess()); diff --git a/request-management-api/migrations/versions/5fdd5df3d642_.py b/request-management-api/migrations/versions/5fdd5df3d642_.py index e943559dc..86b517d77 100644 --- a/request-management-api/migrations/versions/5fdd5df3d642_.py +++ b/request-management-api/migrations/versions/5fdd5df3d642_.py @@ -1,4 +1,4 @@ -"""ADD LOCKRECORDS Column to FOIMinistryRequest +"""ADD userlockedrecords Column to FOIMinistryRequest Revision ID: 5fdd5df3d642 Revises: e698b39da6bd @@ -17,8 +17,8 @@ def upgrade(): - op.add_column('FOIMinistryRequests', sa.Column('lockrecords', sa.Boolean(), nullable=True)) + op.add_column('FOIMinistryRequests', sa.Column('userlockedrecords', sa.Boolean(), nullable=True)) def downgrade(): - op.drop_column('FOIMinistryRequests', 'lockrecords') + op.drop_column('FOIMinistryRequests', 'userlockedrecords') diff --git a/request-management-api/request_api/models/FOIMinistryRequests.py b/request-management-api/request_api/models/FOIMinistryRequests.py index bae906e4a..df9514bba 100644 --- a/request-management-api/request_api/models/FOIMinistryRequests.py +++ b/request-management-api/request_api/models/FOIMinistryRequests.py @@ -72,7 +72,7 @@ class FOIMinistryRequest(db.Model): identityverified = db.Column(JSON, unique=False, nullable=True) ministrysignoffapproval = db.Column(JSON, unique=False, nullable=True) requeststatuslabel = db.Column(db.String(50), nullable=False) - lockrecords = db.Column(db.Boolean, nullable=True) + userlockedrecords = db.Column(db.Boolean, nullable=True) #ForeignKey References @@ -1588,5 +1588,5 @@ class Meta: 'foirequest.receivedmodeid','requeststatus.requeststatusid','requeststatuslabel','requeststatus.name','programarea.bcgovcode', 'programarea.name','foirequest_id','foirequestversion_id','created_at','updated_at','createdby','assignedministryperson', 'assignedministrygroup','cfrduedate','closedate','closereasonid','closereason.name', - 'assignee.firstname','assignee.lastname','ministryassignee.firstname','ministryassignee.lastname', 'axisrequestid', 'axissyncdate', 'axispagecount', 'axislanpagecount', 'linkedrequests', 'ministrysignoffapproval', 'identityverified','originalldd','isoipcreview', 'recordspagecount', 'lockrecords') + 'assignee.firstname','assignee.lastname','ministryassignee.firstname','ministryassignee.lastname', 'axisrequestid', 'axissyncdate', 'axispagecount', 'axislanpagecount', 'linkedrequests', 'ministrysignoffapproval', 'identityverified','originalldd','isoipcreview', 'recordspagecount', 'userlockedrecords') diff --git a/request-management-api/request_api/schemas/foirequestwrapper.py b/request-management-api/request_api/schemas/foirequestwrapper.py index 0d8925d1a..71f23f005 100644 --- a/request-management-api/request_api/schemas/foirequestwrapper.py +++ b/request-management-api/request_api/schemas/foirequestwrapper.py @@ -155,7 +155,7 @@ class Meta: # pylint: disable=too-few-public-methods identityVerified = fields.Str(data_key="identityVerified",allow_none=True) oipcdetails = fields.Nested(FOIMinistryRequestOIPCSchema, many=True,allow_none=True) - lockrecords = fields.Bool(data_key="lockrecords", allow_none=True) + userlockedrecords = fields.Bool(data_key="userlockedrecords", allow_none=True) recordspagecount = fields.Int(data_key="recordspagecount",allow_none=True) axislanpagecount = fields.Int(data_key="axislanpagecount",allow_none=True) diff --git a/request-management-api/request_api/services/foirequest/requestservicebuilder.py b/request-management-api/request_api/services/foirequest/requestservicebuilder.py index 2da57d7bd..879f6ea61 100644 --- a/request-management-api/request_api/services/foirequest/requestservicebuilder.py +++ b/request-management-api/request_api/services/foirequest/requestservicebuilder.py @@ -54,7 +54,7 @@ def createministry(self, requestschema, ministry, activeversion, userid, filenum foiministryrequest.startdate = startdate foiministryrequest.createdby = userid requeststatuslabel = self.getpropertyvaluefromschema(requestschema, 'requeststatuslabel') - foiministryrequest.lockrecords = requestschema.get("lockrecords") + foiministryrequest.userlockedrecords = requestschema.get("userlockedrecords") if requeststatuslabel is not None: status = self.getstatusname(requeststatuslabel) if self.isNotBlankorNone(requestschema,"fromDate","main") == True: diff --git a/request-management-api/request_api/services/foirequest/requestservicegetter.py b/request-management-api/request_api/services/foirequest/requestservicegetter.py index 5e934572e..46cc84c9c 100644 --- a/request-management-api/request_api/services/foirequest/requestservicegetter.py +++ b/request-management-api/request_api/services/foirequest/requestservicegetter.py @@ -191,7 +191,7 @@ def __preparebaseinfo(self,request,foiministryrequestid,requestministry,requestm 'isofflinepayment': FOIMinistryRequest.getofflinepaymentflag(foiministryrequestid), 'linkedRequests' : linkedministryrequests, 'identityVerified':requestministry['identityverified'], - 'lockrecords': requestministry['lockrecords'], + 'userlockedrecords': requestministry['userlockedrecords'], } if requestministry['cfrduedate'] is not None: From 793f77c6eaa7ec5d01380801463296b21bc5189f Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Thu, 11 Jul 2024 16:12:27 -0700 Subject: [PATCH 40/83] controlling of lock records for IAO user completed. WIP ministry user --- .../services/FOI/foiRecordServices.js | 26 ++++++++++- .../components/FOI/FOIRequest/FOIRequest.js | 7 +-- .../FOI/customComponents/Records/index.js | 45 +++++++++++++++++++ .../request_api/resources/foirequest.py | 11 ++++- 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js b/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js index 8c2d48a61..1cdf61f42 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js @@ -620,4 +620,28 @@ export const fetchPDFStitchedRecordForOIPCRedlineReview = ( done(error); }); }; - } \ No newline at end of file + } + +export const updateUserLockedRecords = (data, requestId, ministryId, ...rest) => { + const done = fnDone(rest); + let apiUrl= replaceUrl(replaceUrl( + API.FOI_REQUEST_SECTION_API, + "", + ministryId),"",requestId + ); + return (dispatch) => { + httpPOSTRequest(`${apiUrl}/userlockedrecords`, data) + .then((res) => { + if (res.data) { + done(null, res.data); + } else { + dispatch(serviceActionError(res)); + throw new Error(`Error while updating records lock status for the (request# ${requestId}, ministry# ${ministryId})`); + } + }) + .catch((error) => { + dispatch(serviceActionError(error)); + done(error); + }); + }; +} \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js index 562ad749e..19dda3ec4 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js @@ -1057,11 +1057,6 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { requestDetails?.requestType === FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_GENERAL) } - const handleLockRecords = () => { - setLockRecordsTab(!lockRecordsTab); - console.log("API CALL TO LOCK RECORDS ENDPOINT") // OR JUST REUQEST UPDATE END POINT AND NO NEED FOR NEW ONE? - } - return (!isLoading && requestDetails && Object.keys(requestDetails).length !== 0) || @@ -1671,7 +1666,7 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { recordsTabSelect={tabLinksStatuses.Records.active} requestType={requestDetails?.requestType} lockRecords={lockRecordsTab} - handleLockRecords={handleLockRecords} + setLockRecordsTab={setLockRecordsTab} /> )} diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 67a4095a6..46b40bc65 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -33,6 +33,7 @@ import { fetchPDFStitchedRecordForOIPCRedline, fetchPDFStitchedRecordForOIPCRedlineReview, checkForRecordsChange, + updateUserLockedRecords, } from "../../../../apiManager/services/FOI/foiRecordServices"; import { StateTransitionCategories, @@ -220,6 +221,7 @@ export const RecordsLog = ({ recordsTabSelect, requestType, lockRecords, + setLockRecordsTab, }) => { const user = useSelector((state) => state.user.userDetail); const userGroups = user?.groups?.map((group) => group.slice(1)); @@ -577,6 +579,7 @@ export const RecordsLog = ({ saveDocument(value, fileInfoList, files); } }; + console.log("LOCK", lockRecords) const saveDocument = (value, fileInfoList, files) => { if (value) { @@ -1742,6 +1745,47 @@ export const RecordsLog = ({ ); }; + const handleLockRecords = () => { + setLockRecordsTab(!lockRecords); + const toastID = toast.loading("Updating records lock status for request..."); + const data = {userlockedrecords: !lockRecords}; + dispatch( + updateUserLockedRecords( + data, + requestId, + ministryId, + (err, _res) => { + if(!err) { + toast.update(toastID, { + type: "success", + render: "Request details have been saved successfully.", + position: "top-right", + isLoading: false, + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + }); + } else { + toast.error( + "Temporarily unable to update records lock status for request. Please try again in a few minutes.", + { + position: "top-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + } + ); + } + }) + ) + } + return (
{isAttachmentLoading ? ( @@ -1766,6 +1810,7 @@ export const RecordsLog = ({ + + + + : null } @@ -2157,7 +2164,7 @@ export const RecordsLog = ({ } style={ records.filter((record) => record.attachments?.length > 0) - .length === 0 + .length === 0 || lockRecords ? { pointerEvents: "none" } : {} } @@ -2176,6 +2183,7 @@ export const RecordsLog = ({
To update divisions:{" "}
    +
  • Records log must be unlocked
  • at least one record must be selected
  • all records selected must be tagged to the same @@ -2229,7 +2237,7 @@ export const RecordsLog = ({ // title="Delete" disabled={lockRecords || !checkIsAnySelected()} style={ - !checkIsAnySelected() ? { pointerEvents: "none" } : {} + lockRecords || !checkIsAnySelected() ? { pointerEvents: "none" } : {} } > @@ -2974,6 +2982,7 @@ const AttachmentPopup = React.memo( const DeleteMenu = () => { return ( { handleDelete(); From 300faf77486fe68354afc05f0ac85a0edf56a69e Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Fri, 12 Jul 2024 16:52:59 -0700 Subject: [PATCH 46/83] renamed column for manual records lock in BE + adjusted styling for ministry view records tab --- .../services/FOI/foiRecordServices.js | 2 +- .../FOI/FOIRequest/BottomButtonGroup/index.js | 4 +- .../components/FOI/FOIRequest/FOIRequest.js | 4 +- .../MinistryReview/BottomButtonGroup.js | 4 +- .../MinistryReview/MinistryReview.js | 4 +- .../FOI/customComponents/Records/index.js | 65 ++++++++++--------- .../migrations/versions/5fdd5df3d642_.py | 6 +- .../request_api/models/FOIMinistryRequests.py | 4 +- .../request_api/resources/foirequest.py | 7 +- .../request_api/schemas/foirequestwrapper.py | 2 +- .../foirequest/requestservicebuilder.py | 2 +- .../foirequest/requestservicegetter.py | 2 +- 12 files changed, 54 insertions(+), 52 deletions(-) diff --git a/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js b/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js index 1cdf61f42..47e7f50d3 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js @@ -630,7 +630,7 @@ export const updateUserLockedRecords = (data, requestId, ministryId, ...rest) => ministryId),"",requestId ); return (dispatch) => { - httpPOSTRequest(`${apiUrl}/userlockedrecords`, data) + httpPOSTRequest(`${apiUrl}/userrecordslockstatus`, data) .then((res) => { if (res.data) { done(null, res.data); diff --git a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js index cfc472f3e..a66d4b21d 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js @@ -122,8 +122,8 @@ const BottomButtonGroup = React.memo( } //Logic to reset user lock records status to null (and have FE useEffect in FOIRequest.js/MinistryView.js logic takeover) if request is in unlocked request states - if (saveRequestObject.userlockedrecords !== null && !validLockRecordsState(currentSelectedStatus)) { - saveRequestObject.userlockedrecords = null; + if (saveRequestObject.userrecordslockstatus !== null && !validLockRecordsState(currentSelectedStatus)) { + saveRequestObject.userrecordslockstatus = null; } //add oipc Data to save request object and sync/validate isoipcreview attribute diff --git a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js index a9ee8b437..947dcdcaa 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js @@ -394,10 +394,10 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { //Adjust lockRecords value based on requestState if there is no manual user lockedrecords value present in requestDetails from DB const updateRecordsTabAccess = () => { - if(requestDetails.userlockedrecords === null) { + if(requestDetails.userrecordslockstatus === null) { return validLockRecordsState(requestDetails.currentState); } else { - return requestDetails.userlockedrecords; + return requestDetails.userrecordslockstatus; } } setLockRecordsTab(updateRecordsTabAccess()); diff --git a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/BottomButtonGroup.js b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/BottomButtonGroup.js index c215565a0..362945cb0 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/BottomButtonGroup.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/BottomButtonGroup.js @@ -111,8 +111,8 @@ const BottomButtonGroup = React.memo( const saveMinistryRequest = async () => { //Logic to reset user lock records status to null (and have FE useEffect in FOIRequest.js/MinistryView.js logic takeover) if request is in unlocked request states - if (saveMinistryRequestObject.userlockedrecords !== null && !validLockRecordsState(currentSelectedStatus)) { - saveMinistryRequestObject.userlockedrecords = null; + if (saveMinistryRequestObject.userrecordslockstatus !== null && !validLockRecordsState(currentSelectedStatus)) { + saveMinistryRequestObject.userrecordslockstatus = null; } dispatch( saveMinistryRequestDetails( diff --git a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js index cb22cc392..0e3f867d8 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js @@ -293,10 +293,10 @@ const MinistryReview = React.memo(({ userDetail }) => { //Adjust lockRecords value based on requestState if there is no manual user lockedrecords value present in requestDetails from DB const updateRecordsTabAccess = () => { - if(requestDetails.userlockedrecords === null) { + if(requestDetails.userrecordslockstatus === null) { return validLockRecordsState(requestDetails.currentState); } else { - return requestDetails.userlockedrecords; + return requestDetails.userrecordslockstatus; } } setLockRecordsTab(updateRecordsTabAccess()); diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index cc4ee1133..9c50344a7 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -580,7 +580,6 @@ export const RecordsLog = ({ saveDocument(value, fileInfoList, files); } }; - console.log("LOCK", lockRecords) const saveDocument = (value, fileInfoList, files) => { if (value) { @@ -1749,7 +1748,7 @@ export const RecordsLog = ({ const handleLockRecords = () => { setLockRecordsTab(!lockRecords); const toastID = toast.loading("Updating records lock status for request..."); - const data = {userlockedrecords: !lockRecords}; + const data = {userrecordslockstatus: !lockRecords}; dispatch( updateUserLockedRecords( data, @@ -1808,15 +1807,20 @@ export const RecordsLog = ({ {validLockRecordsState() ? - + - + {isMinistryCoordinator ? +

    + {lockRecords ? "Records Locked" : "Records Unlocked"} +

    + : - - ) - )} -
    + Redact Records + + +
    + ) + )} {hasDocumentsToDownload && ( Date: Fri, 12 Jul 2024 17:03:26 -0700 Subject: [PATCH 47/83] styling of records header grid --- .../src/components/FOI/customComponents/Records/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 9c50344a7..015e7e634 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -1834,7 +1834,7 @@ export const RecordsLog = ({ } - : null + : } {(isMinistryCoordinator == false && records?.length > 0 && From 1c74912d6901aff0b7073c0c6827d30015a17292 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Mon, 15 Jul 2024 17:06:10 -0700 Subject: [PATCH 48/83] Fix observation #2 --- .../FileUpload/FileUploadForMCFPersonal.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js index df8335b3a..103f5ecb8 100644 --- a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js +++ b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js @@ -262,8 +262,8 @@ const FileUploadForMCFPersonal = ({ } React.useEffect(() => { - setIsPersonSelected(Object.keys(person).length !== 0); - },[person]) + setIsPersonSelected(Object.keys(person).length !== 0 && Object.keys(fileType).length !== 0); + },[person, fileType]) React.useEffect(() => { setAdditionalTagList(searchSections(otherTagList, searchValue, tagValue)); @@ -405,10 +405,10 @@ const FileUploadForMCFPersonal = ({ size="small" onClick={()=>{handleVolumeChange(v)}} clicked={volume?.name === v.name} - disabled={!isPersonSelected} + // disabled={!isPersonSelected} /> )} - {!showAllVolumes && isPersonSelected && ({handleFileTypeChange(f)}} clicked={fileType?.name === f.name} - disabled={!isPersonSelected} + // disabled={!isPersonSelected} /> )}
@@ -495,7 +495,7 @@ const FileUploadForMCFPersonal = ({ } fullWidth - disabled={!isPersonSelected} + // disabled={!isPersonSelected} />
From bfaf7b1d01f784b5f8dc2071cf5a420be4f91532 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Mon, 15 Jul 2024 17:21:26 -0700 Subject: [PATCH 49/83] fix observation 3 --- .../FOI/customComponents/Attachments/AttachmentModal.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js index 633eec1a2..a1e6c551a 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js +++ b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js @@ -528,7 +528,11 @@ export default function AttachmentModal({ } else if (files.length === 0 && existingDocuments.length === 0) { return true; } else if (modalFor === "add") { - return tagValue === ""; + if(requestType == FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_PERSONAL && bcgovcode == "MCF") { + return person === "" || fileType === "" || trackingID === ""; + } else { + return tagValue === ""; + } } else if (modalFor === "replace" || modalFor === "replaceattachment") { return false; } From 183a3a6d1c027b57ecc2aa9ad2590e2d271c9639 Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Tue, 16 Jul 2024 12:18:21 -0700 Subject: [PATCH 50/83] Added logic to use error message from computing service response retrieved from docreviwer --- .../request_api/services/records/recordservicegetter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/request-management-api/request_api/services/records/recordservicegetter.py b/request-management-api/request_api/services/records/recordservicegetter.py index c6997efdd..04e48639b 100644 --- a/request-management-api/request_api/services/records/recordservicegetter.py +++ b/request-management-api/request_api/services/records/recordservicegetter.py @@ -237,7 +237,7 @@ def __getcomputingerror(self, computingresponse): if computingresponse["conversionstatus"] == "error": return "conversion" elif computingresponse["deduplicationstatus"] == "error": - return "deduplication" + return "deduplication. " + computingresponse["message"] if computingresponse["message"] != '' else "deduplication" return None def __getcomputingsummary(self, computingresponse): From a1cd3c60a3a27f67f04ee32143b696bf88db4a4b Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Tue, 16 Jul 2024 14:57:20 -0700 Subject: [PATCH 51/83] Added error handling messge for attachment as well --- .../src/components/FOI/customComponents/Records/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index c43fac76a..8b9527393 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -2705,9 +2705,9 @@ const Attachment = React.memo( textOverflow: "ellipsis", whiteSpace: "nowrap", }} - title={`Attachment of ${record.attachmentof}`} + title={record.failed ? `Error during ${record.failed}` : `Attachment of ${record.attachmentof}`} > - Attachment of {record.attachmentof} + {record.failed ? `Error during ${record.failed}` : `Attachment of ${record.attachmentof}`} ) : record.isredactionready ? ( Ready for Redaction From 04a8ccff4c1aee2cd0943070d5cef6197dda9db9 Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Tue, 16 Jul 2024 15:27:51 -0700 Subject: [PATCH 52/83] small bug fix for string interpolation in python BE --- .../request_api/services/records/recordservicegetter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/request-management-api/request_api/services/records/recordservicegetter.py b/request-management-api/request_api/services/records/recordservicegetter.py index 04e48639b..5fde31bf1 100644 --- a/request-management-api/request_api/services/records/recordservicegetter.py +++ b/request-management-api/request_api/services/records/recordservicegetter.py @@ -237,7 +237,7 @@ def __getcomputingerror(self, computingresponse): if computingresponse["conversionstatus"] == "error": return "conversion" elif computingresponse["deduplicationstatus"] == "error": - return "deduplication. " + computingresponse["message"] if computingresponse["message"] != '' else "deduplication" + return "deduplication. " + computingresponse["message"] if computingresponse["message"] not in ['', None] else "deduplication" return None def __getcomputingsummary(self, computingresponse): From 200f043fd5326a32d44c97312138432fde3db32f Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Tue, 16 Jul 2024 18:40:28 -0700 Subject: [PATCH 53/83] 1. fixed losing personal tags while replacing a reocrd 2. fixed the error when no section is selected --- forms-flow-web/Dockerfile | 2 +- forms-flow-web/docker-compose.yml | 2 +- .../Attachments/AttachmentModal.js | 74 +++++++++++++++---- .../FileUpload/FileUploadForMCFPersonal.js | 4 +- .../customComponents/Records/MCFPersonal.js | 4 +- .../FOI/customComponents/Records/index.js | 6 +- .../request_api/schemas/foirecord.py | 4 +- .../request_api/services/recordservice.py | 4 +- 8 files changed, 72 insertions(+), 28 deletions(-) diff --git a/forms-flow-web/Dockerfile b/forms-flow-web/Dockerfile index 4697db0c5..a17c5a25b 100644 --- a/forms-flow-web/Dockerfile +++ b/forms-flow-web/Dockerfile @@ -39,7 +39,7 @@ ARG REACT_APP_RECORD_DOWNLOAD_LIMIT ARG REACT_APP_RECORD_DOWNLOAD_SIZE_LIMIT ENV NODE_ENV ${NODE_ENV} -ENV GENERATE_SOURCEMAP false +ENV GENERATE_SOURCEMAP ${GENERATE_SOURCEMAP} ENV REACT_APP_API_SERVER_URL ${REACT_APP_API_SERVER_URL} ENV REACT_APP_API_PROJECT_URL ${REACT_APP_API_PROJECT_URL} ENV REACT_APP_KEYCLOAK_CLIENT ${REACT_APP_KEYCLOAK_CLIENT} diff --git a/forms-flow-web/docker-compose.yml b/forms-flow-web/docker-compose.yml index 289c4bf54..d1d17a934 100644 --- a/forms-flow-web/docker-compose.yml +++ b/forms-flow-web/docker-compose.yml @@ -8,7 +8,7 @@ services: dockerfile: Dockerfile args: - NODE_ENV=${NODE_ENV:-development} - - GENERATE_SOURCEMAP=false + - GENERATE_SOURCEMAP=${GENERATE_SOURCEMAP} - REACT_APP_CLIENT_ROLE=${CLIENT_ROLE:-formsflow-client} - REACT_APP_STAFF_REVIEWER_ROLE=${REVIEWER_ROLE:-formsflow-reviewer} - REACT_APP_STAFF_DESIGNER_ROLE=${DESIGNER_ROLE:-formsflow-designer} diff --git a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js index a1e6c551a..73e2dbbae 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js +++ b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js @@ -71,7 +71,14 @@ export default function AttachmentModal({ replacementfiletypes = [], totalUploadedRecordSize = 0, requestType = FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_GENERAL, - isScanningTeamMember = false + isScanningTeamMember = false, + curPersonalAttributes = { + person: "", + filetype: "", + volume: "", + trackingid: "", + personaltag: "TBD" + } }) { let tagList = []; if (uploadFor === "attachment") { @@ -90,6 +97,21 @@ export default function AttachmentModal({ }); } + const MCFSections = useSelector( + (state) => state.foiRequests.foiPersonalSections + ); + const MSDSections = useSelector( + (state) => state.foiRequests.foiPersonalDivisionsAndSections + ); + const [TBDID, setTBDID] = useState(0); + useEffect(() => { + setTBDID( + MCFSections?.sections?.find( + (division) => division.name === "TBD" + )?.divisionid + ); + }, [MCFSections]); + const recordFormats = useSelector((state) => state.foiRequests.recordFormats); useEffect(() => { setMimeTypes( @@ -293,23 +315,52 @@ export default function AttachmentModal({ if (modalFor.toLowerCase() === "delete") { handleModal(true, null, null); } else { + let _tagValue = tagValue; let fileInfoList = []; let fileStatusTransition = ""; let personalAttributes = {}; if (modalFor === "replace" || modalFor === "replaceattachment") { fileStatusTransition = attachment?.category; + if ( + bcgovcode == "MCF" && + requestType == FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_PERSONAL + ) { + if(tagValue === "") { + fileStatusTransition = "TBD"; + _tagValue = TBDID; + } else { + fileStatusTransition = + divisions.find((division) => division.divisionid === tagValue) + ?.divisionname || + MCFSections?.sections?.find( + (division) => division.divisionid === tagValue + )?.name; + } + personalAttributes = { + person: curPersonalAttributes.person, + filetype: curPersonalAttributes.filetype, + volume: curPersonalAttributes.volume, + trackingid: curPersonalAttributes.trackingid, + personaltag: curPersonalAttributes.personaltag + } + } } else if (uploadFor === "record") { if ( bcgovcode == "MCF" && requestType == FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_PERSONAL ) { - fileStatusTransition = - divisions.find((division) => division.divisionid === tagValue) - ?.divisionname || - MCFSections?.sections?.find( - (division) => division.divisionid === tagValue - )?.name; + if(tagValue === "") { + fileStatusTransition = "TBD"; + _tagValue = TBDID; + } else { + fileStatusTransition = + divisions.find((division) => division.divisionid === tagValue) + ?.divisionname || + MCFSections?.sections?.find( + (division) => division.divisionid === tagValue + )?.name; + } personalAttributes = { person: person.name, filetype: fileType.name, @@ -340,7 +391,7 @@ export default function AttachmentModal({ filename: file.filename ? file.filename : file.name, filesize: file.size, personalattributes: personalAttributes, - ...(uploadFor === "record" && { divisionid: tagValue }), + ...(uploadFor === "record" && { divisionid: _tagValue }), }; }); @@ -538,13 +589,6 @@ export default function AttachmentModal({ } }; - const MCFSections = useSelector( - (state) => state.foiRequests.foiPersonalSections - ); - const MSDSections = useSelector( - (state) => state.foiRequests.foiPersonalDivisionsAndSections - ); - return (
{(Object.entries(files).length === 0 && !multipleFiles) || multipleFiles ? - : null}
diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js b/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js index 59889bf3c..79680cc06 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js @@ -97,7 +97,7 @@ const MCFPersonal = ({ useEffect(() => { if(MCFVolumes?.volumes) { setAllVolumes(MCFVolumes.volumes); - if(MCFFiletypes.filetypes.length > 5) { + if(MCFVolumes.volumes?.length > 5) { setVolumes(MCFVolumes.volumes.slice(0, 5)); } else { setVolumes(MCFVolumes.volumes); @@ -107,7 +107,7 @@ const MCFPersonal = ({ useEffect(() => { if(MCFFiletypes?.filetypes) { - if(MCFFiletypes.filetypes.length > 6) { + if(MCFFiletypes.filetypes?.length > 6) { setFileTypes(MCFFiletypes.filetypes.slice(0, 6)); setOtherFileTypes(MCFFiletypes.filetypes.slice(6, MCFFiletypes.filetypes.length)) } else { diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index f12dc5d84..72db8a79f 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -298,10 +298,6 @@ export const RecordsLog = ({ requestType === FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_PERSONAL ); - const MCFSections = useSelector( - (state) => state.foiRequests.foiPersonalSections - ); - useEffect(() => { setRecords(recordsObj?.records); let nonDuplicateRecords = recordsObj?.records?.filter( @@ -703,6 +699,7 @@ export const RecordsLog = ({ ? _file.lastModifiedDate : new Date(_file.lastModified), filesize: _file.size, + personalattributes: _fileInfo.personalattributes, }, replacementof: replaceRecord["replacementof"] == null || @@ -2562,6 +2559,7 @@ export const RecordsLog = ({ replacementfiletypes={getreplacementfiletypes()} requestType={requestType} isScanningTeamMember={isScanningTeamMember} + curPersonalAttributes={curPersonalAttributes} />
Date: Wed, 17 Jul 2024 13:47:51 -0700 Subject: [PATCH 54/83] Revert "Ticket 2880 - Lock Records" --- .../services/FOI/foiRecordServices.js | 26 +--- .../FOI/FOIRequest/BottomButtonGroup/index.js | 6 - .../components/FOI/FOIRequest/FOIRequest.js | 31 +--- .../MinistryReview/BottomButtonGroup.js | 5 - .../MinistryReview/MinistryReview.js | 28 ---- .../FOI/customComponents/Records/index.js | 141 ++++-------------- .../migrations/versions/5fdd5df3d642_.py | 24 --- .../request_api/models/FOIMinistryRequests.py | 3 +- .../request_api/resources/foirequest.py | 7 +- .../request_api/schemas/foirequestwrapper.py | 1 - .../foirequest/requestservicebuilder.py | 1 - .../foirequest/requestservicegetter.py | 1 - 12 files changed, 33 insertions(+), 241 deletions(-) delete mode 100644 request-management-api/migrations/versions/5fdd5df3d642_.py diff --git a/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js b/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js index 47e7f50d3..8c2d48a61 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js @@ -620,28 +620,4 @@ export const fetchPDFStitchedRecordForOIPCRedlineReview = ( done(error); }); }; - } - -export const updateUserLockedRecords = (data, requestId, ministryId, ...rest) => { - const done = fnDone(rest); - let apiUrl= replaceUrl(replaceUrl( - API.FOI_REQUEST_SECTION_API, - "", - ministryId),"",requestId - ); - return (dispatch) => { - httpPOSTRequest(`${apiUrl}/userrecordslockstatus`, data) - .then((res) => { - if (res.data) { - done(null, res.data); - } else { - dispatch(serviceActionError(res)); - throw new Error(`Error while updating records lock status for the (request# ${requestId}, ministry# ${ministryId})`); - } - }) - .catch((error) => { - dispatch(serviceActionError(error)); - done(error); - }); - }; -} \ No newline at end of file + } \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js index a66d4b21d..dd2344981 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js @@ -77,7 +77,6 @@ const BottomButtonGroup = React.memo( axisMessage, attachmentsArray, oipcData, - validLockRecordsState, }) => { /** * Bottom Button Group of Review request Page @@ -121,11 +120,6 @@ const BottomButtonGroup = React.memo( setIsAddRequest(false); } - //Logic to reset user lock records status to null (and have FE useEffect in FOIRequest.js/MinistryView.js logic takeover) if request is in unlocked request states - if (saveRequestObject.userrecordslockstatus !== null && !validLockRecordsState(currentSelectedStatus)) { - saveRequestObject.userrecordslockstatus = null; - } - //add oipc Data to save request object and sync/validate isoipcreview attribute if (requestState.toLowerCase() !== StateEnum.intakeinprogress.name.toLowerCase() && requestState.toLowerCase() !== StateEnum.unopened.name.toLowerCase()) { saveRequestObject.oipcdetails = oipcData ? oipcData : []; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js index 947dcdcaa..c472636c0 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js @@ -272,8 +272,7 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { const [isMCFPersonal, setIsMCFPersonal] = useState(bcgovcode.replaceAll('"', '') == "MCF" && requestDetails.requestType == FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_PERSONAL); const {oipcData, addOIPC, removeOIPC, updateOIPC, isOIPCReview, setIsOIPCReview, removeAllOIPCs} = useOIPCHook(); const [oipcDataInitial, setOipcDataInitial] = useState(oipcData); - const [lockRecordsTab, setLockRecordsTab] = useState(false); - + //Update disableInput when requestState changes useEffect(() => { setDisableInput(requestState?.toLowerCase() === StateEnum.closed.name.toLowerCase() && !isOIPCReview); @@ -342,20 +341,6 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { if (bcgovcode) dispatch(fetchFOIMinistryAssignedToList(bcgovcode)); }, [requestId, ministryId, comment, attachments]); - const validLockRecordsState = (currentState=requestDetails.currentState) => { - return ( - currentState === StateEnum.harms.name || - currentState === StateEnum.onhold.name || - currentState === StateEnum.recordsreadyforreview.name || - currentState === StateEnum.review.name || - currentState === StateEnum.consult.name || - currentState === StateEnum.peerreview.name || - currentState === StateEnum.signoff.name || - currentState === StateEnum.response.name || - currentState === StateEnum.closed.name - ); - } - useEffect(() => { const requestDetailsValue = requestDetails; setSaveRequestObject(requestDetailsValue); @@ -391,16 +376,6 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { } else { setIsOIPCReview(false); } - - //Adjust lockRecords value based on requestState if there is no manual user lockedrecords value present in requestDetails from DB - const updateRecordsTabAccess = () => { - if(requestDetails.userrecordslockstatus === null) { - return validLockRecordsState(requestDetails.currentState); - } else { - return requestDetails.userrecordslockstatus; - } - } - setLockRecordsTab(updateRecordsTabAccess()); }, [requestDetails]); //useEffect to manage isoipcreview attribute for requestdetails state @@ -1420,7 +1395,6 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { axisMessage={axisMessage} attachmentsArray={requestAttachments} oipcData={oipcData} - validLockRecordsState={validLockRecordsState} /> @@ -1665,9 +1639,6 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { divisions={requestDetails.divisions} recordsTabSelect={tabLinksStatuses.Records.active} requestType={requestDetails?.requestType} - lockRecords={lockRecordsTab} - setLockRecordsTab={setLockRecordsTab} - validLockRecordsState={validLockRecordsState} /> )} diff --git a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/BottomButtonGroup.js b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/BottomButtonGroup.js index 362945cb0..8d779ca37 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/BottomButtonGroup.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/BottomButtonGroup.js @@ -54,7 +54,6 @@ const BottomButtonGroup = React.memo( hasStatusRequestSaved, attachmentsArray, stateChanged, - validLockRecordsState, }) => { /** * Bottom Button Group of Review request Page @@ -110,10 +109,6 @@ const BottomButtonGroup = React.memo( }; const saveMinistryRequest = async () => { - //Logic to reset user lock records status to null (and have FE useEffect in FOIRequest.js/MinistryView.js logic takeover) if request is in unlocked request states - if (saveMinistryRequestObject.userrecordslockstatus !== null && !validLockRecordsState(currentSelectedStatus)) { - saveMinistryRequestObject.userrecordslockstatus = null; - } dispatch( saveMinistryRequestDetails( { diff --git a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js index 0e3f867d8..9330e5e60 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js @@ -244,20 +244,6 @@ const MinistryReview = React.memo(({ userDetail }) => { const userGroups = userDetail?.groups?.map(group => group.slice(1)); const isMinistry = isMinistryLogin(userGroups); - - const validLockRecordsState = (currentState=requestDetails.currentState) => { - return ( - currentState === StateEnum.harms.name || - currentState === StateEnum.onhold.name || - currentState === StateEnum.recordsreadyforreview.name || - currentState === StateEnum.review.name || - currentState === StateEnum.consult.name || - currentState === StateEnum.peerreview.name || - currentState === StateEnum.signoff.name || - currentState === StateEnum.response.name || - currentState === StateEnum.closed.name - ); - } useEffect(() => { const requestDetailsValue = requestDetails; @@ -290,16 +276,6 @@ const MinistryReview = React.memo(({ userDetail }) => { setIsMCFPersonal(true); } } - - //Adjust lockRecords value based on requestState if there is no manual user lockedrecords value present in requestDetails from DB - const updateRecordsTabAccess = () => { - if(requestDetails.userrecordslockstatus === null) { - return validLockRecordsState(requestDetails.currentState); - } else { - return requestDetails.userrecordslockstatus; - } - } - setLockRecordsTab(updateRecordsTabAccess()); }, [requestDetails, unSavedRequest]); useEffect(() => { @@ -315,7 +291,6 @@ const MinistryReview = React.memo(({ userDetail }) => { }, [isMinistryRestricted, requestWatchers]); const [recordsUploading, setRecordsUploading] = React.useState(false); - const [lockRecordsTab, setLockRecordsTab] = useState(false); const [CFRUnsaved, setCFRUnsaved] = React.useState(false); const hideBottomText = [ StateEnum.onhold.name.toLowerCase(), @@ -827,7 +802,6 @@ const MinistryReview = React.memo(({ userDetail }) => { handleSaveRequest={handleSaveRequest} currentSelectedStatus={_currentrequestStatus} hasStatusRequestSaved={hasStatusRequestSaved} - validLockRecordsState={validLockRecordsState} /> )} @@ -1076,8 +1050,6 @@ const MinistryReview = React.memo(({ userDetail }) => { setRecordsUploading={setRecordsUploading} recordsTabSelect={tabLinksStatuses.Records.active} requestType={requestDetails?.requestType} - lockRecords={lockRecordsTab} - validLockRecordsState={validLockRecordsState} /> ) : ( diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 92128e1ed..8b9527393 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -33,7 +33,6 @@ import { fetchPDFStitchedRecordForOIPCRedline, fetchPDFStitchedRecordForOIPCRedlineReview, checkForRecordsChange, - updateUserLockedRecords, } from "../../../../apiManager/services/FOI/foiRecordServices"; import { StateTransitionCategories, @@ -220,9 +219,6 @@ export const RecordsLog = ({ setRecordsUploading, recordsTabSelect, requestType, - lockRecords, - setLockRecordsTab, - validLockRecordsState, }) => { const user = useSelector((state) => state.user.userDetail); const userGroups = user?.groups?.map((group) => group.slice(1)); @@ -1745,47 +1741,6 @@ export const RecordsLog = ({ ); }; - const handleLockRecords = () => { - setLockRecordsTab(!lockRecords); - const toastID = toast.loading("Updating records lock status for request..."); - const data = {userrecordslockstatus: !lockRecords}; - dispatch( - updateUserLockedRecords( - data, - requestId, - ministryId, - (err, _res) => { - if(!err) { - toast.update(toastID, { - type: "success", - render: "Request details have been saved successfully.", - position: "top-right", - isLoading: false, - autoClose: 3000, - hideProgressBar: true, - closeOnClick: true, - pauseOnHover: true, - draggable: true, - progress: undefined, - }); - } else { - toast.error( - "Temporarily unable to update records lock status for request. Please try again in a few minutes.", - { - position: "top-right", - autoClose: 3000, - hideProgressBar: true, - closeOnClick: true, - pauseOnHover: true, - draggable: true, - progress: undefined, - } - ); - } - }) - ) - } - return (
{isAttachmentLoading ? ( @@ -1806,60 +1761,30 @@ export const RecordsLog = ({ {getRequestNumber()} - {validLockRecordsState() ? - - {isMinistryCoordinator ? -

0 && + DISABLE_REDACT_WEBLINK?.toLowerCase() == "false" && ( + - {lockRecords ? "Records Locked" : "Records Unlocked"} -

- : - - - } -
-
: - } - {(isMinistryCoordinator == false && - records?.length > 0 && - DISABLE_REDACT_WEBLINK?.toLowerCase() == "false" && ( - - - - - - ) - )} + + + ) + )} + {hasDocumentsToDownload && ( setDeleteModalOpen(true)} // title="Remove Attachments" - disabled={lockRecords || + disabled={ records.filter((record) => record.attachments?.length > 0) .length === 0 } style={ records.filter((record) => record.attachments?.length > 0) - .length === 0 || lockRecords + .length === 0 ? { pointerEvents: "none" } : {} } @@ -2188,7 +2113,6 @@ export const RecordsLog = ({
To update divisions:{" "}
- {isMCFPersonal&&!isMinistryCoordinator?( + {isMCFPersonal?( ):(
@@ -2784,18 +2831,7 @@ export const RecordsLog = ({ {requestType == FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_PERSONAL ? ( - (bcgovcode == "MCF" && isMinistryCoordinator) ? ( - r.isselected)[0]?.attributes - .divisions[0].divisionid - } - divisionModalTagValue={divisionModalTagValue} - divisions={divisions} - isMinistryCoordinator={isMinistryCoordinator} - /> - ) : bcgovcode == "MSD" ? ( + bcgovcode == "MSD" ? ( { + const isUploadedByMinistryUser = (record) => { + return hasValidDivisions(record) && isMinistryCoordinator; + }; + + const hasValidDivisions = (record) => { + return record.attributes.divisions.length > 0 && record.attributes.divisions[0].divisionname != "TBD" + }; + return ( setPopoverOpen(false)} > - {(isMCFPersonal && !isMinistryCoordinator) && ( + {(isMCFPersonal && (!isMinistryCoordinator || isUploadedByMinistryUser(record))) && ( { setEditTagModalOpen(true); From b416d0b6c97b686ed05a169a9744fbae8e5f97c3 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Fri, 2 Aug 2024 15:43:10 -0700 Subject: [PATCH 74/83] Fix observation 7 in FOIMOD-2976 --- .../FOI/customComponents/Records/MCFPersonal.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js b/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js index 74d46ede6..e4c51f370 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js @@ -161,6 +161,16 @@ const MCFPersonal = ({ } },[showAllPeople, showAllVolumes]) + React.useEffect(() => { + if(allPeople.length > 0 && personalAttributes.person !== "") { + setShowAllPeople( allPeople.filter(p => p.name==personalAttributes.person)[0].sortorder >= 5 ); + } + + if(allVolumes.length > 0 && personalAttributes.volume !== "") { + setShowAllVolumes( allVolumes.filter(v => v.name==personalAttributes.volume)[0].sortorder >= 5 ); + } + },[personalAttributes]) + React.useEffect(() => { setAdditionalFileTypes(searchFileTypes(otherFileTypes, fileTypeSearchValue, personalAttributes?.filetype)); },[fileTypeSearchValue, otherFileTypes, personalAttributes]) From 15827acec1e99e378ab3965c9716ccc9bc4d5ec9 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Fri, 2 Aug 2024 16:09:06 -0700 Subject: [PATCH 75/83] set division as a required value for observation #6 in FOIMOD-2976 --- .../FileUpload/FileUploadForMCFPersonal.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js index b5098cbf4..9c8e2971e 100644 --- a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js +++ b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js @@ -266,8 +266,8 @@ const FileUploadForMCFPersonal = ({ },[person, fileType]) React.useEffect(() => { - setAdditionalTagList(searchSections(otherTagList, searchValue, tagValue)); - },[searchValue, otherTagList, tagValue]) + setAdditionalTagList(searchSections(otherTagList, searchValue, personalTag)); + },[searchValue, otherTagList, personalTag]) const handleTrackingIDUpdate = (e) => { handleTrackingIDChange(e.target.value); @@ -707,12 +707,12 @@ const FileUploadForMCFPersonal = ({ value="" multiple={multipleFiles} accept={mimeTypes} - disabled={modalFor === "add" && !isPersonSelected} + disabled={modalFor === "add" && (!isPersonSelected || ((isMinistryCoordinator && tagValue == "")))} />
{(Object.entries(files).length === 0 && !multipleFiles) || multipleFiles ? - : null}
From e8e6a209c65a841703de01206f6d640d077279c0 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Wed, 7 Aug 2024 10:05:54 -0700 Subject: [PATCH 76/83] fix migrations for unlock record release --- .../components/FOI/customComponents/Records/MCFPersonal.js | 4 ++-- request-management-api/migrations/versions/5fdd5df3d642_.py | 2 +- .../migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js b/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js index e4c51f370..ba12bf232 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/MCFPersonal.js @@ -163,11 +163,11 @@ const MCFPersonal = ({ React.useEffect(() => { if(allPeople.length > 0 && personalAttributes.person !== "") { - setShowAllPeople( allPeople.filter(p => p.name==personalAttributes.person)[0].sortorder >= 5 ); + setShowAllPeople( allPeople.filter(p => p.name==personalAttributes.person)[0]?.sortorder >= 5 ); } if(allVolumes.length > 0 && personalAttributes.volume !== "") { - setShowAllVolumes( allVolumes.filter(v => v.name==personalAttributes.volume)[0].sortorder >= 5 ); + setShowAllVolumes( allVolumes.filter(v => v.name==personalAttributes.volume)[0]?.sortorder >= 5 ); } },[personalAttributes]) diff --git a/request-management-api/migrations/versions/5fdd5df3d642_.py b/request-management-api/migrations/versions/5fdd5df3d642_.py index 6badacd0e..7a2a3dfe4 100644 --- a/request-management-api/migrations/versions/5fdd5df3d642_.py +++ b/request-management-api/migrations/versions/5fdd5df3d642_.py @@ -11,7 +11,7 @@ # revision identifiers, used by Alembic. revision = '5fdd5df3d642' -down_revision = 'aa2691fa6c3c' +down_revision = 'e698b39da6bd' branch_labels = None depends_on = None diff --git a/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py b/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py index ea55ebf15..3eab975e6 100644 --- a/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py +++ b/request-management-api/migrations/versions/d1d4f6cdfd68_cfd_personal_tags.py @@ -11,7 +11,7 @@ # revision identifiers, used by Alembic. revision = 'd1d4f6cdfd68' -down_revision = 'e698b39da6bd' +down_revision = '5fdd5df3d642' branch_labels = None depends_on = None From 54e6aa051ab82abd71c05d504958ce6889ee6e90 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Wed, 7 Aug 2024 10:28:45 -0700 Subject: [PATCH 77/83] Fix observation 6 in FOIMOD-2976 --- .../src/components/FOI/customComponents/Records/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index a828e28c5..cb6421a91 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -2207,7 +2207,7 @@ export const RecordsLog = ({ variant="contained" onClick={addAttachments} color="primary" - disabled={lockRecords || conversionFormats?.length < 1} + disabled={lockRecords || conversionFormats?.length < 1 || (isMinistryCoordinator && divisions.length > 0)} > + Upload Records From 140d1448a711a2beab63b3c9cfac1fb070d21155 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Wed, 7 Aug 2024 10:34:46 -0700 Subject: [PATCH 78/83] fix observation 8 in FOIMOD-2976 --- .../src/components/FOI/customComponents/Records/index.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index cb6421a91..833c6447f 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -1854,8 +1854,8 @@ export const RecordsLog = ({ for (let record of records) { if(record.attributes?.personalattributes?.person && record.attributes?.personalattributes?.person === currentEditRecord.attributes?.personalattributes?.person - && record.attributes?.personalattributes?.filetype - && record.attributes?.personalattributes?.filetype === currentEditRecord.attributes?.personalattributes?.filetype + // && record.attributes?.personalattributes?.filetype + // && record.attributes?.personalattributes?.filetype === currentEditRecord.attributes?.personalattributes?.filetype ) { updateRecords.push( (({ recordid, documentmasterid, s3uripath }) => ({ @@ -1870,8 +1870,8 @@ export const RecordsLog = ({ for (let attachment of record.attachments) { if(attachment.attributes?.personalattributes?.person && attachment.attributes?.personalattributes?.person === currentEditRecord.attributes?.personalattributes?.person - && attachment.attributes?.personalattributes?.filetype - && attachment.attributes?.personalattributes?.filetype === currentEditRecord.attributes?.personalattributes?.filetype + // && attachment.attributes?.personalattributes?.filetype + // && attachment.attributes?.personalattributes?.filetype === currentEditRecord.attributes?.personalattributes?.filetype ) { updateRecords.push( (({ documentmasterid, s3uripath }) => ({ From 8bf8240b54476322ec07db9c23c36db9185c7fbc Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Wed, 7 Aug 2024 10:51:49 -0700 Subject: [PATCH 79/83] Bug fix on observation 6 in FOIMOD-2976 --- .../src/components/FOI/customComponents/Records/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 833c6447f..87487560e 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -2207,7 +2207,7 @@ export const RecordsLog = ({ variant="contained" onClick={addAttachments} color="primary" - disabled={lockRecords || conversionFormats?.length < 1 || (isMinistryCoordinator && divisions.length > 0)} + disabled={lockRecords || conversionFormats?.length < 1 || (isMinistryCoordinator && divisions.length === 0)} > + Upload Records From 5042421b379f10d583fd65f0b8f34c316616de93 Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Thu, 8 Aug 2024 13:26:16 -0700 Subject: [PATCH 80/83] Fix observation 16 on FOIMOD-3300 --- .../FOI/customComponents/Records/index.js | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 87487560e..764c0e57f 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -2660,6 +2660,7 @@ export const RecordsLog = ({ isMCFPersonal={isMCFPersonal} setEditTagModalOpen={setEditTagModalOpen} setCurrentEditRecord={setCurrentEditRecord} + lockRecords={lockRecords} /> )) ) : ( @@ -3442,36 +3443,37 @@ const AttachmentPopup = React.memo( ); }; - const DeleteMenu = () => { - return ( - { - handleDelete(); - setPopoverOpen(false); - }} - > - Delete - - ); - }; - const ActionsPopover = ({ RestrictViewInBrowser, record, setEditTagModalOpen, isMCFPersonal, - isMinistryCoordinator + isMinistryCoordinator, + lockRecords }) => { const isUploadedByMinistryUser = (record) => { - return hasValidDivisions(record) && isMinistryCoordinator; + return hasValidDivisions(record); }; const hasValidDivisions = (record) => { return record.attributes.divisions.length > 0 && record.attributes.divisions[0].divisionname != "TBD" }; + const DeleteMenu = () => { + return ( + { + handleDelete(); + setPopoverOpen(false); + }} + > + Delete + + ); + }; + return ( setPopoverOpen(false)} > - {(isMCFPersonal && (!isMinistryCoordinator || isUploadedByMinistryUser(record))) && ( + {isMCFPersonal && ( { setEditTagModalOpen(true); setPopoverOpen(false); @@ -3518,7 +3521,7 @@ const AttachmentPopup = React.memo( {(!record.attributes?.isattachment || record.attributes?.isattachment === undefined) && ( { handleReplace(); setPopoverOpen(false); @@ -3529,7 +3532,7 @@ const AttachmentPopup = React.memo( )} {record.attributes?.isattachment && ( { handleReplaceAttachment(); setPopoverOpen(false); @@ -3621,6 +3624,7 @@ const AttachmentPopup = React.memo( setEditTagModalOpen={setEditTagModalOpen} isMCFPersonal={isMCFPersonal} isMinistryCoordinator={isMinistryCoordinator} + lockRecords={lockRecords} /> ); From 6ee19f326967ca6f1a17bf19f1014d896bab4e6f Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Thu, 8 Aug 2024 14:47:12 -0700 Subject: [PATCH 81/83] fix sonar warning --- .../components/FOI/customComponents/Records/index.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/Records/index.js b/forms-flow-web/src/components/FOI/customComponents/Records/index.js index 764c0e57f..e523f7b0e 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Records/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/Records/index.js @@ -3459,11 +3459,13 @@ const AttachmentPopup = React.memo( return record.attributes.divisions.length > 0 && record.attributes.divisions[0].divisionname != "TBD" }; + const disableMinistryUser = isMCFPersonal && isMinistryCoordinator && !isUploadedByMinistryUser(record); + const DeleteMenu = () => { return ( { handleDelete(); setPopoverOpen(false); @@ -3497,7 +3499,7 @@ const AttachmentPopup = React.memo( {isMCFPersonal && ( { setEditTagModalOpen(true); setPopoverOpen(false); @@ -3521,7 +3523,7 @@ const AttachmentPopup = React.memo( {(!record.attributes?.isattachment || record.attributes?.isattachment === undefined) && ( { handleReplace(); setPopoverOpen(false); @@ -3532,7 +3534,7 @@ const AttachmentPopup = React.memo( )} {record.attributes?.isattachment && ( { handleReplaceAttachment(); setPopoverOpen(false); From b736a08e6170b83c9e3ef45c307cdb81e593709b Mon Sep 17 00:00:00 2001 From: Richard Qi Date: Fri, 9 Aug 2024 11:55:46 -0700 Subject: [PATCH 82/83] disable drag and drop for observation #28 in FOIMOD-3300 (observation #9 in FOIMOD-2976) --- .../FileUpload/FileUploadForMCFPersonal.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js index 9c8e2971e..e2684629f 100644 --- a/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js +++ b/forms-flow-web/src/components/FOI/customComponents/FileUpload/FileUploadForMCFPersonal.js @@ -211,9 +211,14 @@ const FileUploadForMCFPersonal = ({ }; const fileDrop = (e) => { e.preventDefault(); - const newFiles = e.dataTransfer.files; - const totalNoOfFiles = Object.entries(files).length + newFiles.length; - validateFiles(newFiles, totalNoOfFiles); + + if(modalFor === "add" && (!isPersonSelected || ((isMinistryCoordinator && tagValue == "")))) { + return + } else { + const newFiles = e.dataTransfer.files; + const totalNoOfFiles = Object.entries(files).length + newFiles.length; + validateFiles(newFiles, totalNoOfFiles); + } } const removeFile = (fileName) => { const _file = files[fileName]; From 826abba5c8c09d47066d38d8fa92176d47f6b532 Mon Sep 17 00:00:00 2001 From: Aman-Hundal Date: Fri, 9 Aug 2024 13:18:22 -0700 Subject: [PATCH 83/83] Adjusted 2 head / migration conflict by adjusting migration files and downgrades --- request-management-api/migrations/versions/3a2ffb0af202_.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/request-management-api/migrations/versions/3a2ffb0af202_.py b/request-management-api/migrations/versions/3a2ffb0af202_.py index 8a1c5faf1..d5c948bf2 100644 --- a/request-management-api/migrations/versions/3a2ffb0af202_.py +++ b/request-management-api/migrations/versions/3a2ffb0af202_.py @@ -1,7 +1,7 @@ """empty message Revision ID: 3a2ffb0af202 -Revises: e698b39da6bd +Revises: aa2691fa6c3c Create Date: 2023-06-01 22:31:24.595281 """ @@ -11,7 +11,7 @@ # revision identifiers, used by Alembic. revision = '3a2ffb0af202' -down_revision = 'e698b39da6bd' +down_revision = 'aa2691fa6c3c' branch_labels = None depends_on = None