diff --git a/.gitignore b/.gitignore index 7ebc811f5..101b92e87 100644 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,10 @@ datamigrations/FOIMOD.CFD.ConsoleApp.DocMigration/FOIMOD.CFD.DocMigration.OCR.Te datamigrations/FOIMOD.CFD.ConsoleApp.DocMigration/FOIMOD.CFD.DocMigration.OCR.Tests/bin/* /datamigrations/FOIMOD.CFD.ConsoleApp.DocMigration/FOIMOD.CFD.DocMigration.Utils.UnitTests/appsettings.dev.json historical-search-api/env/* +datamigrations/FOIMOD.AXIS.DocumentMigration/FOIMOD.HistoricalDocMigration.Utils/obj/* +datamigrations/FOIMOD.AXIS.DocumentMigration/FOIMOD.HistoricalDocMigration.Utils/bin/* +datamigrations/FOIMOD.AXIS.DocumentMigration/*/obj/* +datamigrations/FOIMOD.AXIS.DocumentMigration/*/bin/* +datamigrations/FOIMOD.AXIS.DocumentMigration/.vs/* +*.locenv +*.locenv diff --git a/docker-compose.yml b/docker-compose.yml index 9734b5e24..ca25b7ef6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -117,6 +117,7 @@ services: - UNOPENED_REPORT_EMAIL_RECIPIENT=${UNOPENED_REPORT_EMAIL_RECIPIENT} - AXIS_API_URL=${AXIS_API_URL} - AXIS_SYNC_BATCHSIZE=${AXIS_SYNC_BATCHSIZE} + - SKIP_OPENINFO_MINISTRIES=${SKIP_OPENINFO_MINISTRIES} #- LOG_ROOT=${LOG_ROOT} #- LOG_BASIC=${LOG_BASIC} #- LOG_TRACING=${LOG_TRACING} diff --git a/forms-flow-web/Dockerfile b/forms-flow-web/Dockerfile index 2548fca5e..52220dfbd 100644 --- a/forms-flow-web/Dockerfile +++ b/forms-flow-web/Dockerfile @@ -72,6 +72,7 @@ ENV REACT_APP_SESSION_SECURITY_KEY ${REACT_APP_SESSION_SECURITY_KEY} ENV REACT_APP_SOCKETIO_CONNECT_NONCE ${REACT_APP_SOCKETIO_CONNECT_NONCE} ENV REACT_APP_RECORD_DOWNLOAD_LIMIT ${REACT_APP_RECORD_DOWNLOAD_LIMIT} ENV REACT_APP_RECORD_DOWNLOAD_SIZE_LIMIT ${REACT_APP_RECORD_DOWNLOAD_SIZE_LIMIT} +ENV REACT_APP_SKIP_OPENINFO_MINISTRIES ${REACT_APP_SKIP_OPENINFO_MINISTRIES} # add `/app/node_modules/.bin` to $PATH ENV PATH /forms-flow-web/app/node_modules/.bin:$PATH diff --git a/forms-flow-web/Dockerfile.local b/forms-flow-web/Dockerfile.local index 4a7e37583..0f5fa88b7 100644 --- a/forms-flow-web/Dockerfile.local +++ b/forms-flow-web/Dockerfile.local @@ -65,6 +65,11 @@ ENV REACT_APP_DISABLE_GATHERINGRECORDS_TAB ${REACT_APP_DISABLE_GATHERINGRECORDS_ ENV REACT_APP_RECORD_DOWNLOAD_LIMIT ${REACT_APP_RECORD_DOWNLOAD_LIMIT} ENV REACT_APP_RECORD_DOWNLOAD_SIZE_LIMIT ${REACT_APP_RECORD_DOWNLOAD_SIZE_LIMIT} ENV REACT_APP_SESSION_SECURITY_KEY ${REACT_APP_SESSION_SECURITY_KEY} + +ENV REACT_APP_SOCKETIO_CONNECT_NONCE ${REACT_APP_SOCKETIO_CONNECT_NONCE} +ENV REACT_APP_RECORD_DOWNLOAD_LIMIT ${REACT_APP_RECORD_DOWNLOAD_LIMIT} +ENV REACT_APP_RECORD_DOWNLOAD_SIZE_LIMIT ${REACT_APP_RECORD_DOWNLOAD_SIZE_LIMIT} +ENV REACT_APP_SKIP_OPENINFO_MINISTRIES ${REACT_APP_SKIP_OPENINFO_MINISTRIES} # add `/app/node_modules/.bin` to $PATH ENV PATH /forms-flow-web/app/node_modules/.bin:$PATH diff --git a/forms-flow-web/src/actions/FOI/foiActionConstants.js b/forms-flow-web/src/actions/FOI/foiActionConstants.js index 5fc5fd666..232d61bed 100644 --- a/forms-flow-web/src/actions/FOI/foiActionConstants.js +++ b/forms-flow-web/src/actions/FOI/foiActionConstants.js @@ -107,10 +107,17 @@ const FOI_ACTION_CONSTANTS = { OIPC_REVIEWTYPES: "OIPC_REVIEWTYPES", OIPC_INQUIRYOUTCOMES: "OIPC_INQUIRYOUTCOMES", + OI_EXEMPTIONS: "OI_EXEMPTIONS", + OI_PUBLICATIONSTATUSES: "OI_PUBLICATIONSTATUSES", + OI_STATUSES: "OI_STATUSES", + FOI_OPENINFO_REQUEST: "FOI_OPENINFO_REQUEST", FOI_ADVANCED_SEARCH_FILTER: "FOI_ADVANCED_SEARCH_FILTER", FOI_HISTORIC_SEARCH_PARAMS: "FOI_HISTORIC_SEARCH_PARAMS", FOI_COMMENT_TYPES: "FOI_COMMENT_TYPES", + FOI_OPENINFO_ADDITIONAL_FILES: "FOI_OPENINFO_ADDITIONAL_FILES", + FOI_PDF_STITCHED_OI_PACKAGE: "FOI_PDF_STITCHED_OI_PACKAGE", + FOI_PDF_STITCHED_STATUS_FOR_OI_PACKAGE:"FOI_PDF_STITCHED_STATUS_FOR_OI_PACKAGE" }; export default FOI_ACTION_CONSTANTS; diff --git a/forms-flow-web/src/actions/FOI/foiRequestActions.js b/forms-flow-web/src/actions/FOI/foiRequestActions.js index 3d51a14fa..d3d7ae522 100644 --- a/forms-flow-web/src/actions/FOI/foiRequestActions.js +++ b/forms-flow-web/src/actions/FOI/foiRequestActions.js @@ -153,6 +153,13 @@ export const setFOIPDFStitchedRecordForResponsePackage = payload: data, }); }; +export const setFOIPDFStitchedOIPackage = + (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_OI_PACKAGE, + payload: data, + }); + }; export const setFOIPDFStitchedRecordForOipcRedlineReview = (data) => (dispatch) => { dispatch({ type: FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_RECORD_FOR_OIPCREDLINEREVIEW, @@ -190,6 +197,12 @@ export const setFOIPDFStitchStatusForResponsePackage = (data) => (dispatch) => { payload: data, }); }; +export const setFOIPDFStitchStatusForOIPackage = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_STATUS_FOR_OI_PACKAGE, + payload: data, + }); +}; export const setFOIPDFStitchStatusForOipcRedlineReview = (data) => (dispatch) => { dispatch({ type: FOI_ACTION_CONSTANTS.FOI_PDF_STITCHED_STATUS_FOR_OIPCREDLINEREVIEW, @@ -514,6 +527,34 @@ export const setOIPCInquiryoutcomes = (data) => (dispatch) => { }); }; +export const setOIExemptions = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.OI_EXEMPTIONS, + payload: data, + }); +}; + +export const setOIPublicationStatuses = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.OI_PUBLICATIONSTATUSES, + payload: data, + }); +}; + +export const setOIStatuses = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.OI_STATUSES, + payload: data, + }); +}; + +export const setFOIOpenInfoRequest = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.FOI_OPENINFO_REQUEST, + payload: data, + }); +}; + export const setAdvancedSearchFilter = (data) => (dispatch) => { dispatch({ type: FOI_ACTION_CONSTANTS.FOI_ADVANCED_SEARCH_FILTER, @@ -533,4 +574,11 @@ export const setFOICommentTypes = (data) => (dispatch) => { type: FOI_ACTION_CONSTANTS.FOI_COMMENT_TYPES, payload: data, }); +}; + +export const setFOIOpenInfoAdditionalFiles = (data) => (dispatch) => { + dispatch({ + type: FOI_ACTION_CONSTANTS.FOI_OPENINFO_ADDITIONAL_FILES, + payload: data, + }); }; \ No newline at end of file diff --git a/forms-flow-web/src/apiManager/endpoints/index.js b/forms-flow-web/src/apiManager/endpoints/index.js index da42bc58d..daee15ac1 100644 --- a/forms-flow-web/src/apiManager/endpoints/index.js +++ b/forms-flow-web/src/apiManager/endpoints/index.js @@ -10,6 +10,7 @@ const API = { FOI_GET_MINISTRY_REQUESTS_API: `${FOI_BASE_API_URL}/api/dashboard/ministry`, FOI_GET_REQUESTS_PAGE_API: `${FOI_BASE_API_URL}/api/dashboardpagination`, FOI_GET_MINISTRY_REQUESTS_PAGE_API: `${FOI_BASE_API_URL}/api/dashboardpagination/ministry`, + FOI_GET_OI_REQUESTS_PAGE_API: `${FOI_BASE_API_URL}/api/dashboardpagination/oi`, FOI_GET_CATEGORIES_API: `${FOI_BASE_API_URL}/api/foiflow/applicantcategories`, FOI_GET_PROGRAMAREAS_API: `${FOI_BASE_API_URL}/api/foiflow/programareas`, FOI_GET_PROGRAMAREAS_FORUSER_API: `${FOI_BASE_API_URL}/api/foiflow/programareasforuser`, @@ -153,6 +154,9 @@ const API = { FOI_DOWNLOAD_RECORDS_FOR_RESPONSEPACKAGE: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//download/responsepackage`, FOI_PDF_STITCH_STATUS_FOR_RESPONSEPACKAGE: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//responsepackage/pdfstitchjobstatus`, + FOI_PDF_STITCH_STATUS: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest///pdfstitchjobstatus`, + FOI_DOWNLOAD_ZIPPED_PACKAGE: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//download`, + FOI_DOWNLOAD_RECORDS_FOR_OIPCREDLINEREVIEW: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//download/oipcreviewredline`, FOI_PDF_STITCH_STATUS_FOR_OIPCREDLINEREVIEW: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//oipcreviewredline/pdfstitchjobstatus`, FOI_DOWNLOAD_RECORDS_FOR_OIPCREDLINE: `${FOI_BASE_API_URL}/api/foirecord//ministryrequest//download/oipcredline`, @@ -165,6 +169,14 @@ const API = { FOI_GET_OIPC_STATUSES: `${FOI_BASE_API_URL}/api/foiflow/oipc/statuses`, FOI_GET_OIPC_REVIEWTYPES: `${FOI_BASE_API_URL}/api/foiflow/oipc/reviewtypes`, FOI_GET_OIPC_INQUIRYOUTCOMES: `${FOI_BASE_API_URL}/api/foiflow/oipc/inquiryoutcomes`, + + FOI_GET_OI_PUBLICATIONSTATUSES: `${FOI_BASE_API_URL}/api/foiflow/openinfo/publicationstatuses`, + FOI_GET_OI_EXEMPTIONS: `${FOI_BASE_API_URL}/api/foiflow/openinfo/exemptions`, + FOI_GET_OI_STATUSES: `${FOI_BASE_API_URL}/api/foiflow/openinfo/statuses`, + FOI_GET_OPENINFO_REQUEST: `${FOI_BASE_API_URL}/api/foiopeninfo/ministryrequest/`, + FOI_POST_OPENINFO_REQUEST: `${FOI_BASE_API_URL}/api/foiopeninfo/foirequest//ministryrequest/`, FOI_GET_COMMENT_TYPES: `${FOI_BASE_API_URL}/api/foiflow/commenttypes`, + FOI_OPENINFO_ADDITIONAL_FILES: `${FOI_BASE_API_URL}/api/foiopeninfoadditionalfiles/foirequest//ministryrequest/`, + FOI_DELETE_OPENINFO_ADDITIONAL_FILES: `${FOI_BASE_API_URL}/api/foiopeninfoadditionalfiles/foirequest//ministryrequest//delete` }; export default API; diff --git a/forms-flow-web/src/apiManager/services/FOI/foiHistoricalSearchServices.js b/forms-flow-web/src/apiManager/services/FOI/foiHistoricalSearchServices.js index 7e6e5a00d..530534f4c 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiHistoricalSearchServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiHistoricalSearchServices.js @@ -33,7 +33,7 @@ export const fetchHistoricalSearchData = ({ size: size, sortingitem: sort[0]['field'], sortingorder: sort[0]['sort'], - search: search, + search: (keywords.length === 1 && keywords[0] === "") ? null : search, keywords: keywords, requestType: requestType, requestFlags: requestFlags, diff --git a/forms-flow-web/src/apiManager/services/FOI/foiMasterDataServices.js b/forms-flow-web/src/apiManager/services/FOI/foiMasterDataServices.js index d57398315..eb6957d42 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiMasterDataServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiMasterDataServices.js @@ -29,6 +29,9 @@ import { setOIPCStatuses, setOIPCReviewtypes, setOIPCInquiryoutcomes, + setOIExemptions, + setOIPublicationStatuses, + setOIStatuses, setFOICommentTypes } from "../../../actions/FOI/foiRequestActions"; import { fnDone, catchError } from "./foiServicesUtil"; @@ -692,6 +695,72 @@ import { }; }; + export const fetchOpenInfoPublicationStatuses = () => { + return (dispatch) => { + httpGETRequest(API.FOI_GET_OI_PUBLICATIONSTATUSES, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + const oiPublicationStatuses = res.data; + dispatch(setOIPublicationStatuses(oiPublicationStatuses)); + dispatch(setFOILoader(false)); + } else { + console.log("Error while fetching OI publication statuses master data", res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + console.log("Error while fetching OI publication statuses master data", error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + }; + } + + export const fetchOpenInfoExemptions = () => { + return (dispatch) => { + httpGETRequest(API.FOI_GET_OI_EXEMPTIONS, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + const oiExemptions = res.data; + oiExemptions.unshift({name: "Select Reason", oiexemptionid: 0, isactive: true}) + dispatch(setOIExemptions(oiExemptions)); + dispatch(setFOILoader(false)); + } else { + console.log("Error while fetching OI exemptions master data", res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + console.log("Error while fetching OI exemptions master data", error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + }; + } + + export const fetchOpenInfoStatuses = () => { + return (dispatch) => { + httpGETRequest(API.FOI_GET_OI_STATUSES, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + const oiStatuses = res.data; + dispatch(setOIStatuses(oiStatuses)); + dispatch(setFOILoader(false)); + } else { + console.log("Error while fetching OI statuses master data", res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + console.log("Error while fetching OI statuses master data", error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + }; + } export const fetchFOICommentTypes = () => { return (dispatch) => { httpGETRequest(API.FOI_GET_COMMENT_TYPES, {}, UserService.getToken()) @@ -716,4 +785,6 @@ import { }); }; }; - \ No newline at end of file + + + diff --git a/forms-flow-web/src/apiManager/services/FOI/foiOpenInfoRequestServices.js b/forms-flow-web/src/apiManager/services/FOI/foiOpenInfoRequestServices.js new file mode 100644 index 000000000..d0009cf5c --- /dev/null +++ b/forms-flow-web/src/apiManager/services/FOI/foiOpenInfoRequestServices.js @@ -0,0 +1,211 @@ +import { httpPOSTRequest, httpGETRequest } from "../../httpRequestHandler"; +import API from "../../endpoints"; +import { + serviceActionError, + setFOILoader, + setFOIOpenInfoRequest, + setFOIOpenInfoAdditionalFiles, + setFOIRequestDetail +} from "../../../actions/FOI/foiRequestActions"; +import { fnDone, catchError } from "./foiServicesUtil"; +import UserService from "../../../services/UserService"; +import { replaceUrl } from "../../../helper/FOI/helper"; +import { OIStates, OIPublicationStatuses, OIExemptions } from "../../../helper/openinfo-helper"; + +export const fetchFOIOpenInfoRequest = (foiministryrequestid) => { + if (!foiministryrequestid) { + return () => {}; + } + const foiOpenInfoRequestAPIUrl = replaceUrl( + replaceUrl(API.FOI_GET_OPENINFO_REQUEST), + "", + foiministryrequestid + ); + return (dispatch) => { + httpGETRequest(foiOpenInfoRequestAPIUrl, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + const foiOpenInfoRequest = res.data; + dispatch(setFOIOpenInfoRequest(foiOpenInfoRequest)); + dispatch(setFOILoader(false)); + } else { + console.log("Error while fetching FOIOpenInfoRequest data", res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + console.log("Error while fetching FOIOpenInfoRequest data", error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + }; +}; + +export const saveFOIOpenInfoRequest = ( + foiministryrequestid, + foirequestId, + data, + isOIUser, + requetsinfo, + ...rest +) => { + let apiUrl = replaceUrl( + replaceUrl(API.FOI_POST_OPENINFO_REQUEST, "", foirequestId), + "", + foiministryrequestid + ); + const done = fnDone(rest); + const isValidExemptionRequest = !isOIUser && data.oipublicationstatus_id === OIPublicationStatuses.DoNotPublish && data.oiexemption_id !== OIExemptions.OutsideScopeOfPublication; + const isValidExemptionDenial = isOIUser && data.oipublicationstatus_id === OIPublicationStatuses.DoNotPublish && data.oiexemption_id !== OIExemptions.OutsideScopeOfPublication && data.oiexemptionapproved === false; + const manualPublicationStatusChange = requetsinfo.oistatusid === OIStates.ExemptionRequest && data.oipublicationstatus_id === OIPublicationStatuses.Publish; + return (dispatch) => { + updateFOIMinistryRequestOIStatus(foiministryrequestid, foirequestId, isValidExemptionRequest, isValidExemptionDenial, manualPublicationStatusChange) + .then((res) => { + // If res.data.sucess (meaning BE call to update FOIMinistryRequest oistatusid for IAO OI Exemption purposes is successfull) => + // create and store an exemption date for the related foiopeninfo request + if (res.data?.success && isValidExemptionRequest) { + data.oiexemptiondate = new Date(); + } + if (res.data?.success && (isValidExemptionDenial || manualPublicationStatusChange)) { + data.oiexemptiondate = null; + } + httpPOSTRequest(apiUrl, data) + .then((res) => { + if (res.data) { + done(null, res.data); + // dispatch(setFOILoader(false)); + } else { + console.log("Error while updating FOIOpenInfoRequest data", res); + dispatch(serviceActionError(res)); + // dispatch(setFOILoader(false)); + } + }); + }) + .catch((error) => { + done(error); + console.log("Error while updating FOIOpenInfoRequest data", error); + dispatch(serviceActionError(error)); + // dispatch(setFOILoader(false)); + }); + }; +}; + +const updateFOIMinistryRequestOIStatus = ( + foiministryrequestid, + foirequestId, + isValidExemptionRequest, + isValidExemptionDenial, + manualPublicationStatusChange, +) => { + let apiUrl= replaceUrl(replaceUrl( + API.FOI_REQUEST_SECTION_API, + "", + foiministryrequestid),"", foirequestId + ); + // Update FOIMinistryRequest oistatusid to "Do Not Publish" if EXEMPTION is required from IAO + if (isValidExemptionRequest) { + return httpPOSTRequest(`${apiUrl}/oistatusid`, { oistatusid: OIStates.ExemptionRequest }); + // Update FOIMinistryRequest oistatusid to "Null" if Exemption Request denied OR if OpenInfo Publication status is manually changed from "Do not Publish" to Publish + } else if (isValidExemptionDenial || manualPublicationStatusChange) { + return httpPOSTRequest(`${apiUrl}/oistatusid`, { oistatusid: null }); + } else { + return Promise.resolve("API call to adjust foiministryrequest not needed"); + } +}; + +export const fetchFOIOpenInfoAdditionalFiles = (foirequestId, foiministryrequestid) => { + if (!foiministryrequestid) { + return () => {}; + } + let apiUrl = replaceUrl( + replaceUrl(API.FOI_OPENINFO_ADDITIONAL_FILES, "", foirequestId), + "", + foiministryrequestid + ); + return (dispatch) => { + httpGETRequest(apiUrl, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + const files = res.data; + dispatch(setFOIOpenInfoAdditionalFiles(files)); + dispatch(setFOILoader(false)); + } else { + console.log("Error while fetching FOIOpenInfoRequest data", res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + console.log("Error while fetching FOIOpenInfoRequest data", error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + } +}; + +export const deleteFOIOpenInfoAdditionalFiles = ( + foiministryrequestid, + foirequestId, + data, + ...rest +) => { + let apiUrl = replaceUrl( + replaceUrl(API.FOI_DELETE_OPENINFO_ADDITIONAL_FILES, "", foirequestId), + "", + foiministryrequestid + ); + const done = fnDone(rest); + return (dispatch) => { + httpPOSTRequest(apiUrl, data) + .then((res) => { + if (res.data) { + done(null, res.data); + dispatch(setFOILoader(false)); + } else { + console.log("Error while deleting FOIAdditionalFiles data", res); + dispatch(serviceActionError(res)); + dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + done(error); + console.log("Error while deleting FOIAdditionalFiles data", error); + dispatch(serviceActionError(error)); + dispatch(setFOILoader(false)); + }); + }; +}; + +export const saveFOIOpenInfoAdditionalFiles = ( + foiministryrequestid, + foirequestId, + data, + ...rest +) => { + let apiUrl = replaceUrl( + replaceUrl(API.FOI_OPENINFO_ADDITIONAL_FILES, "", foirequestId), + "", + foiministryrequestid + ); + const done = fnDone(rest); + return (dispatch) => { + httpPOSTRequest(apiUrl, data) + .then((res) => { + if (res.data) { + done(null, res.data); + // dispatch(setFOILoader(false)); + } else { + console.log("Error while saving FOIAdditionalFiles data", res); + dispatch(serviceActionError(res)); + // dispatch(setFOILoader(false)); + } + }) + .catch((error) => { + done(error); + console.log("Error while saving FOIAdditionalFiles data", error); + dispatch(serviceActionError(error)); + // dispatch(setFOILoader(false)); + }); + }; +}; diff --git a/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js b/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js index 8e5cf01dd..e24f1e418 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiRecordServices.js @@ -503,6 +503,76 @@ export const fetchPDFStitchedRecordForResponsePackage = ( }; }; +export const fetchPDFStitchedStatus = ( + requestId, + ministryId, + packagename, + ...rest) => { + if (!ministryId) { + return () => {}; + } + const done = fnDone(rest); + let apiUrl = replaceUrl(replaceUrl( + replaceUrl( + API.FOI_PDF_STITCH_STATUS, + "", + ministryId + ), + "", + requestId + ), '', packagename); + return (dispatch) => { + httpGETRequest(apiUrl, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + done(null, res.data); + } + }) + .catch((error) => { + console.log("Error in fetching pdfstitch job status", error); + dispatch(serviceActionError(error)); + done(error); + }); + }; + } + +export const fetchPDFStitchedPackage = ( + requestId, + ministryId, + packagename, + ...rest +) => { + if (!ministryId) { + return () => {}; + } + const done = fnDone(rest); + let apiUrl = replaceUrl( + replaceUrl( + API.FOI_DOWNLOAD_ZIPPED_PACKAGE, + "", + ministryId + ), + "", + requestId + ) + "/" + packagename; + return (dispatch) => { + httpGETRequest(apiUrl, {}, UserService.getToken()) + .then((res) => { + if (res.data) { + done(null, res.data); + } else { + console.log("Error in fetching records for response package", res); + dispatch(serviceActionError(res)); + } + }) + .catch((error) => { + console.log("Error in fetching records for response package", error); + dispatch(serviceActionError(error)); + done(error); + }); + }; +}; + export const fetchPDFStitchedStatusForOIPCRedline = ( requestId, ministryId, diff --git a/forms-flow-web/src/apiManager/services/FOI/foiRequestServices.js b/forms-flow-web/src/apiManager/services/FOI/foiRequestServices.js index 977334d4b..725bf7c35 100644 --- a/forms-flow-web/src/apiManager/services/FOI/foiRequestServices.js +++ b/forms-flow-web/src/apiManager/services/FOI/foiRequestServices.js @@ -159,6 +159,46 @@ export const fetchFOIMinistryRequestListByPage = (page = 1, size = 10, sort = [{ }; }; +export const fetchFOIOIRequestListByPage = (page = 1, size = 10, sort = [{field:'defaultSorting', sort:'asc'}], filters = null, keyword = null, additionalFilter = 'All', userID = null) => { + let sortingItems = []; + let sortingOrders = []; + sort.forEach((item)=>{ + sortingItems.push(item.field); + sortingOrders.push(item.sort); + }); + + return (dispatch) => { + httpGETRequest( + API.FOI_GET_OI_REQUESTS_PAGE_API, + { + "page": page, + "size": size, + "sortingitems": sortingItems, + "sortingorders": sortingOrders, + "filters": filters, + "keyword": keyword, + "additionalfilter": additionalFilter, + "userid": userID + }, + UserService.getToken()) + .then((res) => { + if (res.data) { + dispatch(clearMinistryViewRequestDetails({})); + dispatch(setFOIMinistryRequestList(res.data)); + dispatch(setFOILoader(false)); + if (res.data?.data[0]?.bcgovcode) + dispatch(fetchFOIMinistryAssignedToList(res.data.data[0].bcgovcode)); + } else { + dispatch(serviceActionError(res)); + throw new Error("Error in fetching dashboard data for IAO"); + } + }) + .catch((error) => { + catchError(error, dispatch); + }); + }; +}; + export const fetchFOIRequestDetailsWrapper = (requestId, ministryId) => { if (ministryId) { return fetchFOIRequestDetails(requestId, ministryId); diff --git a/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/DataGridAdvancedSearch.js b/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/DataGridAdvancedSearch.js index 9f130f9e6..4e60c209f 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/DataGridAdvancedSearch.js +++ b/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/DataGridAdvancedSearch.js @@ -10,7 +10,6 @@ import { getFullName, getDaysLeft, getReceivedDate, - // onBehalfFullName, getRecordsDue, LightTooltip, displayQueueFlagIcons, @@ -22,6 +21,7 @@ import { isProcessingTeam, isFlexTeam, isIntakeTeam, + isOITeam, } from "../../../../../helper/FOI/helper"; import clsx from "clsx"; import { push } from "connected-react-router"; @@ -426,6 +426,81 @@ const DataGridAdvancedSearch = ({ userDetail }) => { }, ]; + + const OITeamColumns = [ + { + field: "receivedDate", + headerName: "RECEIVED DATE", + flex: 1, + renderCell: hyperlinkRenderCell, + cellClassName: 'foi-advanced-search-result-cell', + headerAlign: "left" + }, + { + field: "axisRequestId", + headerName: "ID NUMBER", + flex: 1, + renderCell: hyperlinkRenderCell, + cellClassName: 'foi-advanced-search-result-cell', + headerAlign: "left" + }, + { + field: "requestType", + headerName: "TYPE", + flex: 1, + renderCell: hyperlinkRenderCell, + cellClassName: 'foi-advanced-search-result-cell', + headerAlign: "left", + }, + { + field: "oilayerpagecount", + headerName: "PAGES", + flex: 1, + renderCell: hyperlinkRenderCell, + cellClassName: 'foi-advanced-search-result-cell', + headerAlign: "left", + }, + { + field: "publicationStatus", + headerName: "PUBLICATION STATUS", + flex: 1, + renderCell: hyperlinkRenderCell, + cellClassName: 'foi-advanced-search-result-cell', + headerAlign: "left", + }, + { + field: "fromClosed", + headerName: "FROM CLOSED", + flex: 1, + renderCell: hyperlinkRenderCell, + cellClassName: 'foi-advanced-search-result-cell', + headerAlign: "left", + }, + { + field: "publicationDate", + headerName: "PUBLICATION DATE", + flex: 1, + renderCell: hyperlinkRenderCell, + cellClassName: 'foi-advanced-search-result-cell', + headerAlign: "left", + }, + { + field: "assignedTo", + headerName: "ASSIGNEE", + flex: 1, + renderCell: hyperlinkRenderCell, + cellClassName: 'foi-advanced-search-result-cell', + headerAlign: "left", + }, + { + field: "applicantType", + headerName: "APPLICANT TYPE", + flex: 1, + renderCell: hyperlinkRenderCell, + cellClassName: 'foi-advanced-search-result-cell', + headerAlign: "left", + } + ]; const defaultTableInfo = { columns: IntakeTeamColumns, @@ -476,6 +551,13 @@ const DataGridAdvancedSearch = ({ userDetail }) => { }, }; } + + if (isOITeam(userGroups)) { + defaultTableInfo.columns = OITeamColumns; + defaultTableInfo.sort = [ + { field: "receivedDate", sort: "desc" }, // Default sorting for OI team + ]; + } return defaultTableInfo; }; diff --git a/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/SearchComponent.js b/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/SearchComponent.js index 9ca7fe523..dc3233ca6 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/SearchComponent.js +++ b/forms-flow-web/src/components/FOI/Dashboard/IAO/AdvancedSearch/SearchComponent.js @@ -143,7 +143,8 @@ const AdvancedSearch = ({ userDetail }) => { [StateEnum.review.label]: false, [StateEnum.signoff.label]: false, [StateEnum.closed.label]: false, - [StateEnum.callforrecordsoverdue.label]: false + [StateEnum.callforrecordsoverdue.label]: false, + [StateEnum.onholdother.label]: false }; const [requestState, setRequestState] = useState(() => { @@ -672,7 +673,7 @@ const AdvancedSearch = ({ userDetail }) => { return All; } - return selected.map(value => StateEnum[value].name).join(", "); + return selected.map(value => Object.values(StateEnum).find(state => state.label === value).name).join(", "); }} > @@ -681,11 +682,11 @@ const AdvancedSearch = ({ userDetail }) => { {Object.entries(StateEnum).filter(([key, value]) => key !== 'callforrecordsoverdue').map(([key, value]) => ( -1 + requestState.indexOf(value.label) > -1 } color="success" /> diff --git a/forms-flow-web/src/components/FOI/Dashboard/IAO/Dashboard.js b/forms-flow-web/src/components/FOI/Dashboard/IAO/Dashboard.js index f9e6f0fc2..13300c0e3 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/IAO/Dashboard.js +++ b/forms-flow-web/src/components/FOI/Dashboard/IAO/Dashboard.js @@ -37,6 +37,8 @@ const Dashboard = ({ userDetail }) => { const showAdvancedSearch = useSelector((state) => state.foiRequests.showAdvancedSearch); const showEventQueue = useSelector((state) => state.foiRequests.showEventQueue); + const isOITeam = user.groups.some(group => group.includes('OI Team')); + const addRequest = (_e) => { dispatch(push(`/foi/addrequest`)); }; @@ -148,6 +150,7 @@ const Dashboard = ({ userDetail }) => { + {!isOITeam && ( + )} { (!showAdvancedSearch && !showEventQueue) && diff --git a/forms-flow-web/src/components/FOI/Dashboard/IAO/Queue.js b/forms-flow-web/src/components/FOI/Dashboard/IAO/Queue.js index c074d3485..9c64db60c 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/IAO/Queue.js +++ b/forms-flow-web/src/components/FOI/Dashboard/IAO/Queue.js @@ -190,8 +190,8 @@ const Queue = ({ userDetail, tableInfo }) => { label={"MY TEAM'S REQUESTS"} color="primary" size="small" - onClick={() => requestFilterChange("All")} - clicked={requestFilter === "All"} + onClick={() => requestFilterChange("teamRequests")} + clicked={requestFilter === "teamRequests"} /> { defaultTableInfo.columns = FlexTeamColumns; } + if (isOITeam(userGroups)) { + defaultTableInfo.columns = OITeamColumns; + defaultTableInfo.sort = [ + { field: "receivedDate", sort: "desc" }, // Default sorting for OI team + ]; + } + return defaultTableInfo; }; diff --git a/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/SearchComponent.js b/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/SearchComponent.js index 510da92b1..93eacfa3d 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/SearchComponent.js +++ b/forms-flow-web/src/components/FOI/Dashboard/Ministry/AdvancedSearch/SearchComponent.js @@ -131,7 +131,8 @@ const AdvancedSearch = ({ userDetail }) => { [StateEnum.review.label]: false, [StateEnum.signoff.label]: false, [StateEnum.closed.label]: false, - [StateEnum.callforrecordsoverdue.label]: false + [StateEnum.callforrecordsoverdue.label]: false, + [StateEnum.onholdother.label]: false }; const [requestState, setRequestState] = useState(() => { if (Object.keys(advancedSearchParams).length > 0 && advancedSearchParams.requestState.length > 0) { @@ -622,7 +623,7 @@ const AdvancedSearch = ({ userDetail }) => { return All; } - return selected.map(value => StateEnum[value].name).join(", "); + return selected.map(value => Object.values(StateEnum).find(state => state.label === value).name).join(", "); }} > @@ -633,11 +634,11 @@ const AdvancedSearch = ({ userDetail }) => { ].includes(key)).map(([key, value]) => ( -1 + requestState.indexOf(value.label) > -1 } color="success" /> diff --git a/forms-flow-web/src/components/FOI/Dashboard/Ministry/Queue.js b/forms-flow-web/src/components/FOI/Dashboard/Ministry/Queue.js index a8b92a7ac..277e5168f 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/Ministry/Queue.js +++ b/forms-flow-web/src/components/FOI/Dashboard/Ministry/Queue.js @@ -90,7 +90,7 @@ const Queue = ({ userDetail, tableInfo }) => { function getRecordsDue(params) { let receivedDateString = params.row.cfrduedate; const currentStatus = params.row.currentState; - if (currentStatus.toLowerCase() === StateEnum.onhold.name.toLowerCase()) { + if (currentStatus.toLowerCase() === StateEnum.onhold.name.toLowerCase() || currentStatus.toLowerCase() === StateEnum.onholdother.name.toLowerCase()) { return "N/A"; } else if(!receivedDateString) { return ""; @@ -102,7 +102,7 @@ const Queue = ({ userDetail, tableInfo }) => { function getLDD(params) { let receivedDateString = params.row.duedate; const currentStatus = params.row.currentState; - if (currentStatus.toLowerCase() === StateEnum.onhold.name.toLowerCase()) { + if (currentStatus.toLowerCase() === StateEnum.onhold.name.toLowerCase() || currentStatus.toLowerCase() === StateEnum.onholdother.name.toLowerCase()) { return "N/A"; } else if(!receivedDateString) { return ""; diff --git a/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/ActionContext.js b/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/ActionContext.js new file mode 100644 index 000000000..d02c2b7f5 --- /dev/null +++ b/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/ActionContext.js @@ -0,0 +1,68 @@ +import React, { createContext, useEffect, useState } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { fetchFOIProgramAreaList } from "../../../../../apiManager/services/FOI/foiMasterDataServices"; +import { fetchAdvancedSearchData } from "../../../../../apiManager/services/FOI/foiAdvancedSearchServices"; +import { errorToast } from "../../../../../helper/FOI/helper"; +import { setAdvancedSearchParams } from "../../../../../actions/FOI/foiRequestActions"; +export const ActionContext = createContext(); +ActionContext.displayName = "AdvancedSearchContext"; +export const ActionProvider = ({ children }) => { + const dispatch = useDispatch(); + + const [queryData, setQueryData] = useState(null); + const [searchLoading, setSearchLoading] = useState(false); + const [advancedSearchComponentLoading, setAdvancedSearchComponentLoading] = + useState(true); + const [searchResults, setSearchResults] = useState(null); + const advancedSearchParams = useSelector((state) => state.foiRequests.foiAdvancedSearchParams); + + const handleUpdateSearchFilter = (filterData) => { + dispatch(setAdvancedSearchParams(filterData)) + setQueryData({ ...(queryData || {}), ...filterData }); + }; + + const defaultSortModel = [ + { field: "currentState", sort: "desc" }, + ]; + + useEffect(() => { + dispatch(fetchFOIProgramAreaList()); + }, [dispatch]); + + useEffect(() => { + if (queryData) { + fetchAdvancedSearchData({ + ...queryData, + callback: (data) => { + setSearchLoading(false); + setAdvancedSearchComponentLoading(false); + setSearchResults(data); + }, + errorCallback: (error) => { + setSearchLoading(false); + setAdvancedSearchComponentLoading(false); + errorToast(error); + }, + dispatch, + }); + } + }, [queryData]); + + return ( + + {children} + + ); +}; diff --git a/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/DataGridAdvancedSearch.js b/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/DataGridAdvancedSearch.js new file mode 100644 index 000000000..fa6955135 --- /dev/null +++ b/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/DataGridAdvancedSearch.js @@ -0,0 +1,237 @@ +import React, { useEffect, useContext } from "react"; +import { DataGrid } from '@mui/x-data-grid'; +import "../../dashboard.scss"; +import useStyles from "../../CustomStyle"; +import Loading from "../../../../../containers/Loading"; +import Grid from "@mui/material/Grid"; +import { + updateSortModel, + getLDD, + getRecordsDue, + LightTooltip, + displayQueueFlagIcons +} from "../../utils"; +import { ActionContext } from "./ActionContext"; +import { ConditionalComponent } from "../../../../../helper/FOI/helper"; +import { useDispatch } from "react-redux"; +import Link from "@mui/material/Link"; +import { push } from "connected-react-router"; +import { CustomFooter } from "../../CustomFooter" + +const DataGridAdvancedSearch = ({ userDetail }) => { + const dispatch = useDispatch(); + + const { + handleUpdateSearchFilter, + searchResults, + searchLoading, + queryData, + setSearchLoading, + advancedSearchComponentLoading, + advancedSearchParams, + } = useContext(ActionContext); + + const classes = useStyles(); + + const defaultRowsState = { page: 0, pageSize: 100 }; + const [rowsState, setRowsState] = React.useState( + Object.keys(advancedSearchParams).length > 0 ? + {page: advancedSearchParams.page - 1, pageSize: advancedSearchParams.size} : + defaultRowsState + ); + + const defaultSortModel = [ + { field: "currentState", sort: "desc" }, + // { field: "receivedDateUF", sort: "desc" }, + ]; + const [sortModel, setSortModel] = React.useState(advancedSearchParams?.sort || defaultSortModel); + + useEffect(() => { + if (searchResults) { + setSearchLoading(true); + // page+1 here, because initial page value is 0 for mui-data-grid + handleUpdateSearchFilter({ + page: rowsState.page + 1, + size: rowsState.pageSize, + sort: updateSortModel(sortModel), + userId: userDetail.preferred_username, + }); + } + }, [rowsState, sortModel]); + + const hyperlinkTooltipRenderCellforMinistry = (params) => { + let link; + link = "./ministryreview/" + params.row.id + "/ministryrequest/" + params.row.ministryrequestid; + let description = params.row.description; + if (params.row.fromdate && params.row.todate) { + description += "\n(" + (new Date(params.row.fromdate)).toLocaleDateString() + " to " + (new Date(params.row.todate)).toLocaleDateString() + ")" + } + return ( + {description} + + }> + + renderReviewRequestforMinistry(e, params.row)}> +
{params.value}
+
+
+ ) + }; + + const hyperlinkRenderCellforMinistry = (params) => { + let link; + link = "./ministryreview/" + params.row.id + "/ministryrequest/" + params.row.ministryrequestid; + return ( + renderReviewRequestforMinistry(e, params.row)}> +
{params.value}
+ + ) + }; + + const renderReviewRequestforMinistry = (e, row) => { + e.preventDefault() + dispatch(push(`/foi/ministryreview/${row.id}/ministryrequest/${row.ministryrequestid}`)); + }; + + const columns = React.useRef([ + { + field: "flags", + headerName: "FLAGS", + headerAlign: "left", + renderCell: displayQueueFlagIcons, + }, + { + field: "axisRequestId", + headerName: "ID NUMBER", + width: 170, + headerAlign: "left", + renderCell: hyperlinkTooltipRenderCellforMinistry, + cellClassName: 'foi-advanced-search-result-cell', + }, + { + field: "applicantcategory", + headerName: "CATEGORY", + flex: 1, + headerAlign: "left", + renderCell: hyperlinkRenderCellforMinistry, + cellClassName: 'foi-advanced-search-result-cell', + }, + { + field: "requestType", + headerName: "TYPE", + flex: 1, + headerAlign: "left", + renderCell: hyperlinkRenderCellforMinistry, + cellClassName: 'foi-advanced-search-result-cell', + }, + + { + field: "currentState", + headerName: "REQUEST STATE", + flex: 1, + headerAlign: "left", + renderCell: hyperlinkRenderCellforMinistry, + cellClassName: 'foi-advanced-search-result-cell', + }, + + { + field: "ministryAssignedToFormatted", + headerName: "ASSIGNED TO", + flex: 1, + headerAlign: "left", + renderCell: hyperlinkRenderCellforMinistry, + cellClassName: 'foi-advanced-search-result-cell', + }, + { + field: "CFRDueDateValue", + headerName: "RECORDS DUE", + flex: 1, + headerAlign: "left", + renderCell: hyperlinkRenderCellforMinistry, + cellClassName: 'foi-advanced-search-result-cell', + valueGetter: getRecordsDue, + }, + { + field: "DueDateValue", + headerName: "LDD", + flex: 1, + headerAlign: "left", + renderCell: hyperlinkRenderCellforMinistry, + cellClassName: 'foi-advanced-search-result-cell', + valueGetter: getLDD, + }, + { + field: "cfrduedate", + headerName: "", + width: 0, + hide: true, + renderCell: (_params) => , + }, + ]); + + if (advancedSearchComponentLoading && queryData) { + return ( + + + + ); + } + + return ( + + + +

Search Results

+
+ + row.idNumber} + rows={searchResults?.data || []} + columns={columns.current} + rowHeight={30} + headerHeight={50} + rowCount={searchResults?.meta?.total || 0} + pageSize={rowsState.pageSize} + // rowsPerPageOptions={[10]} + hideFooterSelectedRowCount={true} + disableColumnMenu={true} + pagination + paginationMode="server" + initialState={{ + pagination: rowsState + }} + onPageChange={(newPage) => setRowsState((prev) => ({ ...prev, page: newPage }))} + onPageSizeChange={(newpageSize) => + setRowsState((prev) => ({ ...prev, pageSize: newpageSize })) + } + components={{ + Footer: ()=> + }} + sortingOrder={["desc", "asc"]} + sortModel={[sortModel[0]]} + sortingMode={"server"} + onSortModelChange={(model) => setSortModel(model)} + getRowClassName={(params) => + `super-app-theme--${params.row.currentState + .toLowerCase() + .replace(/ +/g, "")}` + } + loading={searchLoading} + /> + +
+
+ ); +}; + +export default DataGridAdvancedSearch; diff --git a/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/SearchComponent.js b/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/SearchComponent.js new file mode 100644 index 000000000..2b97414b9 --- /dev/null +++ b/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/SearchComponent.js @@ -0,0 +1,1065 @@ +import React, { useState, useContext, useEffect } from "react"; +import "../../dashboard.scss"; +import { useSelector } from "react-redux"; + +import Loading from "../../../../../containers/Loading"; +import Grid from "@mui/material/Grid"; +import Paper from "@mui/material/Paper"; +import InputBase from "@mui/material/InputBase"; +import IconButton from "@mui/material/IconButton"; +import Typography from "@mui/material/Typography"; +import TextField from "@mui/material/TextField"; +import Chip from "@mui/material/Chip"; +import SearchIcon from "@mui/icons-material/Search"; +import { makeStyles } from "@material-ui/core/styles"; +import FormGroup from "@mui/material/FormGroup"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import Checkbox from "@mui/material/Checkbox"; +import Button from "@mui/material/Button"; +import Menu from "@mui/material/Menu"; +import MenuItem from "@mui/material/MenuItem"; +import InputAdornment from "@mui/material/InputAdornment"; + +import OutlinedInput from "@mui/material/OutlinedInput"; +import InputLabel from "@mui/material/InputLabel"; +import FormControl from "@mui/material/FormControl"; +import ListItemText from "@mui/material/ListItemText"; +import Select from "@mui/material/Select"; +import { SearchFilter, DateRangeTypes } from "./enum"; +import { + ConditionalComponent, + formatDate, +} from "../../../../../helper/FOI/helper"; +import { ActionContext } from "./ActionContext"; +import { StateEnum } from "../../../../../constants/FOI/statusEnum"; + +import Tooltip from '../../../customComponents/Tooltip/Tooltip'; + +import { + addYears +} from "../../utils"; + +const DEFAULT_PAGE_SIZE = 100; + +const ITEM_HEIGHT = 48; +const ITEM_PADDING_TOP = 8; +const MenuProps = { + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + width: 250, + }, + }, + MenuListProps: { + autoFocusItem: false, + }, +}; + +const useStyles = makeStyles((_theme) => ({ + search: { + borderBottom: "1px solid #38598A", + backgroundColor: "rgba(56,89,138,0.1)", + }, + searchBody: { + padding: "2em", + }, + label: { + marginBottom: "1em", + }, + chip: { + color: "#38598A", + border: "1px solid #38598A", + width: "100%", + }, + paper: { + backgroundColor: "#F9FBFD", + }, + checkboxLabel: { + marginBottom: 0, + }, +})); + +const AdvancedSearch = ({ userDetail }) => { + const classes = useStyles(); + + const { + handleUpdateSearchFilter, + searchLoading, + defaultSortModel, + advancedSearchComponentLoading, + setAdvancedSearchComponentLoading, + setSearchLoading, + advancedSearchParams, + } = useContext(ActionContext); + + const programAreaList = useSelector( + (state) => state.foiRequests.foiProgramAreaList + ); + + const isLoading = useSelector((state) => state.foiRequests.isLoading); + + const tooltipContentRight = { + "title": "Advanced Search", + "content": "To conduct an Advanced Search using one of the seven filter buttons, you must also enter one or more key words." + }; + + const tooltipContentLeft = { + "title": "Advanced Search", + "content": "Use one or more fields from the following sections on their own or to narrow your search: Request State/Status/Type/Flags, Date Range, or Public Body." + }; + + const [searchFilterSelected, setSearchFilterSelected] = useState(advancedSearchParams?.search || null); + const keywordsMode = searchFilterSelected === SearchFilter.REQUEST_DESCRIPTION; + + const [searchText, setSearchText] = useState(() => { + if (!keywordsMode && Object.keys(advancedSearchParams).length > 0 && advancedSearchParams.keywords.length > 0) { + return advancedSearchParams.keywords[0] + } else { + return ""; + } + }); + const [keywords, setKeywords] = useState(() => { + if (keywordsMode && Object.keys(advancedSearchParams).length > 0 && advancedSearchParams.keywords.length > 0) { + return advancedSearchParams.keywords; + } else { + return []; + } + }); + + const intitialRequestState = { + [StateEnum.callforrecords.label]: false, + [StateEnum.review.label]: false, + [StateEnum.signoff.label]: false, + [StateEnum.closed.label]: false, + [StateEnum.callforrecordsoverdue.label]: false + }; + const [requestState, setRequestState] = useState(() => { + if (Object.keys(advancedSearchParams).length > 0 && advancedSearchParams.requestState.length > 0) { + let savedRequestState = {...intitialRequestState} + advancedSearchParams.requestState.forEach(state => { + savedRequestState[state] = true; + }); + return savedRequestState; + } else { + return intitialRequestState; + } + }); + + const intitialRequestStatus = { + overdue: false, + ontime: false, + }; + + const [requestStatus, setRequestStatus] = useState(() => { + if (Object.keys(advancedSearchParams).length > 0 && advancedSearchParams.requestStatus.length > 0) { + let savedRequestStatus = { ...intitialRequestStatus } + advancedSearchParams.requestStatus.forEach(status => { + savedRequestStatus[status] = true; + }); + return savedRequestStatus; + } else { + return intitialRequestStatus; + } + }); + + const initialRequestTypes = { + personal: false, + general: false, + generaldisabled: false, + }; + + const [requestTypes, setRequestTypes] = useState(() => { + if (Object.keys(advancedSearchParams).length > 0 && advancedSearchParams.requestType.length > 0) { + let savedRequestType = {...initialRequestTypes} + advancedSearchParams.requestType.forEach(type => { + savedRequestType[type] = true; + }); + if (advancedSearchParams.search === SearchFilter.APPLICANT_NAME) { + savedRequestType.generaldisabled = true; + } + return savedRequestType; + } else { + return initialRequestTypes; + } + }); + + const initialRequestFlags = { + restricted: false, + oipc: false, + phased: false + }; + const [requestFlags, setRequestFlags] = useState(() => { + if (Object.keys(advancedSearchParams).length > 0 && advancedSearchParams.requestFlags.length > 0) { + let savedRequestFlags = {...initialRequestFlags} + advancedSearchParams.requestFlags.forEach(type => { + savedRequestFlags[type] = true; + }); + return savedRequestFlags; + } else { + return initialRequestFlags; + } + }); + const [selectedDateRangeType, setSelectedDateRangeType] = useState(advancedSearchParams?.dateRangeType || ""); + const [fromDate, setFromDate] = useState(advancedSearchParams?.fromDate || ""); + const [toDate, setToDate] = useState(advancedSearchParams?.toDate || ""); + const oneYearFromNow = formatDate(addYears(1)); + //default max fromDate - now + const [maxFromDate, setMaxFromDate] = useState(formatDate(new Date())); + //default max toDate - 1 year from now + const [maxToDate, setMaxToDate] = useState(oneYearFromNow); + const resetDateFields = () => { + setFromDate(""); + setToDate(""); + } + const resetMaxFromDate = (dateRangeType) => { + if(dateRangeType == 'receivedDate' || dateRangeType == 'closedate') { + setMaxFromDate(formatDate(new Date())); + }else{ + setMaxFromDate(oneYearFromNow); + } + } + const resetMaxToDate = (dateRangeType) => { + if(dateRangeType == 'receivedDate' || dateRangeType == 'closedate') { + setMaxToDate(formatDate(new Date())); + }else{ + setMaxToDate(oneYearFromNow); + } + } + + const [selectedPublicBodies, setSelectedPublicBodies] = useState(advancedSearchParams?.publicBodies || []); + + const [anchorEl, setAnchorEl] = React.useState(null); + const open = Boolean(anchorEl) && Boolean(searchText); + + const getTrueKeysFromCheckboxObject = (checkboxObject) => { + return Object.entries(checkboxObject) + .map(([key, value]) => { + if (key !== 'generaldisabled') { + return value ? key : null; + } + }) + .filter((value) => value); + }; + const handleApplySearchFilters = () => { + if (!advancedSearchComponentLoading) { + setAdvancedSearchComponentLoading(true); + } + setSearchLoading(true); + handleUpdateSearchFilter({ + search: searchFilterSelected, + keywords: keywordsMode ? keywords : [searchText.trim()], + requestState: getTrueKeysFromCheckboxObject(requestState), + requestType: getTrueKeysFromCheckboxObject(requestTypes), + requestFlags: getTrueKeysFromCheckboxObject(requestFlags), + requestStatus: getTrueKeysFromCheckboxObject(requestStatus), + dateRangeType: selectedDateRangeType || null, + fromDate: fromDate || null, + toDate: toDate || null, + publicBodies: selectedPublicBodies, + page: 1, + size: advancedSearchParams?.size || DEFAULT_PAGE_SIZE, + sort: defaultSortModel, + userId: userDetail.preferred_username, + }); + }; + + useEffect(() => { + if (Object.keys(advancedSearchParams).length > 0) { + if (!advancedSearchComponentLoading) { + setAdvancedSearchComponentLoading(true); + } + setSearchLoading(true); + handleUpdateSearchFilter(advancedSearchParams) + } + }, []); + + const noSearchCriteria = () => { + let selectedRequestStates = getTrueKeysFromCheckboxObject(requestState); + let selectedRequestTypes = getTrueKeysFromCheckboxObject(requestTypes); + let selectedRequestFlags = getTrueKeysFromCheckboxObject(requestFlags); + let selectedRequestStatus = getTrueKeysFromCheckboxObject(requestStatus); + return ((keywords.length===0 && keywordsMode) || (!searchText && !keywordsMode)) + && !fromDate + && !toDate + && selectedPublicBodies.length===0 + && selectedRequestStates.length===0 + && selectedRequestTypes.length===0 + && selectedRequestFlags.length===0 + && selectedRequestStatus.length===0; + }; + + const handleResetSearchFilters = () => { + setSearchText(""); + setSelectedDateRangeType(""); + setKeywords([]); + setSearchFilterSelected(); + setRequestState(intitialRequestState); + setRequestTypes(initialRequestTypes); + setRequestFlags(initialRequestFlags); + setRequestStatus(intitialRequestStatus); + setFromDate(""); + setToDate(""); + setSelectedPublicBodies([]); + }; + + const handleKeywordAdd = () => { + if (!searchText) { + return; + } + setAnchorEl(null); + setKeywords([...keywords, searchText.trim()]); + setSearchText(""); + }; + + const handleSearchChange = (e) => { + setAnchorEl(e.currentTarget); + setSearchText(e.target.value); + }; + + const clickSearchFilter = (SearchFilterType) => { + if (SearchFilterType === SearchFilter.APPLICANT_NAME) { + setRequestTypes({ + personal:true, + general:false, + generaldisabled: true + }); + } + else { + setRequestTypes({ + ...requestTypes, + generaldisabled: false + }); + } + if (searchFilterSelected !== SearchFilterType) { + setSearchFilterSelected(SearchFilterType); + } + }; + + const handleRequestStateChange = (event) => { + setRequestState({ + ...requestState, + [event.target.parentElement.getAttribute('stateid')]: event.target.checked + }); + }; + + const handleRequestStatusChange = (event) => { + setRequestStatus({ + ...requestStatus, + [event.target.name]: event.target.checked, + }); + }; + + const handleRequestTypeChange = (event) => { + setRequestTypes({ + ...requestTypes, + [event.target.name]: event.target.checked, + }); + }; + + const handleRequestFlagsChange = (event) => { + setRequestFlags({ + ...requestFlags, + [event.target.name]: event.target.checked, + }); + }; + + const handleSelectedDateRangeTypeChange = (event) => { + const type = event.target.value; + setSelectedDateRangeType(type); + resetMaxFromDate(type); + resetMaxToDate(type); + resetDateFields(); + }; + + const handleSelectedPublicBodiesChange = (event) => { + const { + target: { value }, + } = event; + setSelectedPublicBodies( + // On autofill we get a stringified value. + typeof value === "string" ? value.split(",") : value + ); + }; + + const ClickableChip = ({ clicked, ...rest }) => { + if (!clicked) { + return ( + + ); + } + + return ( + + ); + }; + + if (isLoading) { + return ( + + + + ); + } + return ( + <> + + + + + + + { + if (keywordsMode && e.key === "Enter") { + handleKeywordAdd(); + } + }} + startAdornment={ + + + Search + + + + } + sx={{ + color: "#38598A", + }} + fullWidth + /> + + + + {keywords.map((keyword, index) => ( + { + setKeywords(keywords.filter((_kw, i) => index !== i)); + }} + color="primary" + sx={{ + backgroundColor: "#38598A", + margin: "1px", + }} + size="small" + /> + ))} + + + + + + + {`Add "${searchText}"`} + + + + + + + + Filter by + + + + + + clickSearchFilter(SearchFilter.REQUEST_DESCRIPTION) + } + clicked={ + searchFilterSelected === SearchFilter.REQUEST_DESCRIPTION + } + /> + + + + clickSearchFilter(SearchFilter.ID_NUM)} + clicked={searchFilterSelected === SearchFilter.ID_NUM} + /> + + + + + clickSearchFilter(SearchFilter.AXIS_REQUEST_NUM) + } + clicked={ + searchFilterSelected === SearchFilter.AXIS_REQUEST_NUM + } + /> + + + + clickSearchFilter(SearchFilter.APPLICANT_NAME)} + clicked={searchFilterSelected === SearchFilter.APPLICANT_NAME} + /> + + + + clickSearchFilter(SearchFilter.ASSIGNEE_NAME)} + clicked={searchFilterSelected === SearchFilter.ASSIGNEE_NAME} + /> + + + + clickSearchFilter(SearchFilter.SUBJECT_CODE)} + clicked={searchFilterSelected === SearchFilter.SUBJECT_CODE} + /> + + + + clickSearchFilter(SearchFilter.OIPC_NUMBER)} + clicked={searchFilterSelected === SearchFilter.OIPC_NUMBER} + /> + + + + + + + Request State + + + + + + } + label="Call for Records" + /> + + } + label="Records Review" + /> + + } + label="Ministry Sign Off" + /> + + } + label="Closed" + /> + + + + + + + + Request Status + + + + + + } + label="Overdue" + /> + + } + label="On Time" + /> + + + + + + + + Request Type + + + + + + + } + label="Personal" + /> + + } + label={
General {requestTypes.generaldisabled ?

*

: ""}
} + /> +
+
+ + + + Request Flags + + + + + + + } + label="Restricted" + /> + + } + label="OIPC" + /> + + } + label="Phased" + /> + + +
+ + + + + Search by Date Range + + + + + + + Type of Date Range + + + + + + + + setFromDate(formatDate(e.target.value))} + disabled={!selectedDateRangeType} + fullWidth + /> + + + + to + + + + + setToDate(formatDate(e.target.value))} + disabled={!selectedDateRangeType} + variant="outlined" + fullWidth + /> + + + + + + Search by Public Body + + + + + + + Public Body + + + + + + + * You are unable to search General Requests with Applicant Name Selected.
+ Deselect Applicant Name Search Filter if you wish to search General Requests. +
+
+
+ + + + + + + + + +
+
+
+
+ + + + + + + + + + ); +}; + +export default AdvancedSearch; diff --git a/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/enum.js b/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/enum.js new file mode 100644 index 000000000..4afb63782 --- /dev/null +++ b/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/enum.js @@ -0,0 +1,21 @@ +const SearchFilter = Object.freeze({ + REQUEST_DESCRIPTION: "requestdescription", + RAW_REQUEST_NUM: "rawrequest_num", + ID_NUM: "idnumber", + AXIS_REQUEST_NUM: "axisrequest_number", + APPLICANT_NAME: "applicantname", + ASSIGNEE_NAME: "ministryassigneename", + SUBJECT_CODE: "subjectcode", + OIPC_NUMBER: "oipc_number" +}); + +const DateRangeTypes = Object.freeze([ + {name: "receivedDate", value: "Received Date"}, + {name: "duedate", value: "Legislated Due Date"}, + {name: "closedate", value: "Closed Date"}, +]); + +export { + SearchFilter, + DateRangeTypes +}; \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/index.js b/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/index.js new file mode 100644 index 000000000..e3c7c571d --- /dev/null +++ b/forms-flow-web/src/components/FOI/Dashboard/OI/AdvancedSearch/index.js @@ -0,0 +1,15 @@ +import React from "react"; +import { ActionProvider } from "./ActionContext"; +import SearchComponent from "./SearchComponent"; +import DataGridAdvancedSearch from "./DataGridAdvancedSearch"; + +const AdvancedSearch = React.memo(({ userDetail }) => { + return ( + + + + + ); +}); + +export default AdvancedSearch; diff --git a/forms-flow-web/src/components/FOI/Dashboard/OI/OIDashboard.js b/forms-flow-web/src/components/FOI/Dashboard/OI/OIDashboard.js new file mode 100644 index 000000000..250d11cb3 --- /dev/null +++ b/forms-flow-web/src/components/FOI/Dashboard/OI/OIDashboard.js @@ -0,0 +1,189 @@ +import React from "react"; +import { useDispatch, useSelector } from "react-redux"; + +import "../dashboard.scss"; +import Grid from "@mui/material/Grid"; +import { makeStyles } from "@material-ui/core/styles"; +import Queue from "./Queue"; +import AdvancedSearch from "./AdvancedSearch"; +import clsx from "clsx"; +import Divider from "@mui/material/Divider"; +import { ButtonBase } from "@mui/material"; +import { setShowAdvancedSearch, setResumeDefaultSorting } from "../../../../actions/FOI/foiRequestActions"; + +const useStyles = makeStyles(() => ({ + displayed: { + display: "flex", + marginTop: "2em", + }, + hidden: { + display: "none !important", + }, + disabledTitle: { + opacity: "0.3", + }, +})); + +const OIDashboard = ({ userDetail }) => { + const classes = useStyles(); + const showAdvancedSearch = useSelector((state) => state.foiRequests.showAdvancedSearch) + const tableInfo = { + sort: [ + { field: "defaultSorting", sort: "asc" }, + // { field: "cfrduedate", sort: "asc" } + ] + }; + const dispatch = useDispatch(); + + React.useEffect(() => { + if (showAdvancedSearch) { + document.title = 'FOI Advanced Search' + } + // else if (showEventQueue) { + // document.title = 'Event Queue' + // } + else { + document.title = 'FOI Request Queue' + } + }, [showAdvancedSearch]); + + return ( +
+ + + + { + dispatch(setShowAdvancedSearch(false)); + dispatch(setResumeDefaultSorting(true)); + }} + disableRipple + > +

+ Your FOI Request Queue - OI +

+
+ + { + // dispatch(setShowEventQueue(true)); + dispatch(setShowAdvancedSearch(false)); + //dispatch(setResumeDefaultSorting(true)); + }} + disableRipple + > + + + { + //dispatch(setShowAdvancedSearch(true)); + //dispatch(setShowEventQueue(false)); + dispatch(setResumeDefaultSorting(true)); + }} + disableRipple + > +

+ Advanced Search +

+
+
+
+ { (!showAdvancedSearch) && + + + + } + {/* { showEventQueue && + + + + } */} + {/* { showAdvancedSearch && + + + + } */} +
+
+ ); +}; + +export default OIDashboard; diff --git a/forms-flow-web/src/components/FOI/Dashboard/OI/Queue.js b/forms-flow-web/src/components/FOI/Dashboard/OI/Queue.js new file mode 100644 index 000000000..3c27f82cc --- /dev/null +++ b/forms-flow-web/src/components/FOI/Dashboard/OI/Queue.js @@ -0,0 +1,363 @@ +import React, { useEffect, useMemo } from "react" +import { DataGrid } from "@mui/x-data-grid"; +import "../dashboard.scss"; +import useStyles from "../CustomStyle"; +import { useDispatch, useSelector } from "react-redux"; +import { push } from "connected-react-router"; +import { fetchFOIOIRequestListByPage } from "../../../../apiManager/services/FOI/foiRequestServices"; +import Loading from "../../../../containers/Loading"; +import { debounce, ClickableChip, } from "../utils"; +import Grid from "@mui/material/Grid"; +import Stack from "@mui/material/Stack"; +import SearchIcon from "@material-ui/icons/Search"; +import InputAdornment from "@mui/material/InputAdornment"; +import InputBase from "@mui/material/InputBase"; +import IconButton from "@mui/material/IconButton"; +import Paper from "@mui/material/Paper"; +import { setQueueFilter, setQueueParams } from "../../../../actions/FOI/foiRequestActions"; +import { CustomFooter } from "../CustomFooter" + +const Queue = ({ userDetail, tableInfo }) => { + const dispatch = useDispatch(); + + const requestQueue = useSelector( + (state) => state.foiRequests.foiMinistryRequestsList + ); + + const isLoading = useSelector((state) => state.foiRequests.isLoading); + + const classes = useStyles(); + + const filterFields = [ + "applicantcategory", + "requestType", + "idNumber", + "axisRequestId", + "currentState", + "assignedministrypersonLastName", + "assignedministrypersonFirstName", + ]; + + const queueParams = useSelector((state) => state.foiRequests.queueParams); + const rowsState = useSelector((state) => state.foiRequests.queueParams?.rowsState); + const sortModel = useSelector((state) => state.foiRequests.queueParams?.sortModel || tableInfo.sort); + + let serverSortModel; + + const keyword = useSelector((state) => state.foiRequests.queueParams?.keyword); + const requestFilter = useSelector((state) => state.foiRequests.queueFilter); + + // update sortModel for records due, ldd & assignedTo + const updateSortModel = () => { + let smodel = JSON.parse(JSON.stringify(sortModel)); + if (smodel) { + smodel.map((row) => { + if (row.field === "CFRDueDateValue") { + row.field = "cfrduedate"; + } + if (row.field === "DueDateValue"){ + row.field = "duedate"; + } + }); + + //add cfrduedate asc to default sorting + if(smodel[0]?.field === "defaultSorting") { + smodel.push( + { field: "receivedDate", sort: "asc" }, + ); + } + } + + return smodel; + }; + + useEffect(() => { + serverSortModel = updateSortModel(); + // page+1 here, because initial page value is 0 for mui-data-grid + dispatch( + fetchFOIOIRequestListByPage( + rowsState.page + 1, + rowsState.pageSize, + serverSortModel, + filterFields, + keyword, + requestFilter, + userDetail.preferred_username + ) + ); + }, [rowsState, sortModel, keyword, requestFilter]); + + const columnsRef = React.useRef(tableInfo?.columns || []); + + const columns = React.useRef([ + { + field: "receivedDate", + headerName: "RECEIVED DATE", + flex: 1, + headerAlign: "left" + }, + { + field: "axisRequestId", + headerName: "ID NUMBER", + flex: 1, + headerAlign: "left" + }, + { + field: "requestType", + headerName: "TYPE", + flex: 1, + headerAlign: "left", + }, + { + field: "recordspagecount", + headerName: "PAGES", + flex: 1, + headerAlign: "left", + }, + { + field: "publicationStatus", + headerName: "PUBLICATION STATUS", + flex: 1, + headerAlign: "left", + }, + { + field: "fromClosed", + headerName: "FROM CLOSED", + flex: 1, + headerAlign: "left", + }, + { + field: "publicationDate", + headerName: "PUBLICATION DATE", + flex: 1, + headerAlign: "left", + }, + { + field: "assignedTo", + headerName: "ASSIGNEE", + flex: 1, + headerAlign: "left", + }, + { + field: "applicantType", + headerName: "APPLICANT TYPE", + flex: 1, + headerAlign: "left", + } + // { + // field: "ministryAssignedToFormatted", + // headerName: "ASSIGNED TO", + // flex: 1, + // headerAlign: "left", + // }, + // { + // field: "CFRDueDateValue", + // headerName: "RECORDS DUE", + // flex: 1, + // headerAlign: "left", + // valueGetter: getRecordsDue, + // }, + // { + // field: "DueDateValue", + // headerName: "LDD", + // flex: 1, + // headerAlign: "left", + // valueGetter: getLDD, + // }, + // { + // field: "cfrduedate", + // headerName: "", + // width: 0, + // hide: true, + // renderCell: (_params) => , + // } + ]); + + const requestFilterChange = (filter) => { + if (filter === requestFilter) { + return; + } + dispatch(setQueueParams({...queueParams, rowsState: {...rowsState, page: 0}})); + dispatch(setQueueFilter(filter)); + }; + + const setSearch = debounce((e) => { + dispatch(setQueueParams({ + ...queueParams, + keyword: e.target.value.trim(), + rowsState: {...rowsState, page: 0} + })); + }, 500); + + const rows = useMemo(() => { + return requestQueue?.data || []; + }, [JSON.stringify(requestQueue)]); + + const renderReviewRequest = (e) => { + if (e.row.ministryrequestid) { + dispatch( + push( + `/foi/foirequests/${e.row.id}/ministryrequest/${e.row.ministryrequestid}` + ) + ); + } else { + dispatch(push(`/foi/reviewrequest/${e.row.id}`)); + } + }; + + if (requestQueue === null) { + return ( + + + + ); + } + + const handleSortChange = (model) => { + if (model.length === 0) { + return; + } + dispatch(setQueueParams({...queueParams, sortModel: model})); + }; + + return ( + <> + + + + + + + Search in Queue + + + + } + fullWidth + /> + + + + requestFilterChange("myRequests")} + clicked={requestFilter === "myRequests"} + /> + requestFilterChange("All")} + clicked={requestFilter === "All"} + /> + requestFilterChange("watchingRequests")} + clicked={requestFilter === "watchingRequests"} + /> + { + requestFilterChange("unassignedRequests") + }} + clicked={requestFilter === "unassignedRequests"} + /> + + + + + + row.idNumber} + rows={rows} + columns={columns.current} + rowHeight={30} + headerHeight={50} + rowCount={requestQueue?.meta?.total || 0} + pageSize={rowsState?.pageSize} + // rowsPerPageOptions={[10,20,50,100]} + hideFooterSelectedRowCount={true} + disableColumnMenu={true} + pagination + paginationMode="server" + page={rowsState?.page} + onPageChange={(newPage) => dispatch(setQueueParams({...queueParams, rowsState: {...rowsState, page: newPage}}))} + onPageSizeChange={(newpageSize) => + dispatch(setQueueParams({...queueParams, rowsState: {...rowsState, pageSize: newpageSize}})) + } + components={{ + Footer: ()=> + }} + sortingOrder={["desc", "asc"]} + sortModel={[sortModel[0]]} + sortingMode={"server"} + onSortModelChange={(model) => { + if (model.length > 0) { + dispatch(setQueueParams({...queueParams, sortModel: model})); + } + }} + getRowClassName={(params) => (params.row.assignedministryperson == null) && "not-assigned"} + onRowClick={renderReviewRequest} + loading={isLoading} + /> + + + ); +}; + +export default Queue; diff --git a/forms-flow-web/src/components/FOI/Dashboard/index.js b/forms-flow-web/src/components/FOI/Dashboard/index.js index 1be1ea659..a49b24d1b 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/index.js +++ b/forms-flow-web/src/components/FOI/Dashboard/index.js @@ -1,4 +1,6 @@ import Dashboard from "./IAO/Dashboard"; import MinistryDashboard from "./Ministry/MinistryDashboard"; -export { Dashboard, MinistryDashboard }; \ No newline at end of file +//import OIDashboard from "./OI/OIDashboard"; +export { Dashboard, MinistryDashboard }; +// export { Dashboard, MinistryDashboard, OIDashboard }; \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/Dashboard/utils.js b/forms-flow-web/src/components/FOI/Dashboard/utils.js index 086411d30..827e11488 100644 --- a/forms-flow-web/src/components/FOI/Dashboard/utils.js +++ b/forms-flow-web/src/components/FOI/Dashboard/utils.js @@ -133,7 +133,7 @@ export const onBehalfFullName = (params) => { export const getRecordsDue = (params) => { let receivedDateString = params.row.cfrduedate; const currentStatus = params.row.currentState; - if (currentStatus.toLowerCase() === StateEnum.onhold.name.toLowerCase()) { + if (currentStatus.toLowerCase() === StateEnum.onhold.name.toLowerCase() || currentStatus.toLowerCase() === StateEnum.onholdother.name.toLowerCase()) { return "N/A"; } else if(!receivedDateString) { return ""; @@ -145,7 +145,7 @@ export const getRecordsDue = (params) => { export const getLDD = (params) => { let receivedDateString = params.row.duedate; const currentStatus = params.row.currentState; - if (currentStatus.toLowerCase() === StateEnum.onhold.name.toLowerCase()) { + if (currentStatus.toLowerCase() === StateEnum.onhold.name.toLowerCase()||currentStatus.toLowerCase() === StateEnum.onholdother.name.toLowerCase()) { return "N/A"; } else if(!receivedDateString) { return ""; @@ -158,7 +158,7 @@ export const getDaysLeft = (params) => { const receivedDateString = params.row.duedate; if ( - [StateEnum.onhold.name.toLowerCase(), StateEnum.closed.name.toLowerCase()].includes(params.row.currentState.toLowerCase()) + [StateEnum.onhold.name.toLowerCase(), StateEnum.closed.name.toLowerCase(), StateEnum.onholdother.name.toLowerCase()].includes(params.row.currentState.toLowerCase()) ) { return "N/A"; } else if(!receivedDateString) { diff --git a/forms-flow-web/src/components/FOI/FOIAuthenticateRouting.jsx b/forms-flow-web/src/components/FOI/FOIAuthenticateRouting.jsx index fe1d70fb3..88e5c01a5 100644 --- a/forms-flow-web/src/components/FOI/FOIAuthenticateRouting.jsx +++ b/forms-flow-web/src/components/FOI/FOIAuthenticateRouting.jsx @@ -2,7 +2,6 @@ import React, {useEffect, useState}from "react"; import { Redirect, Route } from "react-router-dom"; import { useDispatch, useSelector } from "react-redux"; import "semantic-ui-css/semantic.min.css"; - import UserService from "../../services/UserService"; import { setUserAuth } from "../../actions/bpmActions"; import Loading from "../../containers/Loading"; @@ -32,7 +31,7 @@ const FOIAuthenticateRouting = React.memo((props) => { } useEffect(()=>{ - console.log('authenticate') + // console.log('authenticate') if(props.store){ UserService.initKeycloak(props.store, (_err, res) => { dispatch(setUserAuth(res.authenticated)); @@ -41,12 +40,12 @@ const FOIAuthenticateRouting = React.memo((props) => { },[props.store, dispatch]); const userDetail = useSelector(state=> state.user.userDetail); const isAuthorized = useSelector(state=> state.user.isAuthorized); + let isMinistry = false; if (Object.entries(userDetail).length !== 0) { const userGroups = userDetail && userDetail.groups.map(group => group.slice(1)); isMinistry = isMinistryLogin(userGroups); } - return ( <> {isAuth && Object.entries(userDetail).length !== 0 ? ( @@ -54,10 +53,11 @@ const FOIAuthenticateRouting = React.memo((props) => { <> - {isMinistry ? - - : - } + {isMinistry ? ( + + ) : ( + + )} diff --git a/forms-flow-web/src/components/FOI/FOIDocumentViewRouting.jsx b/forms-flow-web/src/components/FOI/FOIDocumentViewRouting.jsx index 41f9527aa..e944261ca 100644 --- a/forms-flow-web/src/components/FOI/FOIDocumentViewRouting.jsx +++ b/forms-flow-web/src/components/FOI/FOIDocumentViewRouting.jsx @@ -28,6 +28,8 @@ const FOIDocumentViewRouting = React.memo((props) => { const _isAuth = useSelector((state) => state.user.isAuthenticated); const _queryParams = new URLSearchParams(window.location.search); const _filepath = _queryParams.get('filepath'); + const _filetype = _queryParams.get('filetype') || 'attachments'; + const _bcgovcode = _queryParams.get('bcgovcode'); const _id = _queryParams.get('id'); const renderViewer =()=>{ @@ -37,7 +39,7 @@ const FOIDocumentViewRouting = React.memo((props) => { return ( <> - + 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 bbef03d0e..88ff82044 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/BottomButtonGroup/index.js @@ -100,6 +100,13 @@ const BottomButtonGroup = React.memo( const assignedToList = useSelector( (state) => state.foiRequests.foiAssignedToList ); + + const user = useSelector((reduxState) => reduxState.user.userDetail); + const userGroups = user?.groups?.map(group => group.slice(1)); + + const openInfoStates = useSelector( + (state) => state.foiRequests.oiStatuses + ); const handleClosingDateChange = (cDate) => { setClosingDate(cDate); @@ -307,7 +314,9 @@ const BottomButtonGroup = React.memo( const [documents, setDocuments] = useState([]); const saveStatusId = () => { - if (currentSelectedStatus) { + if (userGroups.includes("OI Team")) { + saveRequestObject.oistatusid = openInfoStates.find(s => s.name === currentSelectedStatus).oistatusid; + } else if (currentSelectedStatus) { switch (currentSelectedStatus) { case StateEnum.closed.name: saveRequestObject.requeststatuslabel = StateEnum.closed.label; @@ -369,12 +378,13 @@ const BottomButtonGroup = React.memo( case StateEnum.peerreview.name: case StateEnum.section5pending.name: case StateEnum.appfeeowing.name: + case StateEnum.onholdother.name: case StateEnum.recordsreadyforreview.name: const status = Object.values(StateEnum).find( (statusValue) => statusValue.name === currentSelectedStatus ); saveRequestObject.requeststatuslabel = status.label; - if (currentSelectedStatus === StateEnum.onhold.name && !saveRequestObject.paymentExpiryDate) { + if ((currentSelectedStatus === StateEnum.onhold.name) && !saveRequestObject.paymentExpiryDate) { saveRequestObject.paymentExpiryDate = dueDateCalculation(new Date(), PAYMENT_EXPIRY_DAYS); } break; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/ExtensionDetails/ExtensionDetailsBox.js b/forms-flow-web/src/components/FOI/FOIRequest/ExtensionDetails/ExtensionDetailsBox.js index 0e32914db..e512144e7 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/ExtensionDetails/ExtensionDetailsBox.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/ExtensionDetails/ExtensionDetailsBox.js @@ -67,7 +67,7 @@ const ExtensionDetailsBox = React.memo(() => { setExtensionId(null); }} disabled={pendingExtensionExists || requestState?.toLowerCase() === StateEnum.onhold.name.toLowerCase() || - requestState?.toLowerCase() === StateEnum.closed.name.toLowerCase()} + requestState?.toLowerCase() === StateEnum.onholdother.name.toLowerCase() || requestState?.toLowerCase() === StateEnum.closed.name.toLowerCase()} > New Extension diff --git a/forms-flow-web/src/components/FOI/FOIRequest/ExtensionDetails/ExtensionsTable.js b/forms-flow-web/src/components/FOI/FOIRequest/ExtensionDetails/ExtensionsTable.js index 08d1293e7..09f914cf4 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/ExtensionDetails/ExtensionsTable.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/ExtensionDetails/ExtensionsTable.js @@ -168,7 +168,7 @@ const ExtensionsTable = ({ showActions = true }) => { disabled={ (index > 0 && extension.extensionstatus !== extensionStatusId.pending) || requestState?.toLowerCase() === StateEnum.onhold.name.toLowerCase() || - requestState?.toLowerCase() === StateEnum.closed.name.toLowerCase() + requestState?.toLowerCase() === StateEnum.onholdother.name.toLowerCase() || requestState?.toLowerCase() === StateEnum.closed.name.toLowerCase() } > diff --git a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js index 23450bed3..565970995 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/FOIRequest.js @@ -31,6 +31,9 @@ import { fetchOIPCStatuses, fetchOIPCReviewtypes, fetchOIPCInquiryoutcomes, + fetchOpenInfoPublicationStatuses, + fetchOpenInfoStatuses, + fetchOpenInfoExemptions, fetchFOICommentTypes } from "../../../apiManager/services/FOI/foiMasterDataServices"; import { @@ -60,7 +63,13 @@ import { fetchPDFStitchedStatusForOIPCRedline, fetchHistoricalRecords, fetchPDFStitchStatusForConsults, + fetchPDFStitchedPackage, + fetchPDFStitchedStatus } from "../../../apiManager/services/FOI/foiRecordServices"; +import { + fetchFOIOpenInfoAdditionalFiles, + fetchFOIOpenInfoRequest, +} from "../../../apiManager/services/FOI/foiOpenInfoRequestServices"; import { makeStyles } from "@material-ui/core/styles"; import FOI_COMPONENT_CONSTANTS from "../../../constants/FOI/foiComponentConstants"; import { push } from "connected-react-router"; @@ -79,6 +88,7 @@ import { confirmChangesLost, getRedirectAfterSaveUrl, getTabBG, + getOITabBG, assignValue, createRequestDetailsObjectFunc, checkContactGiven, @@ -106,14 +116,15 @@ import { toast } from "react-toastify"; import HomeIcon from "@mui/icons-material/Home"; import { RecordsLog } from "../customComponents/Records"; import { UnsavedModal } from "../customComponents"; -import { DISABLE_GATHERINGRECORDS_TAB } from "../../../constants/constants"; +import { DISABLE_GATHERINGRECORDS_TAB, SKIP_OPENINFO_MINISTRIES } from "../../../constants/constants"; import _ from "lodash"; import { MinistryNeedsScanning } from "../../../constants/FOI/enum"; import ApplicantProfileModal from "./ApplicantProfileModal"; -import { setFOIRequestDetail } from "../../../actions/FOI/foiRequestActions"; +import { setFOIRequestDetail, setFOIPDFStitchedOIPackage, setFOIPDFStitchStatusForOIPackage } from "../../../actions/FOI/foiRequestActions"; import OIPCDetails from "./OIPCDetails/Index"; import useOIPCHook from "./OIPCDetails/oipcHook"; import MANDATORY_FOI_REQUEST_FIELDS from "../../../constants/FOI/mandatoryFOIRequestFields"; +import OpenInfo from "./OpenInformation/OpenInfo"; const useStyles = makeStyles((theme) => ({ root: { @@ -187,8 +198,13 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { const [comment, setComment] = useState([]); const [requestState, setRequestState] = useState(StateEnum.unopened.name); const [disableInput, setDisableInput] = useState(requestState?.toLowerCase() === StateEnum.closed.name.toLowerCase() && !requestDetails?.isoipcreview); - const [_tabStatus, settabStatus] = React.useState(requestState); - let foitabheaderBG = getTabBG(_tabStatus, requestState); + const [_tabStatus, settabStatus] = React.useState(requestState); + const isOITeam = userDetail.groups.includes("/OI Team"); + const openInfoStates = useSelector( + (state) => state.foiRequests.oiStatuses + ); + let foitabheaderBG = isOITeam ? getOITabBG(requestDetails?.oistatusid, openInfoStates) : getTabBG(_tabStatus, requestState); + const [unsavedPrompt, setUnsavedPrompt] = useState(false); const [unsavedMessage, setUnsavedMessage] = useState(<>); @@ -252,6 +268,10 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { display: false, active: false, }, + OpenInformation: { + display: false, + active: false, + }, }; const [tabLinksStatuses, setTabLinksStatuses] = useState({ @@ -350,6 +370,8 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { } dispatch(fetchFOICategoryList()); + dispatch(fetchOpenInfoExemptions()); + dispatch(fetchOpenInfoPublicationStatuses()); dispatch(fetchFOIReceivedModeList()); dispatch(fetchFOIDeliveryModeList()); dispatch(fetchFOISubjectCodeList()); @@ -359,10 +381,26 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { dispatch(fetchOIPCStatuses()); dispatch(fetchOIPCReviewtypes()); dispatch(fetchOIPCInquiryoutcomes()); + if (isOITeam) { + dispatch(fetchOpenInfoStatuses()); + dispatch(fetchFOIOpenInfoAdditionalFiles(requestId, ministryId)); + dispatch(fetchPDFStitchedStatus(requestId, ministryId, "openinfo", (err, res) => { + dispatch(setFOIPDFStitchStatusForOIPackage(res)) + })); + dispatch(fetchPDFStitchedPackage(requestId, ministryId, "openinfo", (err, res) => { + dispatch(setFOIPDFStitchedOIPackage(res)) + })); + } if (bcgovcode) dispatch(fetchFOIMinistryAssignedToList(bcgovcode)); }, [requestId, ministryId, comment, attachments]); + useEffect(() => { + if (!SKIP_OPENINFO_MINISTRIES.split(",").includes(requestDetails?.bcgovcode?.toUpperCase()) && requestDetails?.requestType === FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_GENERAL) { + dispatch(fetchFOIOpenInfoRequest(ministryId)); + } + }, [requestId, ministryId, requestDetails]) + const validLockRecordsState = (currentState=requestDetails.currentState) => { return ( currentState === StateEnum.harms.name || @@ -373,6 +411,7 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { currentState === StateEnum.peerreview.name || currentState === StateEnum.signoff.name || currentState === StateEnum.response.name || + currentState === StateEnum.onholdother.name || currentState === StateEnum.closed.name ); } @@ -414,6 +453,7 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { setIsMCFPersonal(true); } } + if(requestDetails.isoipcreview) { setIsOIPCReview(true); } else { @@ -893,7 +933,6 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { const handleSaveRequest = (_state, _unSaved, id) => { setHeader(_state); - if (!_unSaved) { setUnSavedRequest(_unSaved); dispatch(fetchFOIRequestDetailsWrapper(id || requestId, ministryId)); @@ -931,7 +970,11 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { }; const handleStateChange = (currentStatus) => { - setcurrentrequestStatus(currentStatus); + if (isOITeam) { + setOIStatus(currentStatus) + } else { + setcurrentrequestStatus(currentStatus); + } setStateChanged(true); }; @@ -1018,6 +1061,8 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { const signinUrl = "/signin"; const signupUrl = "/signup"; + const [OIStatus, setOIStatus] = useState(""); + const requestNumber = requestDetails?.axisRequestId ? requestDetails.axisRequestId : requestDetails?.idNumber; @@ -1088,6 +1133,14 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { requestDetails?.requestType === FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_GENERAL) } + const showOpenInformationTab = () => { + return ( + requestDetails?.requestType === FOI_COMPONENT_CONSTANTS.REQUEST_TYPE_GENERAL && + !SKIP_OPENINFO_MINISTRIES.includes(requestDetails?.bcgovcode) && + requestState !== StateEnum.intakeinprogress.name && + requestState !== StateEnum.unopened.name + ); + } const getCommentsCount = () => { let commentsCount= (requestNotes.filter( c => c.commentTypeId !== getCommentTypeIdByName(commentTypes,"Ministry Internal") && @@ -1096,6 +1149,14 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { } + const getOIRequestState = () => { + if (requestDetails?.oistatusid) { + return openInfoStates?.find(s => s.oistatusid === requestDetails?.oistatusid)?.name; + } else { + return StateEnum.unopened.name + } + } + return (!isLoading && requestDetails && Object.keys(requestDetails).length !== 0) || @@ -1112,7 +1173,7 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => {

{headerText}

{ isMinistryCoordinator={false} isValidationError={isValidationError} requestType={requestDetails?.requestType} - isDivisionalCoordinator={false} - isHistoricalRequest={isHistoricalRequest} + isOITeam={isOITeam} />
@@ -1185,6 +1245,17 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { Records )} + {showOpenInformationTab() && ( +
tabclick("OpenInformation")} + > + Publication +
+ )} {showContactApplicantTab() && (
{ CFRUnsaved={CFRUnsaved} handleSaveRequest={handleSaveRequest} handleOpenRequest={handleOpenRequest} - currentSelectedStatus={_currentrequestStatus} + currentSelectedStatus={isOITeam ? OIStatus : _currentrequestStatus} hasStatusRequestSaved={hasStatusRequestSaved} disableInput={disableInput} requestState={requestState} @@ -1721,11 +1792,31 @@ const FOIRequest = React.memo(({ userDetail, openApplicantProfileModal }) => { setLockRecordsTab={setLockRecordsTab} validLockRecordsState={validLockRecordsState} setSaveRequestObject={setSaveRequestObject} - handleSaveRequest={handleSaveRequest} /> )}
+ {showOpenInformationTab() && ( +
+ +
+ )} {showContactApplicantTab() && (
{ 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 a89a289bf..9459d9a31 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.js @@ -263,6 +263,7 @@ const MinistryReview = React.memo(({ userDetail }) => { currentState === StateEnum.peerreview.name || currentState === StateEnum.signoff.name || currentState === StateEnum.response.name || + currentState === StateEnum.onholdother.name || currentState === StateEnum.closed.name ); } @@ -328,6 +329,7 @@ const MinistryReview = React.memo(({ userDetail }) => { const [CFRUnsaved, setCFRUnsaved] = React.useState(false); const hideBottomText = [ StateEnum.onhold.name.toLowerCase(), + StateEnum.onholdother.name.toLowerCase(), StateEnum.closed.name.toLowerCase(), ]; @@ -489,6 +491,9 @@ const MinistryReview = React.memo(({ userDetail }) => { break; case StateEnum.recordsreadyforreview.name: foitabheaderBG = "foitabheadercollection foitabheaderRecordsReadyForReviewBG"; + break; + case StateEnum.onholdother.name: + foitabheaderBG = "foitabheadercollection foitabheaderOnHoldOtherBG"; break; default: foitabheaderBG = "foitabheadercollection foitabheaderdefaultBG"; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.scss b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.scss index cddb1ac41..0c0cdb1d5 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.scss +++ b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReview.scss @@ -85,12 +85,20 @@ .MuiFormLabel-root.Mui-disabled { font-family: "BCSans-Bold", sans-serif !important; - color: rgba(0, 0, 0, 0.58) !important; + color: black !important; } -input.MuiInputBase-input.Mui-disabled { +.MuiInputBase-input.Mui-disabled { background-color: #EEEEEE; - color: rgba(0, 0, 0, 0.58); + color: black; +} + +// .Mui-disabled.MuiInputBase-root { +// background-color: #EEEEEE; +// } + +.request-flag .MuiInputBase-input.Mui-disabled { + background-color: transparent !important; } .PrivateNotchedOutline-legendLabelled-4 { diff --git a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReviewTabbedContainer.scss b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReviewTabbedContainer.scss index fd1a57bdc..617a97784 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReviewTabbedContainer.scss +++ b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/MinistryReviewTabbedContainer.scss @@ -214,7 +214,9 @@ .foitabheaderPeerreviewBG{ background-color: #096DD1; } - + .foitabheaderOnHoldOtherBG{ + background-color: #595959; + } .foileftpanelheader { padding-left: 12%; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/RequestDetails.js b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/RequestDetails.js index 2e4c233a4..ec9a9472a 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/RequestDetails.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/MinistryReview/RequestDetails.js @@ -57,8 +57,9 @@ const RequestDetails = React.memo((requestDetails) => { Records Due Date - {_requestDetails?.currentState?.toLowerCase() !== - StateEnum.onhold.name.toLowerCase() + {(_requestDetails?.currentState?.toLowerCase() !== + StateEnum.onhold.name.toLowerCase() && _requestDetails?.currentState?.toLowerCase() !== + StateEnum.onholdother.name.toLowerCase()) ? formatDate( _requestDetails.cfrDueDate, "MMM dd yyyy" @@ -71,8 +72,9 @@ const RequestDetails = React.memo((requestDetails) => { Legislated Due Date - {_requestDetails?.currentState?.toLowerCase() !== - StateEnum.onhold.name.toLowerCase() + {(_requestDetails?.currentState?.toLowerCase() !== + StateEnum.onhold.name.toLowerCase() && _requestDetails?.currentState?.toLowerCase() !== + StateEnum.onholdother.name.toLowerCase()) ? formatDate( _requestDetails.dueDate, "MMM dd yyyy" diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/Exemption/IAOOpenInfoMain.tsx b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/Exemption/IAOOpenInfoMain.tsx new file mode 100644 index 000000000..a851a4e7b --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/Exemption/IAOOpenInfoMain.tsx @@ -0,0 +1,228 @@ +import { + Grid, + TextField, + Accordion, + AccordionDetails, + AccordionSummary, + Typography, + MenuItem, + Radio, + RadioGroup, + FormControlLabel, +} from "@material-ui/core"; +import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; +import { makeStyles } from "@material-ui/styles"; +import { useSelector } from "react-redux"; +import { OIPublicationStatus, OIExemption } from "../types"; +import { OIPublicationStatuses, OIExemptions } from "../../../../../helper/openinfo-helper"; + +const IAOOpenInfoMain = ({ + oiPublicationData, + handleOIDataChange, + isOIUser, +}: any) => { + const oiPublicationStatuses: OIPublicationStatus[] = useSelector( + (state: any) => state.foiRequests.oiPublicationStatuses + ); + const oiExemptions: OIExemption[] = useSelector( + (state: any) => state.foiRequests.oiExemptions + ); + + const disableIAOField = + oiPublicationData?.oipublicationstatus_id === OIPublicationStatuses.Publish || isOIUser; + const disableOIField = + oiPublicationData?.oipublicationstatus_id === OIPublicationStatuses.Publish || !isOIUser; + + //Styling + const useStyles = makeStyles({ + heading: { + color: "#FFF", + fontSize: "16px !important", + fontWeight: "bold", + }, + accordionSummary: { + flexDirection: "row-reverse", + }, + accordionDetails: { + margin: "10px 0 10px 0", + }, + }); + const classes = useStyles(); + + return ( +
+ + } + > + + EXEMPTION STATUS + + + + + + + handleOIDataChange(event.target.value, event.target.name) + } + > + {oiPublicationStatuses.map((status) => { + if (status.oipublicationstatusid === OIPublicationStatuses.UnpublishRequest) { + return null; + } + return ( + + {status.name} + + ); + })} + + + + + handleOIDataChange(event.target.value, event.target.name) + } + error={ + oiPublicationData?.oipublicationstatus_id !== OIPublicationStatuses.Publish && + !oiPublicationData?.oiexemption_id + } + disabled={disableIAOField} + > + {oiExemptions.map((reason) => { + return ( + + {reason.name} + + ); + })} + + + + + handleOIDataChange(event.target.value, event.target.name) + } + required={!isOIUser} + error={ + oiPublicationData?.oipublicationstatus_id !== OIPublicationStatuses.Publish && + oiPublicationData?.oiexemption_id !== OIExemptions.OutsideScopeOfPublication && + !oiPublicationData?.pagereference + } + disabled={disableIAOField} + > + + + + + handleOIDataChange(true, event.target.name) + } + color="default" + checked={oiPublicationData?.oiexemptionapproved === true} + /> + } + label="Approved" + /> + + handleOIDataChange(false, event.target.name) + } + color="default" + checked={oiPublicationData?.oiexemptionapproved === false} + /> + } + label="Declined" + /> + + + +
+ + handleOIDataChange(event.target.value, event.target.name) + } + multiline + minRows={6} + /> + + handleOIDataChange(event.target.value, event.target.name) + } + multiline + minRows={6} + /> +
+
+
+
+ ); +}; + +export default IAOOpenInfoMain; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/Exemption/IAOOpenInfoPublishing.tsx b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/Exemption/IAOOpenInfoPublishing.tsx new file mode 100644 index 000000000..c736c655a --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/Exemption/IAOOpenInfoPublishing.tsx @@ -0,0 +1,40 @@ +import IAOOpenInfoMain from "./IAOOpenInfoMain"; +import OpenInfoConfirmationModal from "../OpenInfoConfirmationModal"; +import "../openinfo.scss"; + +const IAOOpenInfoPublishing = ({ + handleOIDataChange, + oiPublicationData, + handleExemptionSave, + disableSave, + isOIUser, + saveModal, + saveData, + setSaveModal +}: any) => { + + return ( + <> + + + saveData()} + setModal={setSaveModal} + /> + + ); +}; + +export default IAOOpenInfoPublishing; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfo.tsx b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfo.tsx new file mode 100644 index 000000000..5a458e1ed --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfo.tsx @@ -0,0 +1,295 @@ +import { useState, useEffect } from "react"; +import { saveFOIOpenInfoRequest, fetchFOIOpenInfoRequest } from "../../../../apiManager/services/FOI/foiOpenInfoRequestServices"; +import { useDispatch, useSelector } from "react-redux"; +import IAOOpenInfoPublishing from "./Exemption/IAOOpenInfoPublishing"; +import OpenInfoPublication from "./Publication/OpenInfoPublication"; +import OpenInfoHeader from "./OpenInfoHeader"; +import OpenInfoTab from "./OpenInfoTab"; +import "./openinfo.scss"; +import { isReadyForPublishing } from "../utils"; +import { OIPublicationStatus, OITransactionObject } from "./types"; +import { OIStates, OIPublicationStatuses, OIExemptions } from "../../../../helper/openinfo-helper"; + +const OpenInfo = ({ + requestNumber, + requestDetails, + foiministryrequestid, + foirequestid, + bcgovcode, + toast, + currentOIRequestState, + isOITeam, +}: any) => { + const dispatch = useDispatch(); + + //App State + const assignedToList = useSelector( + (state: any) => state.foiRequests.foiFullAssignedToList + ); + let foiOpenInfoAdditionalFiles = useSelector( + (state: any) => state.foiRequests.foiOpenInfoAdditionalFiles + ); + let foiOITransactionData = useSelector( + (state: any) => state.foiRequests.foiOpenInfoRequest + ); + const oiPublicationStatuses: OIPublicationStatus[] = useSelector( + (state: any) => state.foiRequests.oiPublicationStatuses + ); + + //Local State + const [oiPublicationData, setOiPublicationData] = + useState(foiOITransactionData); + const [confirmationModal, setConfirmationModal] = useState({ + show: false, + title: "", + message: "", + description: "", + confirmButtonTitle: "", + confirmationData: null, + }); + const [tabValue, setTabValue] = useState(isOITeam ? 2 : 1); + const [isDataEdited, setIsDataEdited] = useState(false); + + useEffect(() => { + setOiPublicationData(foiOITransactionData); + if (isOITeam) { + if (foiOITransactionData.oipublicationstatus_id === findOIPublicationState('Do Not Publish')?.oipublicationstatusid) { + setTabValue(1) + } + } + }, [foiOITransactionData]); + + //Functions + const findOIPublicationState = (name: string) => { + return oiPublicationStatuses.find((s: OIPublicationStatus) => s.name === name); + } + const handleOIDataChange = ( + value: number | string | boolean, + oiDataKey: string + ) => { + if (!isDataEdited) { + setIsDataEdited(true); + } + //Reset foi oi data if publication status goes back to publication. + if (oiDataKey === "oipublicationstatus_id" && value === findOIPublicationState("Publish")?.oipublicationstatusid) { + setOiPublicationData((prev: any) => ({ + ...prev, + [oiDataKey]: value, + copyrightsevered: null, + publicationdate: null, + oiexemptiondate: null, + oiexemption_id: null + })); + } else if (oiDataKey === "publicationdate" && requestDetails.closedate + && typeof(value) === "string" && calculateDaysBetweenDates(value, requestDetails.closedate) >= 1 && calculateDaysBetweenDates(value, requestDetails.closedate) <= 10) { + setConfirmationModal((prev : any) => ({ + ...prev, + show: true, + title: "Change Publication Date", + description: "The date you have chosen falls within 10 business days of the closed date. Are you sure you want to continue?", + message: "", + confirmButtonTitle: "Continue", + confirmationData: value, + })); + } else { + setOiPublicationData((prev: any) => ({ + ...prev, + [oiDataKey]: value, + })); + } + }; + const handleExemptionSave = () => { + if ( + oiPublicationData?.oipublicationstatus_id === OIPublicationStatuses.DoNotPublish && + oiPublicationData?.oiexemption_id !== OIExemptions.OutsideScopeOfPublication + ) { + if (isOITeam && oiPublicationData?.oiexemptionapproved != null) { + setConfirmationModal((prev: any) => ({ + ...prev, + show: true, + title: oiPublicationData.oiexemptionapproved ? "Exemption Approved" : "Exemption Denied", + description: oiPublicationData.oiexemptionapproved + ? "Are you sure you want to approve this exemption?" + : "Are you sure you want to deny this exemption? ", + message: oiPublicationData.oiexemptionapproved ? "The request will not be eligible for publication and will be removed from the OI Queue." : "The request will still be eligible for publication and will remain in the OI Queue.", + confirmButtonTitle: "Save Change" + })); + } else { + setConfirmationModal((prev: any) => ({ + ...prev, + show: true, + title: "Exemption Request", + description: "Are you sure you want to change the state to Exemption Request?", + message: "This will assign the request to the Open Information Queue.", + confirmButtonTitle: "Save Changes" + })); + } + } else { + saveData(); + } + }; + const saveData = (publicationdate?: any) => { + const toastID = toast.loading("Saving FOI OpenInformation request..."); + publicationdate = publicationdate || (oiPublicationData.publicationdate ? + new Date(oiPublicationData.publicationdate).toISOString().split('T')[0] : + null) + const formattedData = { + ...oiPublicationData, + publicationdate: publicationdate + }; + if (formattedData.oiexemptionapproved === false) { + formattedData.oipublicationstatus_id = findOIPublicationState("Publish")?.oipublicationstatusid || OIPublicationStatuses.Publish; + formattedData.oiexemption_id = null; + } + dispatch( + saveFOIOpenInfoRequest( + foiministryrequestid, + foirequestid, + formattedData, + isOITeam, + requestDetails, + (err: any, _res: any) => { + if (!err) { + toast.update(toastID, { + type: "success", + render: + "FOI OpenInformation request has been saved successfully.", + position: "top-right", + isLoading: false, + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + }); + const isValidExemptionRequest = !isOITeam && formattedData.oipublicationstatus_id === OIPublicationStatuses.DoNotPublish && formattedData.oiexemption_id !== OIExemptions.OutsideScopeOfPublication; + const isValidExemptionDenial = isOITeam && formattedData.oipublicationstatus_id === OIPublicationStatuses.DoNotPublish && formattedData.oiexemption_id !== OIExemptions.OutsideScopeOfPublication && formattedData.oiexemptionapproved === false; + const manualPublicationStatusChange = requestDetails.oistatusid === OIStates.ExemptionRequest && formattedData.oipublicationstatus_id === OIPublicationStatuses.Publish; + if (isValidExemptionRequest) { + requestDetails.oistatusid = OIStates.ExemptionRequest; + } + if (isValidExemptionDenial || manualPublicationStatusChange) { + requestDetails.oistatusid = null; + } + dispatch(fetchFOIOpenInfoRequest(foiministryrequestid)); + } else { + toast.error( + "Temporarily unable to save FOI OpenInformation request. Please try again in a few minutes.", + { + position: "top-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + } + ); + } + } + ) + ); + }; + const disableSave = (oiPublicationData: OITransactionObject): boolean => { + const isDoNotPublish = oiPublicationData?.oipublicationstatus_id === OIPublicationStatuses.DoNotPublish; + const hasExemption = oiPublicationData?.oiexemption_id; + const isMissingRequiredFields = + !oiPublicationData?.iaorationale || !oiPublicationData?.pagereference; + const hasOutOfScopeExemption = oiPublicationData?.oiexemption_id === OIExemptions.OutsideScopeOfPublication; + if (isDoNotPublish && hasOutOfScopeExemption) { + return false; + } + if (isDoNotPublish && !hasExemption) { + return true; + } + if (isDoNotPublish && hasExemption) { + return isMissingRequiredFields; + } + if (isDataEdited) { + return false; + } + return true; + }; + const disablePublish = (oiPublicationData: OITransactionObject) : boolean => { + const isMissingRequiredInput = !isReadyForPublishing(oiPublicationData, foiOpenInfoAdditionalFiles, requestNumber); + const isOIReadyToPublish = currentOIRequestState === "Ready to Publish"; + if (!isOIReadyToPublish) { + return true; + } + if (isMissingRequiredInput) { + return true; + } + return false; + } + const handleTabSelect = (value: number): void => { + setTabValue(value); + }; + const handleDateConfirmation = (value : Date) => { + setOiPublicationData((prev: any) => ({ + ...prev, + publicationdate: value, + })); + } + const calculateDaysBetweenDates = (date1: string, date2: string) => { + return Math.round((new Date(date1).getTime() - new Date(date2).getTime()) / (1000 * 3600 *24)); + } + const handlePublishNow = () => { + setConfirmationModal((prev : any) => ({ + ...prev, + show: true, + title: "Publish Now", + description: "Are you sure you want to Publish this request now?", + message: "", + confirmButtonTitle: "Publish Now", + })); + } + + return ( + <> +
+ + + {tabValue === 1 ? ( + + ) : ( + + )} +
+ + ); +}; + +export default OpenInfo; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoApproveModal.tsx b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoApproveModal.tsx new file mode 100644 index 000000000..e1b96fd59 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoApproveModal.tsx @@ -0,0 +1,66 @@ +import { + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions, + IconButton, +} from "@material-ui/core"; +import CloseIcon from "@material-ui/icons/Close"; + +const OAOpenInfoApproveModal = ({ showApproveModal, saveData, setShowApproveModal }: any) => { + const handleSave = () => { + saveData(); + setShowApproveModal(false); + }; + const handleClose = () => { + setShowApproveModal(false); + }; + + return ( +
+ { + handleClose() + }} + aria-labelledby="state-change-dialog-title" + aria-describedby="restricted-modal-text" + maxWidth={"md"} + fullWidth={true} + > + +

{"Exemption Approved"}

+ + Close + + +
+ + +
+
{"Are you sure you want to approve this exemption?"}
+
+ The request will not be eligible for publication and will be removed from the OI Queue. +
+
+
+
+ + + + +
+
+ ); +}; + +export default OAOpenInfoApproveModal; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoConfirmationModal.tsx b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoConfirmationModal.tsx new file mode 100644 index 000000000..94aae386d --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoConfirmationModal.tsx @@ -0,0 +1,92 @@ +import { + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions, + IconButton, +} from "@material-ui/core"; +import CloseIcon from "@material-ui/icons/Close"; + +const OpenInfoConfirmationModal = ({ + confirm, + setModal, + modal, +}: any) => { + const handleConfirmation = () => { + if (modal.title === "Change Publication Date") { + confirm(modal.confirmationData); + } else { + confirm(); + } + setModal((prev : any) => ({ + ...prev, + show: false, + title: "", + message: "", + description: "", + confirmButtonTitle: "", + confirmationData: null, + })); + }; + const handleClose = () => { + setModal((prev : any) => ({ + ...prev, + show: false, + title: "", + message: "", + description: "", + confirmButtonTitle: "", + confirmationData: null, + })); + }; + + return ( +
+ { + handleClose(); + }} + aria-labelledby="state-change-dialog-title" + aria-describedby="restricted-modal-text" + maxWidth={"md"} + fullWidth={true} + > + +

{modal.title}

+ + Close + + +
+ + +
+
{modal.description}
+
+ + {modal.message} + +
+
+
+
+ + + + +
+
+ ); +}; + +export default OpenInfoConfirmationModal; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoDeniedModal.tsx b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoDeniedModal.tsx new file mode 100644 index 000000000..43e567452 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoDeniedModal.tsx @@ -0,0 +1,66 @@ +import { + Dialog, + DialogTitle, + DialogContent, + DialogContentText, + DialogActions, + IconButton, +} from "@material-ui/core"; +import CloseIcon from "@material-ui/icons/Close"; + +const OAOpenInfoDeniedModal = ({ showDeniedModal, saveData, setShowDeniedModal }: any) => { + const handleSave = () => { + saveData(); + setShowDeniedModal(false); + }; + const handleClose = () => { + setShowDeniedModal(false); + }; + + return ( +
+ { + handleClose() + }} + aria-labelledby="state-change-dialog-title" + aria-describedby="restricted-modal-text" + maxWidth={"md"} + fullWidth={true} + > + +

{"Exemption Denied"}

+ + Close + + +
+ + +
+
{"Are you sure you want to deny this exemption?"}
+
+ The request will not be eligible for publication and will remain in the OI Queue. +
+
+
+
+ + + + +
+
+ ); +}; + +export default OAOpenInfoDeniedModal; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoHeader.tsx b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoHeader.tsx new file mode 100644 index 000000000..9e9e83dcb --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoHeader.tsx @@ -0,0 +1,224 @@ +import { TextField } from "@material-ui/core"; +import { useEffect, useState } from "react"; +import { useSelector } from "react-redux"; +import { makeStyles, Theme } from '@material-ui/core/styles'; +import { getMenuItems } from "../FOIRequestHeader/utils"; +import { saveFOIOpenInfoRequest } from "../../../../apiManager/services/FOI/foiOpenInfoRequestServices"; +import { useDispatch } from "react-redux"; +import "./openinfo.scss"; + +const OIAssignedToStyles = makeStyles((theme: Theme) => ({ + formControl: { + margin: theme.spacing(1), + minWidth: 120, + }, + item: { + paddingLeft: theme.spacing(3), + }, + group: { + fontWeight: theme.typography.fontWeightBold as any, + opacity: 1, + }, + blankrow: { + padding: 25 + } + })); + +const OpenInfoHeader = ({ + requestNumber, + requestDetails, + isOIUser, + assignedToList, + foiministryrequestid, + foirequestid, + toast, +}: any) => { + const dispatch = useDispatch(); + const classes = OIAssignedToStyles(); + let foiOITransactionData = useSelector( + (state: any) => state.foiRequests.foiOpenInfoRequest + ); + + let iaoassignedToList = useSelector( + (state : any) => state.foiRequests.foiAssignedToList + ); + + const getGroupName = () => { + if (requestDetails.assignedGroup) return requestDetails.assignedGroup; + return "Unassigned"; + }; + const getAssignedTo = (groupName: any) => { + if (requestDetails.assignedTo) return requestDetails.assignedTo; + return groupName; + }; + function getFullName() { + const groupName = getGroupName(); + const assignedTo = getAssignedTo(groupName); + if (assignedToList?.length > 0) { + const assigneeGroup = assignedToList.find( + (_assigneeGroup: any) => _assigneeGroup.name === groupName + ); + const assignee = assigneeGroup?.members?.find( + (_assignee: any) => _assignee.username === assignedTo + ); + if (groupName === assignedTo) return groupName; + return assignee !== undefined + ? `${assignee.lastname}, ${assignee.firstname}` + : "invalid user"; + } + return groupName; + } + + const [selectedAssignedTo, setAssignedTo] = useState(() => getFullName()); + +const handleOIAssigneeUpdate = async (event: any) => { + const assigneeValue = event?.target?.value; + const [groupName, username, firstName, lastName] = assigneeValue.split('|'); + const fullName = firstName !== "" ? `${lastName}, ${firstName}` : username;; + + // Update the selected assignee in the dropdown + setOIAssignedTo(assigneeValue); + + if(username != 'OI Team'){ + const assigneeDetails = { + assignedGroup: groupName, + assignedTo: username, + assignedToFirstName: firstName, + assignedToLastName: lastName, + assignedToName: fullName + }; + + const updatedOpenInfoRequest = { + ...foiOITransactionData, + oiassignedto: username, + assigneeDetails:assigneeDetails, + publicationdate: foiOITransactionData.publicationdate ? + new Date(foiOITransactionData.publicationdate).toISOString().split('T')[0] : + null + }; + + dispatch( + saveFOIOpenInfoRequest( + foiministryrequestid, + foirequestid, + updatedOpenInfoRequest, + isOIUser, + requestDetails, + (err: any, res: any) => { + if (!err) { + toast.success("Assignee has been saved successfully.", { + position: "top-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + }); + } else { + toast.error( + "Temporarily unable to save the assignee. Please try again in a few minutes.", + { + position: "top-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + } + ); + } + }) + ); + } +} + +const [menuItems, setMenuItems] = useState([]); +const [selectedOIAssignedTo, setOIAssignedTo] = useState("Unassigned"); + +useEffect(() => { + // Find OI Team from the assignedToList + const oiTeam = iaoassignedToList?.find((team: any) => team.name === 'OI Team'); + + // Find specific member if request is assigned to individual + const member = oiTeam?.members?.find( + (member: any) => member.username === foiOITransactionData?.oiassignedto + ); + + if(isOIUser){ + /* For OI users, Mapping for assignee display values: + - 'OI Team': When assigned to entire OI Team + - 'member': When assigned to specific OI Team member + - 'default': When unassigned */ + const assigneeMap = { + 'OI Team': 'OI Team|OI Team', + 'member': member && `${oiTeam.name}|${member.username}|${member.firstname}|${member.lastname}`, + 'default': 'OI Team|OI Team' + }; + + const currentAssignee = assigneeMap[foiOITransactionData?.oiassignedto === null ? 'OI Team' : member ? 'member' : 'default']; + setOIAssignedTo(currentAssignee); + + // Generate menu items for the dropdown with OI Team members only + setMenuItems( + getMenuItems({ + classes, + assignedToList: iaoassignedToList?.filter((team: any) => team.name === 'OI Team'), + selectedAssignedTo: currentAssignee, + isIAORestrictedRequest: false + }) + ); + } else { + // For non-OI users, just set the formatted name + const displayMap = { + 'OI Team': 'OI Team', + 'member': member && `${member.lastname}, ${member.firstname}`, + 'default': 'OI Team' + }; + + setOIAssignedTo(displayMap[foiOITransactionData?.oiassignedto === null ? 'OI Team' : member ? 'member' : 'default']); + } +}, [iaoassignedToList, foiOITransactionData, isOIUser]); + + return ( +
+

+ {requestNumber ? `Request #${requestNumber}` : ""} +

+
+ + + {menuItems} + +
+
+ ); +}; + +export default OpenInfoHeader; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoTab.tsx b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoTab.tsx new file mode 100644 index 000000000..31bc344e6 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/OpenInfoTab.tsx @@ -0,0 +1,30 @@ +import "./openinfo.scss"; +import { Typography } from "@material-ui/core"; + +const OpenInfoTab = ({ tabValue, handleTabSelect, isOIUser }: any) => { + + return ( +
+
+ handleTabSelect(1)} + id="exemption-tab" + >Exemption +
+ {isOIUser && +
+ handleTabSelect(2)} + id="openinformation-tab" + >Open Information +
+ } +
+ ); +}; + +export default OpenInfoTab; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/Publication/OpenInfoPublication.tsx b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/Publication/OpenInfoPublication.tsx new file mode 100644 index 000000000..1f0195d35 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/Publication/OpenInfoPublication.tsx @@ -0,0 +1,64 @@ +import OpenInfoPublicationMain from "./OpenInfoPublicationMain"; +import OpenInfoConfirmationModal from "../OpenInfoConfirmationModal"; +import { formatDateInPst } from "../../../../../helper/FOI/helper"; + +const OpenInfoPublication = ({ + oiPublicationData, + handleOIDataChange, + disablePublish, + confirmModal, + handleDateConfirmation, + setConfirmationModal, + isDataEdited, + saveData, + currentOIRequestState, + ministryId, + requestId, + bcgovcode, + requestNumber, + handlePublishNow, +}: any) => { + + //Functions + const publishConfirmation = () => { + const todaysDate = formatDateInPst(new Date()); + saveData(todaysDate); + } + + return ( + <> + + + + + + ); +}; + +export default OpenInfoPublication; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/Publication/OpenInfoPublicationMain.tsx b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/Publication/OpenInfoPublicationMain.tsx new file mode 100644 index 000000000..7653b65bb --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/Publication/OpenInfoPublicationMain.tsx @@ -0,0 +1,544 @@ +import { + Grid, + TextField, + Accordion, + AccordionDetails, + AccordionSummary, + Typography, + MenuItem, + Radio, + RadioGroup, + FormControlLabel, +} from "@material-ui/core"; +import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; +import { makeStyles } from "@material-ui/styles"; +import { formatDate } from "../../../../../helper/FOI/helper"; +import { useDispatch, useSelector } from "react-redux"; +import FilePreviewContainer from "../../../customComponents/FileUpload/FilePreviewContainer"; +import clsx from "clsx"; +import { useState, useEffect } from "react"; +import { deleteFOIOpenInfoAdditionalFiles, fetchFOIOpenInfoAdditionalFiles, saveFOIOpenInfoAdditionalFiles } from "../../../../../apiManager/services/FOI/foiOpenInfoRequestServices"; +import { setFOILoader } from "../../../../../actions/FOI/foiRequestActions"; +import AttachmentModal from "../../../customComponents/Attachments/AttachmentModal"; +import { + saveFilesinS3, + getFileFromS3, + postFOIS3DocumentPreSignedUrl, + getFOIS3DocumentPreSignedUrl, + completeMultiPartUpload, +} from "../../../../../apiManager/services/FOI/foiOSSServices"; +import { toast } from "react-toastify"; +import { readUploadedFileAsBytes } from "../../../../../helper/FOI/helper"; +import { OSS_S3_CHUNK_SIZE } from "../../../../../constants/constants"; +import { RecordDownloadStatus } from "../../../../../constants/FOI/enum"; +import Tooltip from "@mui/material/Tooltip"; +import { OIPublicationStatus } from "../types"; +import { OIPublicationStatuses } from "../../../../../helper/openinfo-helper"; + +const OpenInfoPublicationMain = ({ + requestId, + ministryId, + oiPublicationData, + currentOIRequestState, + handleOIDataChange, + bcgovcode, + requestNumber, +}: any) => { + const dispatch = useDispatch(); + + //App State + const oiPublicationStatuses: OIPublicationStatus[] = useSelector( + (state: any) => state.foiRequests.oiPublicationStatuses + ); + let foiOpenInfoAdditionalFiles = useSelector( + (state: any) => state.foiRequests.foiOpenInfoAdditionalFiles + ); + + let foiPDFStitchedOIPackage = useSelector( + (state: any) => state.foiRequests.foiPDFStitchedOIPackage + ); + + let foiPDFStitchStatusForOIPackage = useSelector( + (state: any) => state.foiRequests.foiPDFStitchStatusForOIPackage + ); + + const [downloadDisabled, setDownloadDisabled] = useState(true) + const [packageCreatedAt, setPackageCreatedAt] = useState("N/A"); + + useEffect(() => { + if (foiPDFStitchStatusForOIPackage === RecordDownloadStatus.completed) { + setDownloadDisabled(false) + } + }, [foiPDFStitchStatusForOIPackage]) + + useEffect(() => { + setPackageCreatedAt(foiPDFStitchedOIPackage?.createdat_datetime || 'N/A') + }, [foiPDFStitchedOIPackage]) + + //Styling + const useStyles = makeStyles({ + heading: { + color: "#FFF", + fontSize: "16px !important", + fontWeight: "bold", + }, + accordionSummary: { + flexDirection: "row-reverse", + }, + accordionDetails: { + margin: "10px 0 10px 0", + }, + }); + const classes = useStyles(); + + //Local State + const [additionalFiles, setAdditionalFiles] = useState([]); + const [openModal, setOpenModal] = useState(false); + + useEffect(() => { + setAdditionalFiles(foiOpenInfoAdditionalFiles); + }, [foiOpenInfoAdditionalFiles]) + + //Functions + const deleteFile = (_index: any) => { + let file: any = additionalFiles[_index]; + dispatch(setFOILoader(true)); + dispatch( + deleteFOIOpenInfoAdditionalFiles( + requestId, + ministryId, + { fileids: [file.additionalfileid] }, + (err: any, _res: any) => { + if (!err) { + dispatch(fetchFOIOpenInfoAdditionalFiles(requestId, ministryId)); + } else { + console.log("Error while deleting additional files, please try again") + } + } + ) + ); + } + + const handleModal = (value: any, fileInfoList: any, files: any) => { + setOpenModal(false) + if (value) { + saveDocument(value, fileInfoList, files) + } + } + + const saveDocument = (value: any, fileInfoList: any, files: any) => { + if (value) { + if (files.length !== 0) { + setFOILoader(true); + postFOIS3DocumentPreSignedUrl( + ministryId, + fileInfoList.map((file: any) => ({ ...file, multipart: true })), + "additionalfiles", + bcgovcode, + dispatch, + async (err: any, res: any) => { + let _documents: any = []; + if (!err) { + var completed = 0; + let failed = []; + const toastID = toast.loading( + "Uploading files (" + + completed + + "/" + + fileInfoList.length + + ")" + ); + for (let header of res) { + const _file = files.find( + (file: any) => file.filename === header.filename + ); + const _fileInfo = fileInfoList.find( + (fileInfo: any) => fileInfo.filename === header.filename + ); + var documentDetails = { + s3uripath: header.filepathdb, + filename: header.filename, + }; + let bytes = await readUploadedFileAsBytes(_file); + const CHUNK_SIZE = OSS_S3_CHUNK_SIZE; + const totalChunks = Math.ceil(bytes.byteLength / CHUNK_SIZE); + let parts = []; + for (let chunk = 0; chunk < totalChunks; chunk++) { + let CHUNK = bytes.slice( + chunk * CHUNK_SIZE, + (chunk + 1) * CHUNK_SIZE + ); + let response: any = await saveFilesinS3( + { filepath: header.filepaths[chunk] }, + CHUNK, + dispatch, + (_err: any, _res: any) => { + if (_err) { + failed.push(header.filename); + } + } + ); + if (response.status === 200) { + parts.push({ + PartNumber: chunk + 1, + ETag: response.headers.etag, + }); + } else { + failed.push(header.filename); + } + } + await completeMultiPartUpload( + { + uploadid: header.uploadid, + filepath: header.filepathdb, + parts: parts, + }, + ministryId, + "additionalfiles", + bcgovcode, + dispatch, + (_err: any, _res: any) => { + if (!_err && _res.ResponseMetadata.HTTPStatusCode === 200) { + completed++; + toast.update(toastID, { + render: + "Uploading files (" + + completed + + "/" + + fileInfoList.length + + ")", + isLoading: true, + }); + _documents.push(documentDetails); + } else { + failed.push(header.filename); + } + } + ); + } + if (_documents.length > 0) { + dispatch( + saveFOIOpenInfoAdditionalFiles( + requestId, + ministryId, + { additionalfiles: _documents }, + (err: any, _res: any) => { + dispatch(fetchFOIOpenInfoAdditionalFiles(requestId, ministryId)); + } + ) + ); + } + var toastOptions: any = { + render: + failed.length > 0 + ? "The following " + + failed.length + + " file uploads failed\n- " + + failed.join("\n- ") + : fileInfoList.length + " Files successfully saved", + type: failed.length > 0 ? "error" : "success", + }; + toast.update(toastID, { + ...toastOptions, + className: "file-upload-toast", + isLoading: false, + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + closeButton: true, + }); + setFOILoader(false); + } else { + toast.error( + err, + { + position: "top-right", + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + } + ); + } + } + ); + } + } + }; + + const openDocuemnt = (index: any) => { + let file: any = additionalFiles[index]; + let filepath = file.s3uripath.split("/").slice(4).join("/"); + let url = `/foidocument?id=${ministryId}&filepath=${filepath}&filetype=additionalfiles&bcgovcode=${bcgovcode}`; + window.open(url, "_blank")?.focus(); + } + + const disableUserInput = !oiPublicationData?.oiexemptionapproved && oiPublicationData.oipublicationstatus_id === OIPublicationStatuses.DoNotPublish; + + var saveAs = (blob: any, filename: any) => { + const fileURL = URL.createObjectURL(blob); + const downloadLink = document.createElement('a'); + downloadLink.href = fileURL; + downloadLink.download = filename; + document.body.appendChild(downloadLink); + downloadLink.click(); + URL.revokeObjectURL(fileURL); + } + + const downloadPackage = () => { + const toastID = toast.loading("Downloading file (0%)"); + const s3filepath = foiPDFStitchedOIPackage?.finalpackagepath; + getFOIS3DocumentPreSignedUrl( + s3filepath.split("/").slice(4).join("/"), + ministryId, + dispatch, + (err: any, res: any) => { + if (!err) { + getFileFromS3( + { filepath: res }, + (_err: any, response: any) => { + let blob = new Blob([response.data], { + type: "application/octet-stream", + }); + const filename = requestNumber + ".zip"; + saveAs(blob, filename); + toast.update(toastID, { + render: _err ? "File download failed" : "Download complete", + type: _err ? "error" : "success", + className: "file-upload-toast", + isLoading: false, + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + closeButton: true, + }); + }, + (progressEvent: any) => { + toast.update(toastID, { + render: + "Downloading file (" + + Math.floor( + (progressEvent.loaded / progressEvent.total) * 100 + ) + + "%)", + isLoading: true, + }); + } + ); + } else { + toast.update(toastID, { + render: "File download failed", + type: "error", + className: "file-upload-toast", + isLoading: false, + autoClose: 3000, + hideProgressBar: true, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + closeButton: true, + }); + } + }, + "additionalfiles", + bcgovcode + ); + } + + return ( +
+ + } + > + + PUBLICATION DETAILS + + + + + + + handleOIDataChange(event.target.value, event.target.name) + } + > + {oiPublicationStatuses.map((status) => { + if (status.oipublicationstatusid === OIPublicationStatuses.DoNotPublish) { + return null; + } + return ( + + {status.name} + + ); + })} + + + + + handleOIDataChange(event.target.value, event.target.name) + } + value={(oiPublicationData?.publicationdate ? formatDate(new Date(oiPublicationData?.publicationdate)) : "") || ""} + type="date" + > + + + + + handleOIDataChange(true, event.target.name) + } + color="default" + checked={oiPublicationData?.copyrightsevered === true} + /> + } + label="Copyright Severed" + /> + + handleOIDataChange(false, event.target.name) + } + color="default" + checked={oiPublicationData?.copyrightsevered === false} + /> + } + label="No Copyright" + /> + + + + + + Files to be Published: + + + + + + + + {!disableUserInput && +
+ Additional Files: +
+
+
+ { + { + f.fileName = f.filename; + return f; + })} + removeFile={deleteFile} + clickHandler={openDocuemnt} + /> + } +
+
+ {/* */} +
+
+ { + + } +
+
+
+
+ } +
+
+ +
+ ); +}; + +export default OpenInfoPublicationMain; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/openinfo.scss b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/openinfo.scss new file mode 100644 index 000000000..26c7d2667 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/openinfo.scss @@ -0,0 +1,49 @@ +.oi-section{ + margin-top: 60px; + margin-left: 2em; + margin-right: 2em; + margin-bottom: 60px +} + +.oi-header { + display: flex; + flex-direction: row; + justify-content: space-between; + margin-bottom: 30px; +} + +.oi-assignment { + min-width: 400px; +} + +.oi-main-text { + margin-top: 40px; +} + +.openinfotab-select{ + border: 1px solid #036; + border-radius: 40px; + padding: 0 20px 0 20px; + margin-left: 15px; + cursor: pointer; + color: #036; +} + +.openinfotab-selected{ + border: 1px solid #036; + border-radius: 40px; + padding: 0 20px 0 20px; + margin-left: 15px; + cursor: pointer; + color: white; + background-color: #036; +} + +.openinfotab { + border: 1px solid; + border-color: #036; + height: 50px; + display: flex; + justify-content: flex-start; + align-items: center; +} \ No newline at end of file diff --git a/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/types.ts b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/types.ts new file mode 100644 index 000000000..b2cc38978 --- /dev/null +++ b/forms-flow-web/src/components/FOI/FOIRequest/OpenInformation/types.ts @@ -0,0 +1,22 @@ +export type OITransactionObject = { + oipublicationstatus_id: number; + oiexemption_id: number | null; + oiexemptionapproved: boolean | null; + pagereference: string; + iaorationale: string; + oifeedback: string; + copyrightsevered: boolean; + publicationdate: string; + oiexemptiondate: string; +}; + +export type OIPublicationStatus = { + oipublicationstatusid: number; + name: string; + isactive: boolean; +}; +export type OIExemption = { + oiexemptionid: number; + name: string; + isactive: boolean; +}; diff --git a/forms-flow-web/src/components/FOI/FOIRequest/RequestDetails.js b/forms-flow-web/src/components/FOI/FOIRequest/RequestDetails.js index 4ee9e5deb..c171c02c9 100644 --- a/forms-flow-web/src/components/FOI/FOIRequest/RequestDetails.js +++ b/forms-flow-web/src/components/FOI/FOIRequest/RequestDetails.js @@ -266,8 +266,8 @@ const RequestDetails = React.memo( { return "foitabheadercollection foitabheaderAppFeeOwingBG"; case StateEnum.recordsreadyforreview.name: return "foitabheadercollection foitabheaderRecordsReadyForReviewBG"; - + case StateEnum.onholdother.name: + return "foitabheadercollection foitabheaderOnholdOtherBG"; default: return "foitabheadercollection foitabheaderdefaultBG"; } }; +export const getOITabBG = (OIRequestStatusId, OIStatuses) => { + if (OIStatuses) { + var OIStatusName = OIStatuses.find(s => s.oistatusid === OIRequestStatusId)?.name + switch (OIStatusName) { + case "First Review": + return "foitabheadercollection foitabheaderFirstReviewBG"; + case "Peer Review": + return "foitabheadercollection foitabheaderOIPeerReviewBG"; + case "Ready to Publish": + return "foitabheadercollection foitabheaderReadyToPublishBG"; + case "Published": + return "foitabheadercollection foitabheaderPublishedBG"; + case "HOLD Publication": + return "foitabheadercollection foitabheaderHoldBG"; + case "Unpublished": + return "foitabheadercollection foitabheaderUnpublishedBG"; + case "Do Not Publish": + return "foitabheadercollection foitabheaderDoNotPublishBG"; + case "Exemption Request": + return "foitabheadercollection foitabheaderExemptionBG"; + + + default: + return "foitabheadercollection foitabheaderdefaultBG"; + } + } +}; + export const assignValue = (jsonObj, value, name) => { let _obj = { ...jsonObj }; if (_obj[name] !== undefined) { @@ -460,3 +490,7 @@ export const getUniqueIdentifier = (obj) => { return (obj.extensionstatusid+formatDate(obj.extendedduedate, "MMM dd yyyy")+obj.extensionreasonid).replace(/\s+/g, ''); } +export const isReadyForPublishing = (openinfo, additionalfiles, requestnumber) => { + return !(openinfo?.copyrightsevered === null || openinfo?.publicationdate === null || additionalfiles?.findIndex(f => f.filename.includes("Response_Letter_" + requestnumber + ".pdf")) < 0) +} + diff --git a/forms-flow-web/src/components/FOI/customComponents/AttachmentViewer/index.tsx b/forms-flow-web/src/components/FOI/customComponents/AttachmentViewer/index.tsx index bb6e8874f..758059b00 100644 --- a/forms-flow-web/src/components/FOI/customComponents/AttachmentViewer/index.tsx +++ b/forms-flow-web/src/components/FOI/customComponents/AttachmentViewer/index.tsx @@ -5,14 +5,14 @@ import './index.scss' import {getFOIS3DocumentPreSignedUrl} from '../../../../apiManager/services/FOI/foiOSSServices' -export const AttachmentViewer = ({filepath, ministryrequestid}:params) => { +export const AttachmentViewer = ({filepath, ministryrequestid, filetype, bcgovcode}:params) => { const dispatch = useDispatch(); const [presignedUrl, setpresignedUrl] = React.useState(filepath); React.useEffect(() => { if(filepath) { - const response = getFOIS3DocumentPreSignedUrl(filepath,ministryrequestid, dispatch) + const response = getFOIS3DocumentPreSignedUrl(filepath,ministryrequestid, dispatch, () => {}, filetype, bcgovcode) response.then((result)=>{ var viwerUrl = result.data if(filepath.toLowerCase().indexOf('.docx') >-1 ||filepath.toLowerCase().indexOf('.doc') >-1 || filepath.toLowerCase().indexOf('.xls') >-1 || filepath.toLowerCase().indexOf('.xlsx')>-1) diff --git a/forms-flow-web/src/components/FOI/customComponents/AttachmentViewer/types.ts b/forms-flow-web/src/components/FOI/customComponents/AttachmentViewer/types.ts index 1f42b6a34..8e3e22455 100644 --- a/forms-flow-web/src/components/FOI/customComponents/AttachmentViewer/types.ts +++ b/forms-flow-web/src/components/FOI/customComponents/AttachmentViewer/types.ts @@ -1,4 +1,6 @@ export type params = { filepath: string; ministryrequestid:string; + filetype: string; + bcgovcode: string; } \ No newline at end of file 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 907436dfb..4cd823c82 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js +++ b/forms-flow-web/src/components/FOI/customComponents/Attachments/AttachmentModal.js @@ -121,7 +121,7 @@ export default function AttachmentModal({ : uploadFor === "record" && (modalFor === "replace" || modalFor === "replaceattachment") ? replacementfiletypes - : recordFormats + : uploadFor === "additionalFiles" ? MimeTypeList.openInfo : recordFormats : MimeTypeList.stateTransition ); }, [recordFormats]); @@ -132,7 +132,7 @@ export default function AttachmentModal({ : uploadFor === "record" && (modalFor === "replace" || modalFor === "replaceattachment") ? replacementfiletypes - : recordFormats + : uploadFor === "additionalFiles" ? MimeTypeList.openInfo : recordFormats : MimeTypeList.stateTransition ); const maxFileSize = @@ -385,7 +385,7 @@ export default function AttachmentModal({ } fileInfoList = files?.map((file) => { return { - ministrycode: uploadFor === "record" ? bcgovcode : "Misc", + ministrycode: (uploadFor === "record" || uploadFor === "additionalFiles") ? bcgovcode : "Misc", requestnumber: requestNumber ? requestNumber : `U-00${requestId}`, filestatustransition: fileStatusTransition, filename: file.filename ? file.filename : file.name, diff --git a/forms-flow-web/src/components/FOI/customComponents/CFRForm/index.tsx b/forms-flow-web/src/components/FOI/customComponents/CFRForm/index.tsx index e916cf549..3bacb9768 100644 --- a/forms-flow-web/src/components/FOI/customComponents/CFRForm/index.tsx +++ b/forms-flow-web/src/components/FOI/customComponents/CFRForm/index.tsx @@ -379,14 +379,14 @@ export const CFRForm = ({ if(requestState === StateEnum.peerreview.name){ return true; } - if (formHistory.length > 0 && [StateEnum.feeassessed.name, StateEnum.onhold.name, StateEnum.callforrecords.name].includes(requestState)) { + if (formHistory.length > 0 && [StateEnum.feeassessed.name, StateEnum.onhold.name, StateEnum.callforrecords.name, StateEnum.onholdother.name].includes(requestState)) { if (isMinistry) { return ['review', 'approved'].includes(initialFormData.formStatus) || isNewCFRForm; } else { return initialFormData.formStatus !== 'review'; } } - if (formData.balanceRemaining > 0 && [StateEnum.feeassessed.name, StateEnum.onhold.name].includes(requestState)) { + if (formData.balanceRemaining > 0 && [StateEnum.feeassessed.name, StateEnum.onhold.name, StateEnum.onholdother.name].includes(requestState)) { if (isMinistry) { return !['clarification', 'init'].includes(initialFormData.formStatus); } else { @@ -523,7 +523,8 @@ export const CFRForm = ({ const disableNewCfrFormBtn = () => { return(formData?.formStatus !== 'approved' || requestState === StateEnum.peerreview.name || (requestState !== StateEnum.callforrecords.name && - requestState !== StateEnum.feeassessed.name && requestState !== StateEnum.onhold.name) || (requestState === StateEnum.onhold.name && formData?.actualTotalDue > 0)); + requestState !== StateEnum.feeassessed.name && requestState !== StateEnum.onhold.name) || (requestState === StateEnum.onhold.name && formData?.actualTotalDue > 0) || + (requestState === StateEnum.onholdother.name && formData?.actualTotalDue > 0)); } const disableAmountPaid = () => { @@ -544,7 +545,7 @@ export const CFRForm = ({ } const isFeeWaiverDisabled = () => { - if(isMinistry || requestState === StateEnum.peerreview.name || (!isMinistry && (requestState !== StateEnum.onhold.name || formData?.formStatus !== 'approved'))) + if(isMinistry || requestState === StateEnum.peerreview.name || (!isMinistry && (requestState !== StateEnum.onhold.name || requestState !== StateEnum.onholdother.name || formData?.formStatus !== 'approved'))) return true; else return false; @@ -556,6 +557,8 @@ export const CFRForm = ({ component="form" sx={{ '& .MuiTextField-root': { my: 1, mx: 0 }, + '& .Mui-disabled': { '-webkit-text-fill-color': "black !important" }, + '& .MuiInputBase-root.Mui-disabled': { 'background-color': "#eee !important" }, }} autoComplete="off" > diff --git a/forms-flow-web/src/components/FOI/customComponents/Comments/DisplayComments.js b/forms-flow-web/src/components/FOI/customComponents/Comments/DisplayComments.js index d70a89b5d..a41738293 100644 --- a/forms-flow-web/src/components/FOI/customComponents/Comments/DisplayComments.js +++ b/forms-flow-web/src/components/FOI/customComponents/Comments/DisplayComments.js @@ -12,11 +12,6 @@ const DisplayComments = ({ comments, bcgovcode, currentUser, iaoassignedToList, const [fullnameList, setFullnameList] = useState(getFullnameList); - - useEffect(() => { - setTeamTagList(bcgovcode) - }, [fullnameList]) - const finduserbyuserid = (userId) => { let user = fullnameList.find(u => u.username === userId); return user && user.fullname ? user.fullname : userId; diff --git a/forms-flow-web/src/components/FOI/customComponents/ConfirmationModal/index.js b/forms-flow-web/src/components/FOI/customComponents/ConfirmationModal/index.js index d3eacd84a..4328e719f 100644 --- a/forms-flow-web/src/components/FOI/customComponents/ConfirmationModal/index.js +++ b/forms-flow-web/src/components/FOI/customComponents/ConfirmationModal/index.js @@ -18,9 +18,10 @@ import './confirmationmodal.scss'; import { StateEnum, StateTransitionCategories } from '../../../../constants/FOI/statusEnum'; import FileUpload from '../FileUpload' import MinistryApprovalModal from './MinistryApprovalModal'; -import { formatDate, calculateDaysRemaining, ConditionalComponent, isMinistryLogin } from "../../../../helper/FOI/helper"; +import { formatDate, calculateDaysRemaining, ConditionalComponent, isMinistryLogin, isOITeam } from "../../../../helper/FOI/helper"; import { MimeTypeList, MaxFileSizeInMB, MaxNumberOfFiles } from "../../../../constants/FOI/enum"; -import { getMessage, getAssignedTo, getMinistryGroup, getSelectedMinistry, getSelectedMinistryAssignedTo, getProcessingTeams, getUpdatedAssignedTo } from './util'; +import { getMessage, getAssignedTo, getMinistryGroup, getSelectedMinistry, getSelectedMinistryAssignedTo, getProcessingTeams, getUpdatedAssignedTo, getMessageForOITeam } from './util'; +import { isReadyForPublishing } from '../../FOIRequest/utils'; const useStyles = makeStyles((theme) => ({ root: { @@ -81,6 +82,9 @@ export default function ConfirmationModal({requestId, openModal, handleModal, st const userGroups = user?.groups?.map(group => group.slice(1)); let isMinistry = isMinistryLogin(userGroups); + + const openInfo = useSelector((reduxState) => reduxState.foiRequests.foiOpenInfoRequest); + const additionalFiles = useSelector((reduxState) => reduxState.foiRequests.foiOpenInfoAdditionalFiles); const cfrStatus = useSelector((reduxState) => reduxState.foiRequests.foiRequestCFRForm.status); const cfrFeeData = useSelector((reduxState) => reduxState.foiRequests.foiRequestCFRForm.feedata); const actualsFeeDataFields = [ @@ -118,6 +122,13 @@ export default function ConfirmationModal({requestId, openModal, handleModal, st } const isBtnDisabled = () => { + if (isOITeam) { + if (state === 'Ready to Publish') { + return !isReadyForPublishing(openInfo, additionalFiles, axisRequestId) + } else { + return false; + } + } if ((state.toLowerCase() === StateEnum.feeassessed.name.toLowerCase() && cfrStatus === 'init') || (state.toLowerCase() === StateEnum.feeassessed.name.toLowerCase() && estimatedTotalDue === 0) || (state.toLowerCase() === StateEnum.onhold.name.toLowerCase() && amountDue === 0) @@ -169,7 +180,6 @@ export default function ConfirmationModal({requestId, openModal, handleModal, st else if (saveRequestObject.requeststatuslabel === StateEnum.signoff.label && state.toLowerCase() === StateEnum.review.name.toLowerCase()) fileStatusTransition = StateTransitionCategories.signoffreview.name; - fileInfoList = files.map(file => { return { ministrycode: requestNumber.split("-")[0], @@ -182,11 +192,13 @@ export default function ConfirmationModal({requestId, openModal, handleModal, st handleModal(true, fileInfoList, files); } - let message = getMessage(saveRequestObject, state, axisRequestId, currentState, requestId, cfrStatus,allowStateChange,isAnyAmountPaid, estimatedTotalDue); + let message = userGroups.includes("OI Team") ? + getMessageForOITeam(state, openInfo, additionalFiles, axisRequestId) + : getMessage(saveRequestObject, state, axisRequestId, currentState, requestId, cfrStatus,allowStateChange,isAnyAmountPaid, estimatedTotalDue); const attchmentFileNameList = attachmentsArray?.map(_file => _file.filename); const getDaysRemaining = () => { - if (currentState?.toLowerCase() === StateEnum.closed.name.toLowerCase() && state.toLowerCase() !== StateEnum.closed.name.toLowerCase() && state.toLowerCase() !== StateEnum.onhold.name.toLowerCase()) { + if (currentState?.toLowerCase() === StateEnum.closed.name.toLowerCase() && state.toLowerCase() !== StateEnum.closed.name.toLowerCase() && state.toLowerCase() !== StateEnum.onhold.name.toLowerCase() && state.toLowerCase() !== StateEnum.onholdother.name.toLowerCase()) { return ( {daysRemainingLDD} DAYS REMAINING ); @@ -194,7 +206,9 @@ export default function ConfirmationModal({requestId, openModal, handleModal, st } const addorUpdateConfirmationModal = () => { - if (state.toLowerCase() === StateEnum.closed.name.toLowerCase() && + if(currentState?.toLowerCase() === StateEnum.onholdother.name.toLowerCase()) + return null; + else if (state.toLowerCase() === StateEnum.closed.name.toLowerCase() && currentState?.toLowerCase() !== StateEnum.closed.name.toLowerCase()) { return ( @@ -261,7 +275,7 @@ export default function ConfirmationModal({requestId, openModal, handleModal, st : null } - {(currentState?.toLowerCase() !== StateEnum.closed.name.toLowerCase() && [StateEnum.callforrecords.name.toLowerCase(), StateEnum.consult.name.toLowerCase(), StateEnum.onhold.name.toLowerCase()].includes(state.toLowerCase())) ? + {(currentState?.toLowerCase() !== StateEnum.closed.name.toLowerCase() && [StateEnum.callforrecords.name.toLowerCase(), StateEnum.consult.name.toLowerCase(), StateEnum.onhold.name.toLowerCase(), StateEnum.onholdother.name.toLowerCase()].includes(state.toLowerCase())) ? @@ -305,7 +319,7 @@ export default function ConfirmationModal({requestId, openModal, handleModal, st