From 8016d8795bca93e9b9c3f35f1f5575e60b759aca Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Fri, 24 Jan 2025 21:18:07 +0000 Subject: [PATCH 01/22] make the tab, put it on the page, fix the HSRC title --- .../ComplianceCodeManagement.tsx | 136 ++++++++---------- .../complianceCodes/ComplianceManagement.tsx | 42 ++++++ .../ComplianceReportManagement.tsx | 47 ++++++ .../PermitConditionsNavigation.tsx | 15 +- services/core-web/src/constants/routes.ts | 7 +- 5 files changed, 167 insertions(+), 80 deletions(-) create mode 100644 services/core-web/src/components/admin/complianceCodes/ComplianceManagement.tsx create mode 100644 services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx diff --git a/services/core-web/src/components/admin/complianceCodes/ComplianceCodeManagement.tsx b/services/core-web/src/components/admin/complianceCodes/ComplianceCodeManagement.tsx index 0df73cecce..cb3a41c0bb 100644 --- a/services/core-web/src/components/admin/complianceCodes/ComplianceCodeManagement.tsx +++ b/services/core-web/src/components/admin/complianceCodes/ComplianceCodeManagement.tsx @@ -4,7 +4,6 @@ import { change, Field, initialize, reset } from "redux-form"; import SearchOutlined from "@ant-design/icons/SearchOutlined"; import PlusOutlined from "@ant-design/icons/PlusOutlined"; import { Button, Input, Row, Table, Tag, Typography } from "antd"; - import CoreTable from "@mds/common/components/common/CoreTable"; import { renderActionsColumn, @@ -20,7 +19,6 @@ import RenderDate from "@mds/common/components/forms/RenderDate"; import RenderCancelButton from "@mds/common/components/forms/RenderCancelButton"; import { IComplianceArticle, ItemMap } from "@mds/common/interfaces/"; import { sortComplianceCodesByArticleNumber } from "@mds/common/redux/utils/helpers"; -import PermitConditionsNavigation from "../permitConditions/PermitConditionsNavigation"; import { fetchComplianceCodes, getFormattedComplianceCodes, @@ -92,7 +90,7 @@ const ComplianceCodeManagement: FC = () => { dispatch( openModal({ props: { - title: "Add Health and Safety Reclamation Code", + title: "Add Health, Safety and Reclamation Code", onSave: handleModalSave, }, content: ComplianceCodeViewEditForm, @@ -104,7 +102,7 @@ const ComplianceCodeManagement: FC = () => { dispatch( openModal({ props: { - title: "View Health and Safety Reclamation Code", + title: "View Health, Safety and Reclamation Code", isEditMode: false, initialValues: record, }, @@ -275,81 +273,71 @@ const ComplianceCodeManagement: FC = () => { return (
-
-

Permit Condition Management

-
- -
-

Health and Safety Reclamation Code

- - Manage HSRC in the system that are associated with Incidents, Variances, and{" "} - Reports. View the code to see long description and the regulatory authority. - - - - - + Manage HSRC in the system that are associated with Incidents, Variances, and{" "} + Reports. View the code to see long description and the regulatory authority. + + + - - ) : ( + Add Code + + + + ( + + + + + {isEditMode ? ( + <> + - )} - - - - - )} - sticky - /> - -
+ + ) : ( + + )} + + + + + )} + sticky + /> +
); }; diff --git a/services/core-web/src/components/admin/complianceCodes/ComplianceManagement.tsx b/services/core-web/src/components/admin/complianceCodes/ComplianceManagement.tsx new file mode 100644 index 0000000000..2a80c73134 --- /dev/null +++ b/services/core-web/src/components/admin/complianceCodes/ComplianceManagement.tsx @@ -0,0 +1,42 @@ +import React, { FC } from "react"; +import PermitConditionsNavigation from "../permitConditions/PermitConditionsNavigation"; +import { useParams } from "react-router-dom"; +import ComplianceCodeManagement from "./ComplianceCodeManagement"; +import ComplianceReportManagement from "./ComplianceReportManagement"; + +export const COMPLIANCE_TABS = ["codes", "reports"]; + +const ComplianceManagement: FC = () => { + const { tab } = useParams<{ tab: string }>(); + const activeTab = tab ?? COMPLIANCE_TABS[0]; + + const tabContent = { + [COMPLIANCE_TABS[0]]: { + title: "Health, Safety and Reclamation Code", + content: + }, + [COMPLIANCE_TABS[1]]: { + title: "Health, Safety and Reclamation Code Report", + content: + } + }; + const activeContent = tabContent[activeTab]; + + return ( +
+
+

Permit Condition Management

+
+ +
+

{activeContent.title}

+ {activeContent.content} +
+
+ ) +}; + +export default ComplianceManagement; \ No newline at end of file diff --git a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx new file mode 100644 index 0000000000..2a377b259f --- /dev/null +++ b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx @@ -0,0 +1,47 @@ +import React, { FC, useState } from "react"; +import { Button, Row, Typography } from "antd"; +import PlusOutlined from "@ant-design/icons/PlusOutlined"; +import CoreTable from "@mds/common/components/common/CoreTable"; + + +const ComplianceReportManagement: FC = () => { + const [isLoading, setIsLoading] = useState(false); + const codeRequiredReports = []; + const columns = []; + + const openAddModal = () => { + console.log('not implemented'); + }; + + return ( +
+ + Manage Code Required Reports that are associated to HSRC. Create a new report before adding it to a code in Health, Safety and Reclamation Code page. + + + + + + +
+ ); +}; + +export default ComplianceReportManagement; \ No newline at end of file diff --git a/services/core-web/src/components/admin/permitConditions/PermitConditionsNavigation.tsx b/services/core-web/src/components/admin/permitConditions/PermitConditionsNavigation.tsx index 4454bc4a61..a3d36f49bd 100644 --- a/services/core-web/src/components/admin/permitConditions/PermitConditionsNavigation.tsx +++ b/services/core-web/src/components/admin/permitConditions/PermitConditionsNavigation.tsx @@ -9,6 +9,7 @@ import { useFeatureFlag } from "@mds/common/providers/featureFlags/useFeatureFla import { userHasRole } from "@mds/common/redux/selectors/authenticationSelectors"; import { USER_ROLES } from "@mds/common/constants/environment"; import { Feature } from "@mds/common/utils/featureFlag"; +import { COMPLIANCE_TABS } from "../complianceCodes/ComplianceManagement"; interface AdminNavigationProps { activeButton: string; @@ -24,19 +25,27 @@ const PermitConditionsNavigation: FC = (props) => { const ifActiveButton = (route) => (includes(props.activeButton, route) ? "active-menu-btn" : ""); const complianceItem = { - label: "Health and Safety Reclamation Code", + label: "Health, Safety and Reclamation Code", key: "hsrc-management", id: ifActiveButton("hsrc-management"), icon: , children: [ { - key: "submenu-compliance-codes", + key: COMPLIANCE_TABS[0], label: ( - + Manage Compliance Codes ), }, + { + key: COMPLIANCE_TABS[1], + label: ( + + Manage Compliance Reports + + ) + } ], }; diff --git a/services/core-web/src/constants/routes.ts b/services/core-web/src/constants/routes.ts index 38d7dfdf61..a4b9c24e9c 100644 --- a/services/core-web/src/constants/routes.ts +++ b/services/core-web/src/constants/routes.ts @@ -53,7 +53,7 @@ import DamsDetailsPage from "@/components/mine/Tailings/DamsDetailsPage"; import ReportPage from "@/components/mine/Reports/ReportPage"; import ReportSteps from "@mds/common/components/reports/ReportSteps"; import ViewDigitalPermitCredential from "@/components/mine/DigitalPermitCredential/ViewDigitalPermitCredential"; -import ComplianceCodeManagement from "@/components/admin/complianceCodes/ComplianceCodeManagement"; +import ComplianceManagement from "@/components/admin/complianceCodes/ComplianceManagement"; import ProjectSubmissionStatusPage from "@mds/common/components/projectSummary/ProjectSubmissionStatusPage"; import ViewPermit from "@/components/mine/Permit/ViewPermit"; import { getEnvironment } from "@mds/common/utils/environmentUtils"; @@ -500,8 +500,9 @@ export const ADMIN_CONTACT_MANAGEMENT = { }; export const ADMIN_HSRC_COMPLIANCE_CODE_MANAGEMENT = { - route: "/admin/hsrc-management", - component: ComplianceCodeManagement, + route: "/admin/hsrc-management/:tab", + component: ComplianceManagement, + dynamicRoute: (tab: string) => `/admin/hsrc-management/${tab}`, helpKey: "HSRC-Code-Management", }; From 643993bc44767276e4c8a3798eb7a25a994d818e Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Fri, 24 Jan 2025 22:22:34 +0000 Subject: [PATCH 02/22] add pagination to mine report definitions in BE, take out of static content, and make a slice on the FE --- services/common/src/constants/API.ts | 2 +- .../src/redux/reducers/rootReducerShared.ts | 6 +++-- .../app/api/exports/response_models.py | 4 +--- .../resources/core_static_content_resource.py | 3 +-- .../reports/models/mine_report_definition.py | 13 ++++++++++ .../resources/mine_report_definition.py | 24 +++++++++++++++---- .../core-api/app/api/mines/response_models.py | 8 +++---- 7 files changed, 44 insertions(+), 16 deletions(-) diff --git a/services/common/src/constants/API.ts b/services/common/src/constants/API.ts index 9cebccbb5a..d83bd28d13 100644 --- a/services/common/src/constants/API.ts +++ b/services/common/src/constants/API.ts @@ -259,7 +259,7 @@ export const MINE_WORK_INFORMATION = (mineGuid, mineWorkInformationGuid) => export const REPORTS = (params = {}) => `/mines/reports?${queryString.stringify(params)}`; export const REPORT_SUBMISSIONS = (params?) => `/mines/reports/submissions?${queryString.stringify(params)}`; -export const MINE_REPORT_DEFINITIONS = () => `/mines/reports/definitions`; +export const MINE_REPORT_DEFINITIONS = (params = {}) => `/mines/reports/definitions?${queryString.stringify(params)}`; export const MINE_REPORTS = (mineGuid, params?) => `/mines/${mineGuid}/reports?${queryString.stringify(params)}`; export const MINE_REPORT = (mineGuid, mineReportGuid) => diff --git a/services/common/src/redux/reducers/rootReducerShared.ts b/services/common/src/redux/reducers/rootReducerShared.ts index 96a78bc871..4697e28240 100644 --- a/services/common/src/redux/reducers/rootReducerShared.ts +++ b/services/common/src/redux/reducers/rootReducerShared.ts @@ -34,19 +34,20 @@ import reportSubmissionReducer from "@mds/common/components/reports/reportSubmis import verifiableCredentialsReducer from "@mds/common/redux/slices/verifiableCredentialsSlice"; import regionsReducer from "@mds/common/redux/slices/regionsSlice"; import complianceCodeReducer, { complianceCodeReducerType } from "../slices/complianceCodesSlice"; +import complianceReportReducer, { complianceReportReducerType } from "../slices/complianceReportsSlice"; import spatialDataReducer, { spatialDataReducerType } from "../slices/spatialDataSlice"; import permitServiceReducer, { permitServiceReducerType } from "../slices/permitServiceSlice"; import searchConditionCategoriesReducer, { searchConditionCategoriesType, } from "../slices/permitConditionCategorySlice"; import helpReducer, { helpReducerType } from "../slices/helpSlice"; +import userReducer, { userReducerType } from "@mds/common/redux/slices/userSlice"; + const networkReducers = Object.fromEntries(Object.entries(NetworkReducerTypes).map(([key, value]) => [NetworkReducerTypes[key], createReducer(networkReducer, value)] )); -import userReducer, { userReducerType } from "@mds/common/redux/slices/userSlice"; - export const sharedReducer = { ...activityReducer, ...authenticationReducer, @@ -80,6 +81,7 @@ export const sharedReducer = { regions: regionsReducer, [spatialDataReducerType]: spatialDataReducer, [complianceCodeReducerType]: complianceCodeReducer, + [complianceReportReducerType]: complianceReportReducer, [permitServiceReducerType]: permitServiceReducer, [helpReducerType]: helpReducer, [searchConditionCategoriesType]: searchConditionCategoriesReducer, diff --git a/services/core-api/app/api/exports/response_models.py b/services/core-api/app/api/exports/response_models.py index 352f23c5dd..ac982b37c7 100644 --- a/services/core-api/app/api/exports/response_models.py +++ b/services/core-api/app/api/exports/response_models.py @@ -1,7 +1,7 @@ from app.extensions import api from flask_restx import fields -from app.api.mines.response_models import MINE_TENURE_TYPE_CODE_MODEL, MINE_COMMODITY_CODE_MODEL, MINE_DISTURBANCE_CODE_MODEL, MINE_STATUS_CODE_MODEL, MINE_REGION_OPTION, MINE_REPORT_DEFINITION_CATEGORIES, MINE_REPORT_DEFINITION_MODEL, MINE_REPORT_SUBMISSION_STATUS, EXEMPTION_FEE_STATUS_CODE_MODEL, PERMIT_STATUS_CODE_MODEL, PERMIT_CONDITION_CATEGORY_MODEL, PERMIT_CONDITION_TYPE_MODEL, PERMIT_AMENDEMENT_TYPE_CODE_MODEL, GOVERNMENT_AGENCY_TYPE_MODEL, CONSEQUENCE_CLASSIFICATION_STATUS_MODEL, ITRB_EXEMPTION_STATUS_MODEL, TSF_OPERATING_STATUS_MODEL +from app.api.mines.response_models import MINE_TENURE_TYPE_CODE_MODEL, MINE_COMMODITY_CODE_MODEL, MINE_DISTURBANCE_CODE_MODEL, MINE_STATUS_CODE_MODEL, MINE_REGION_OPTION, MINE_REPORT_DEFINITION_CATEGORIES, MINE_REPORT_SUBMISSION_STATUS, EXEMPTION_FEE_STATUS_CODE_MODEL, PERMIT_STATUS_CODE_MODEL, PERMIT_CONDITION_CATEGORY_MODEL, PERMIT_CONDITION_TYPE_MODEL, PERMIT_AMENDEMENT_TYPE_CODE_MODEL, GOVERNMENT_AGENCY_TYPE_MODEL, CONSEQUENCE_CLASSIFICATION_STATUS_MODEL, ITRB_EXEMPTION_STATUS_MODEL, TSF_OPERATING_STATUS_MODEL from app.api.mines.explosives_permit.response_models import EXPLOSIVES_PERMIT_STATUS_MODEL, EXPLOSIVES_PERMIT_DOCUMENT_TYPE_MODEL, EXPLOSIVES_PERMIT_MAGAZINE_TYPE_MODEL from app.api.projects.response_models import PROJECT_SUMMARY_STATUS_CODE_MODEL, PROJECT_SUMMARY_DOCUMENT_TYPE_MODEL, PROJECT_SUMMARY_AUTHORIZATION_TYPE_MODEL, PROJECT_SUMMARY_PERMIT_TYPE_MODEL, IRT_STATUS_CODE_MODEL, IRT_DOCUMENT_TYPE_MODEL, MAJOR_MINE_APPLICATION_STATUS_CODE_MODEL, MAJOR_MINE_APPLICATION_DOCUMENT_TYPE_MODEL, PROJECT_DECISION_PACKAGE_STATUS_CODE_MODEL, PROJECT_DECISION_PACKAGE_DOCUMENT_TYPE_MODEL from app.api.compliance.response_models import COMPLIANCE_ARTICLE_MODEL @@ -125,8 +125,6 @@ fields.List( fields.Nested(VARIANCE_DOCUMENT_CATEGORY_CODE), attribute='VarianceDocumentCategoryCode'), - 'mineReportDefinitionOptions': - fields.List(fields.Nested(MINE_REPORT_DEFINITION_MODEL), attribute='MineReportDefinition'), 'mineReportStatusOptions': fields.List( fields.Nested(MINE_REPORT_SUBMISSION_STATUS), diff --git a/services/core-api/app/api/exports/static_content/resources/core_static_content_resource.py b/services/core-api/app/api/exports/static_content/resources/core_static_content_resource.py index 7e0d0cee88..386e7c6270 100644 --- a/services/core-api/app/api/exports/static_content/resources/core_static_content_resource.py +++ b/services/core-api/app/api/exports/static_content/resources/core_static_content_resource.py @@ -29,7 +29,6 @@ from app.api.compliance.models.compliance_article import ComplianceArticle from app.api.variances.models.variance_application_status_code import VarianceApplicationStatusCode from app.api.variances.models.variance_document_category_code import VarianceDocumentCategoryCode -from app.api.mines.reports.models.mine_report_definition import MineReportDefinition from app.api.mines.reports.models.mine_report_category import MineReportCategory from app.api.mines.reports.models.mine_report_submission_status_code import MineReportSubmissionStatusCode from app.api.now_applications.models.activity_summary.activity_type import ActivityType @@ -77,7 +76,7 @@ PermitStatusCode, MineIncidentDocumentTypeCode, MineIncidentFollowupInvestigationType, MineIncidentDeterminationType, MineIncidentStatusCode, MineIncidentCategory, SubDivisionCode, ComplianceArticle, VarianceApplicationStatusCode, VarianceDocumentCategoryCode, - MineReportDefinition, MineReportCategory, MineReportSubmissionStatusCode, ActivityType, + MineReportCategory, MineReportSubmissionStatusCode, ActivityType, UnitType, NOWApplicationType, NOWApplicationStatus, NOWApplicationDocumentType, UndergroundExplorationType, NOWApplicationProgressStatus, NOWApplicationPermitType, MinePartyAppointmentType, NOWApplicationReviewType, BondType, BondStatus, BondDocumentType, diff --git a/services/core-api/app/api/mines/reports/models/mine_report_definition.py b/services/core-api/app/api/mines/reports/models/mine_report_definition.py index a17e020738..a134b842e0 100644 --- a/services/core-api/app/api/mines/reports/models/mine_report_definition.py +++ b/services/core-api/app/api/mines/reports/models/mine_report_definition.py @@ -4,6 +4,7 @@ from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.schema import FetchedValue from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy_filters import apply_pagination from app.api.utils.models_mixins import Base, AuditMixin from app.extensions import db @@ -75,6 +76,18 @@ def get_all(cls): except ValueError: return None + @classmethod + def get_paginated(cls, page, per_page): + query = cls.query.filter_by(active_ind=True) + records, pagination_details = apply_pagination(query, page, per_page) + return { + 'records': records.all(), + 'current_page': pagination_details.page_number, + 'total_pages': pagination_details.num_pages, + 'items_per_page': pagination_details.page_size, + 'total': pagination_details.total_results, + } + @classmethod def find_required_reports_by_category(cls, _mine_report_category): try: diff --git a/services/core-api/app/api/mines/reports/resources/mine_report_definition.py b/services/core-api/app/api/mines/reports/resources/mine_report_definition.py index b3f1b92bc8..020b0f25c2 100644 --- a/services/core-api/app/api/mines/reports/resources/mine_report_definition.py +++ b/services/core-api/app/api/mines/reports/resources/mine_report_definition.py @@ -15,12 +15,28 @@ from app.api.mines.reports.models.mine_report_due_date_type import MineReportDueDateType from app.api.mines.reports.models.mine_report_definition_compliance_article_xref import MineReportDefinitionComplianceArticleXref from app.api.utils.custom_reqparser import CustomReqparser -from app.api.mines.response_models import MINE_REPORT_DEFINITION_MODEL +from app.api.mines.response_models import PAGINATED_MINE_REPORT_DEFINITION_MODEL +PAGE_DEFAULT = 1 +PER_PAGE_DEFAULT = 50 class MineReportDefinitionListResource(Resource, UserMixin): - @api.marshal_with(MINE_REPORT_DEFINITION_MODEL, envelope='records', code=200, as_list=True) - @api.doc(description='returns the report definitions for possible reports.') + parser = reqparse.RequestParser() + parser.add_argument( + 'page', type=int, help='page for pagination', location='args', store_missing=False + ) + parser.add_argument( + 'per_page', type=int, help='records per page', location='args', store_missing=False + ) + @api.doc( + params={ + 'page': f'The page number of paginated records to return. Default: {PAGE_DEFAULT}', + 'per_page': f'The number of records to return per page. Default: {PER_PAGE_DEFAULT}', + }, + description='returns the report definitions for possible reports.') + @api.marshal_with(PAGINATED_MINE_REPORT_DEFINITION_MODEL, code=200) @requires_any_of([VIEW_ALL, MINESPACE_PROPONENT]) def get(self): - return MineReportDefinition.get_all() + page = request.args.get('page', PAGE_DEFAULT, type=int) + per_page = request.args.get('per_page', PER_PAGE_DEFAULT, type=int) + return MineReportDefinition.get_paginated(page, per_page) diff --git a/services/core-api/app/api/mines/response_models.py b/services/core-api/app/api/mines/response_models.py index 044648a094..5cae49bbb0 100644 --- a/services/core-api/app/api/mines/response_models.py +++ b/services/core-api/app/api/mines/response_models.py @@ -820,10 +820,6 @@ def format(self, value): 'is_prr_only': fields.Boolean, }) -MINE_REPORT_DEFINITION_MODEL = api.inherit('MineReportDefinition', MINE_REPORT_DEFINITION_BASE_MODEL, { - 'compliance_articles': fields.List(fields.Nested(COMPLIANCE_ARTICLE_MODEL)), -}) - PAGINATED_LIST = api.model( 'List', { 'current_page': fields.Integer, @@ -832,6 +828,10 @@ def format(self, value): 'total': fields.Integer, }) +PAGINATED_MINE_REPORT_DEFINITION_MODEL = api.inherit('MineReportDefinition', PAGINATED_LIST, { + 'records': fields.List(fields.Nested(MINE_REPORT_DEFINITION_BASE_MODEL)), +}) + PAGINATED_REPORT_LIST = api.inherit('ReportList', PAGINATED_LIST, { 'records': fields.List(fields.Nested(MINE_REPORT_MODEL)), }) From 270ddc4b998ece0eb0d27538c4cce04b2ecd14d2 Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Fri, 24 Jan 2025 22:23:00 +0000 Subject: [PATCH 03/22] missed adding the slice file --- .../redux/slices/complianceReportsSlice.ts | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 services/common/src/redux/slices/complianceReportsSlice.ts diff --git a/services/common/src/redux/slices/complianceReportsSlice.ts b/services/common/src/redux/slices/complianceReportsSlice.ts new file mode 100644 index 0000000000..c7c4dbb3a7 --- /dev/null +++ b/services/common/src/redux/slices/complianceReportsSlice.ts @@ -0,0 +1,61 @@ +import { hideLoading, showLoading } from "react-redux-loading-bar"; +import { ENVIRONMENT } from "@mds/common/constants/environment"; +import { createAppSlice, rejectHandler } from "@mds/common/redux/createAppSlice"; +import CustomAxios from "@mds/common/redux/customAxios"; +import * as API from "@mds/common/constants/API"; +import { IMineReportDefinition, ItemMap } from "@mds/common/interfaces"; +import { createItemMap } from "@mds/common/redux/utils/helpers"; + + +export const complianceReportReducerType = 'complianceReports' + +interface ComplianceReportState { + complianceReportMap: ItemMap; +}; + +const initialState: ComplianceReportState = { + complianceReportMap: null, +}; + +const createRequestHeader = REQUEST_HEADER.createRequestHeader; + +const complianceReportSlice = createAppSlice({ + name: complianceReportReducerType, + initialState, + reducers: (create) => ({ + fetchComplianceReports: create.asyncThunk( + async (searchParams = {}, thunkApi) => { + const headers = createRequestHeader(); + thunkApi.dispatch(showLoading()); + const resp = await CustomAxios({ + errorToastMessage: "Failed to load compliance reports", + }).get(`${ENVIRONMENT.apiUrl}${API.MINE_REPORT_DEFINITIONS(searchParams)}`, headers); + thunkApi.dispatch(hideLoading()); + return resp.data; + }, + { + fulfilled: (state: ComplianceReportState, action) => { + const records: IMineReportDefinition[] = action.payload.records ?? []; + const itemMap = createItemMap(records, "mine_report_definition_guid") + state.complianceReportMap = itemMap; + }, + rejected: (_state: ComplianceReportState, action) => { + rejectHandler(action); + } + } + ) + }), + selectors: { + getComplianceReports: (state): ItemMap => { + return state.complianceReportMap; + } + } +}); + +export const { + fetchComplianceReports +} = complianceReportSlice.actions; +export const { getComplianceReports } = complianceReportSlice.selectors; + +const complianceReportReducer = complianceReportSlice.reducer; +export default complianceReportReducer; \ No newline at end of file From b03c71ccae0c86ad332fe47b5d63afc2f2af3a17 Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Fri, 24 Jan 2025 23:01:31 +0000 Subject: [PATCH 04/22] get the data in the table and pagination working properly with BE pagination. TODO: I'm missing data fields, and the sort only applies to data currently on the page --- .../redux/slices/complianceReportsSlice.ts | 21 +++++- .../ComplianceReportManagement.tsx | 70 ++++++++++++++++--- 2 files changed, 80 insertions(+), 11 deletions(-) diff --git a/services/common/src/redux/slices/complianceReportsSlice.ts b/services/common/src/redux/slices/complianceReportsSlice.ts index c7c4dbb3a7..1827814997 100644 --- a/services/common/src/redux/slices/complianceReportsSlice.ts +++ b/services/common/src/redux/slices/complianceReportsSlice.ts @@ -3,7 +3,7 @@ import { ENVIRONMENT } from "@mds/common/constants/environment"; import { createAppSlice, rejectHandler } from "@mds/common/redux/createAppSlice"; import CustomAxios from "@mds/common/redux/customAxios"; import * as API from "@mds/common/constants/API"; -import { IMineReportDefinition, ItemMap } from "@mds/common/interfaces"; +import { IMineReportDefinition, IPageData, ItemMap } from "@mds/common/interfaces"; import { createItemMap } from "@mds/common/redux/utils/helpers"; @@ -11,10 +11,18 @@ export const complianceReportReducerType = 'complianceReports' interface ComplianceReportState { complianceReportMap: ItemMap; + reportPageData: IPageData, }; const initialState: ComplianceReportState = { complianceReportMap: null, + reportPageData: { + records: [], + current_page: 0, + items_per_page: 0, + total: 0, + total_pages: 0 + } }; const createRequestHeader = REQUEST_HEADER.createRequestHeader; @@ -31,13 +39,16 @@ const complianceReportSlice = createAppSlice({ errorToastMessage: "Failed to load compliance reports", }).get(`${ENVIRONMENT.apiUrl}${API.MINE_REPORT_DEFINITIONS(searchParams)}`, headers); thunkApi.dispatch(hideLoading()); + console.log(resp); return resp.data; }, { fulfilled: (state: ComplianceReportState, action) => { const records: IMineReportDefinition[] = action.payload.records ?? []; + const { current_page, items_per_page, total, total_pages } = action.payload; const itemMap = createItemMap(records, "mine_report_definition_guid") state.complianceReportMap = itemMap; + state.reportPageData = { records, current_page, items_per_page, total, total_pages }; }, rejected: (_state: ComplianceReportState, action) => { rejectHandler(action); @@ -48,6 +59,12 @@ const complianceReportSlice = createAppSlice({ selectors: { getComplianceReports: (state): ItemMap => { return state.complianceReportMap; + }, + getComplianceReportsAsList: (state): IMineReportDefinition[] => { + return Object.values(state.complianceReportMap); + }, + getComplianceReportPageData: (state): IPageData => { + return state.reportPageData; } } }); @@ -55,7 +72,7 @@ const complianceReportSlice = createAppSlice({ export const { fetchComplianceReports } = complianceReportSlice.actions; -export const { getComplianceReports } = complianceReportSlice.selectors; +export const { getComplianceReports, getComplianceReportsAsList, getComplianceReportPageData } = complianceReportSlice.selectors; const complianceReportReducer = complianceReportSlice.reducer; export default complianceReportReducer; \ No newline at end of file diff --git a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx index 2a377b259f..af1ea30865 100644 --- a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx +++ b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx @@ -1,18 +1,69 @@ -import React, { FC, useState } from "react"; +import React, { FC, useEffect, useState } from "react"; import { Button, Row, Typography } from "antd"; import PlusOutlined from "@ant-design/icons/PlusOutlined"; import CoreTable from "@mds/common/components/common/CoreTable"; +import { useDispatch, useSelector } from "react-redux"; +import { fetchComplianceReports, getComplianceReportPageData } from "@mds/common/redux/slices/complianceReportsSlice"; +import { renderActionsColumn, renderTextColumn } from "@mds/common/components/common/CoreTableCommonColumns"; +import { IMineReportDefinition } from "@mds/common/interfaces"; const ComplianceReportManagement: FC = () => { - const [isLoading, setIsLoading] = useState(false); - const codeRequiredReports = []; - const columns = []; + const dispatch = useDispatch(); + const reportPageData = useSelector(getComplianceReportPageData); + const reportDefinitions = reportPageData?.records ?? []; + const defaultLoaded = reportPageData.total > 0; + console.log(reportPageData); + const [isLoading, setIsLoading] = useState(defaultLoaded); + + + const fetchData = (page = 1, per_page = 50) => { + setIsLoading(true); + dispatch(fetchComplianceReports({ page, per_page })).then(setIsLoading(false)) + } + + useEffect(() => { + if (!isLoading && !defaultLoaded) { + fetchData(); + } + }, []); const openAddModal = () => { console.log('not implemented'); }; + const openViewModal = (record) => { + console.log('record', record) + }; + + const actions = [{ + key: "view", + label: "View", + clickFunction: (_, record) => openViewModal(record) + }]; + + // TODO: the sorting for report name only sorts the data that is currently on the page + // and also I don't have data for the columns that are currently commented out + + const columns = [ + renderTextColumn("report_name", "Report Name", true), // sort + // renderTextColumn("section", "Section"), // sort, filter + renderTextColumn("report_type", "Report Type"), // filter + // renderTextColumn("regulatory_authority", "Regulatory Authority"), // sort, filter + // renderTextColumn("office", "Office"), // sort, filter + renderActionsColumn({ actions }) + ]; + + const transformData = (reports: IMineReportDefinition[]) => { + return reports.map((r) => { + const report_type = r.is_prr_only ? "Permit Required Report" : "Core Required Report"; + return { + ...r, + report_type + }; + }) + } + return (
@@ -29,14 +80,15 @@ const ComplianceReportManagement: FC = () => { From 9bb1a23020481453116ba229139b89041cb35d57 Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Thu, 30 Jan 2025 21:37:55 +0000 Subject: [PATCH 05/22] remove mine report definition options from static content, get basics of sort/filter working (still a bit buggy) --- .../reports/ReportDefinitionFieldSelect.tsx | 4 +- .../reports/ReportDetailsForm-view.spec.tsx | 11 +- .../components/reports/ReportDetailsForm.tsx | 4 +- .../components/reports/ReportGetStarted.tsx | 3 +- services/common/src/constants/actionTypes.ts | 1 - services/common/src/constants/enums.ts | 2 + .../interfaces/complianceArticle.interface.ts | 4 +- .../reports/mineReportDefinition.interface.ts | 2 +- .../redux/reducers/staticContentReducer.js | 3 - .../redux/selectors/staticContentSelectors.ts | 45 +- .../redux/slices/complianceReportsSlice.ts | 52 +- services/common/src/tests/mocks/dataMocks.tsx | 894 ++++++++------ .../app/api/compliance/response_models.py | 7 +- services/core-api/app/api/mines/namespace.py | 2 +- .../reports/models/mine_report_definition.py | 96 +- .../resources/mine_report_definition.py | 42 - .../mine_report_definition_resource.py | 63 + .../core-api/app/api/mines/response_models.py | 8 +- .../Forms/reports/ReportFilterForm.tsx | 2 +- .../Forms/reports/ReportSearchForm.js | 2 +- .../Forms/reports/RequestReportForm.tsx | 5 +- .../ComplianceReportManagement.tsx | 89 +- .../admin/complianceCodes/ReportEditForm.tsx | 18 +- .../mine/Reports/MineReportInfo.tsx | 2 +- .../mine/Reports/MineReportTable.js | 2 +- .../mine/Tailings/MineTailingsInfoTabs.tsx | 2 +- .../Forms/reports/ReportPage-prr.spec.tsx | 11 +- .../Forms/reports/ReportPage.spec.tsx | 11 +- .../ReportPermitRequirementForm.spec.tsx | 11 +- .../Forms/reports/RequestReportForm.spec.tsx | 11 +- .../__snapshots__/ReportPage.spec.tsx.snap | 8 +- .../ComplianceCodeManagement.spec.tsx.snap | 1059 +++++++---------- .../DashboardRoutes.spec.js.snap | 14 +- ...spec.js => staticContentSelectors.spec.ts} | 5 - .../src/actions/staticContentActions.js | 6 - .../dashboard/mine/reports/ReportsTable.tsx | 17 +- .../src/constants/actionTypes.js | 1 - .../src/reducers/staticContentReducer.js | 22 +- .../dashboard/mine/reports/Reports.spec.tsx | 13 +- 39 files changed, 1343 insertions(+), 1211 deletions(-) delete mode 100644 services/core-api/app/api/mines/reports/resources/mine_report_definition.py create mode 100644 services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py rename services/core-web/src/tests/selectors/{staticContentSelectors.spec.js => staticContentSelectors.spec.ts} (99%) delete mode 100644 services/minespace-web/src/actions/staticContentActions.js diff --git a/services/common/src/components/reports/ReportDefinitionFieldSelect.tsx b/services/common/src/components/reports/ReportDefinitionFieldSelect.tsx index 4e0dcf3f4d..e495c161a9 100644 --- a/services/common/src/components/reports/ReportDefinitionFieldSelect.tsx +++ b/services/common/src/components/reports/ReportDefinitionFieldSelect.tsx @@ -2,9 +2,7 @@ import { formatComplianceCodeReportName } from "@mds/common/redux/utils/helpers" import React, { useEffect, useState } from "react"; import { useSelector } from "react-redux"; import { Field } from "redux-form"; - -import { getMineReportDefinitionOptions } from "@mds/common/redux/selectors/staticContentSelectors"; - +import { getMineReportDefinitionOptions } from "@mds/common/redux/slices/complianceReportsSlice"; import RenderSelect from "../forms/RenderSelect"; import { uniqBy } from "lodash"; import moment from "moment"; diff --git a/services/common/src/components/reports/ReportDetailsForm-view.spec.tsx b/services/common/src/components/reports/ReportDetailsForm-view.spec.tsx index 71e57f2e29..afd9e33017 100644 --- a/services/common/src/components/reports/ReportDetailsForm-view.spec.tsx +++ b/services/common/src/components/reports/ReportDetailsForm-view.spec.tsx @@ -8,6 +8,7 @@ import * as MOCK from "@mds/common/tests/mocks/dataMocks"; import { SystemFlagEnum } from "@mds/common/constants/enums"; import { USER_ROLES } from "@mds/common/constants/environment"; import { IMineReportSubmission } from "@mds/common/interfaces/reports/mineReportSubmission.interface"; +import { complianceReportReducerType } from "@mds/common/redux/slices/complianceReportsSlice"; const mineReportSubmission = MOCK.MINE_REPORT_SUBMISSIONS[0]; const initialState = { @@ -16,10 +17,18 @@ const initialState = { mineReportGuid: mineReportSubmission.mine_report_guid, }, [STATIC_CONTENT]: { - mineReportDefinitionOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.mineReportDefinitionOptions, permitConditionCategoryOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.permitConditionCategoryOptions, }, + [complianceReportReducerType]: { + reportPageData: { + records: MOCK.MINE_REPORT_DEFINITION_OPTIONS, + current_page: 1, + items_per_page: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, + total: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, + total_pages: 1 + } + }, [AUTHENTICATION]: { systemFlag: SystemFlagEnum.core, userAccessData: [USER_ROLES.role_edit_reports], diff --git a/services/common/src/components/reports/ReportDetailsForm.tsx b/services/common/src/components/reports/ReportDetailsForm.tsx index b24363f1ca..53c026314a 100644 --- a/services/common/src/components/reports/ReportDetailsForm.tsx +++ b/services/common/src/components/reports/ReportDetailsForm.tsx @@ -4,13 +4,11 @@ import { useDispatch, useSelector } from "react-redux"; import { arrayPush, change, Field, FieldArray, getFormValues } from "redux-form"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faTrashAlt } from "@fortawesome/pro-light-svg-icons"; - import { getDropdownPermitConditionCategoryOptions, - getMineReportDefinitionOptions, } from "@mds/common/redux/selectors/staticContentSelectors"; +import { getMineReportDefinitionOptions } from "@mds/common/redux/slices/complianceReportsSlice"; import ReportFileUpload from "@mds/common/components/reports/ReportFileUpload"; - import { FORM } from "@mds/common/constants/forms"; import { email, diff --git a/services/common/src/components/reports/ReportGetStarted.tsx b/services/common/src/components/reports/ReportGetStarted.tsx index 986fed4869..9e16db7042 100644 --- a/services/common/src/components/reports/ReportGetStarted.tsx +++ b/services/common/src/components/reports/ReportGetStarted.tsx @@ -15,9 +15,8 @@ import { required, requiredRadioButton } from "@mds/common/redux/utils/Validate" import RenderSelect from "../forms/RenderSelect"; import { getDropdownPermitConditionCategoryOptions, - getFormattedMineReportDefinitionOptions, - getMineReportDefinitionByGuid, } from "@mds/common/redux/selectors/staticContentSelectors"; +import { getFormattedMineReportDefinitionOptions, getMineReportDefinitionByGuid } from "@mds/common/redux/slices/complianceReportsSlice"; import { getPermits } from "@mds/common/redux/selectors/permitSelectors"; import { fetchPermits } from "@mds/common/redux/actionCreators/permitActionCreator"; import { getSystemFlag } from "@mds/common/redux/selectors/authenticationSelectors"; diff --git a/services/common/src/constants/actionTypes.ts b/services/common/src/constants/actionTypes.ts index b2b5f2ffc6..9895746241 100755 --- a/services/common/src/constants/actionTypes.ts +++ b/services/common/src/constants/actionTypes.ts @@ -34,7 +34,6 @@ export const STORE_MINE_DOCUMENTS = "STORE_MINE_DOCUMENTS"; export const STORE_MINE_TSF_REQUIRED_DOCUMENTS = "STORE_MINE_TSF_REQUIRED_DOCUMENTS"; export const STORE_MINE_COMPLIANCE_INFO = "STORE_MINE_COMPLIANCE_INFO"; export const STORE_PROVINCE_OPTIONS = "STORE_PROVINCE_OPTIONS"; -export const STORE_MINE_REPORT_DEFINITION_OPTIONS = "STORE_MINE_REPORT_DEFINITION_OPTIONS"; export const STORE_PARTY = "STORE_PARTY"; export const STORE_PARTIES = "STORE_PARTIES"; diff --git a/services/common/src/constants/enums.ts b/services/common/src/constants/enums.ts index 0c565fb32d..7ee95ba800 100644 --- a/services/common/src/constants/enums.ts +++ b/services/common/src/constants/enums.ts @@ -227,6 +227,8 @@ export enum REPORT_REGULATORY_AUTHORITY_CODES { export enum REPORT_REGULATORY_AUTHORITY_ENUM { CPO = "Chief Permitting Officer", CIM = "Chief Inspector of Mines", + Both = "Both", + } export enum MineReportType { diff --git a/services/common/src/interfaces/complianceArticle.interface.ts b/services/common/src/interfaces/complianceArticle.interface.ts index fa90064812..38daba3996 100644 --- a/services/common/src/interfaces/complianceArticle.interface.ts +++ b/services/common/src/interfaces/complianceArticle.interface.ts @@ -11,8 +11,8 @@ export interface IComplianceArticle { sub_paragraph: string; description: string; long_description: string; - effective_date: Date; - expiry_date: Date; + effective_date: string; + expiry_date: string; help_reference_link: string; cim_or_cpo: string; reports: IMineReportDefinition[]; diff --git a/services/common/src/interfaces/reports/mineReportDefinition.interface.ts b/services/common/src/interfaces/reports/mineReportDefinition.interface.ts index 40adc70c05..376a4a0c6f 100644 --- a/services/common/src/interfaces/reports/mineReportDefinition.interface.ts +++ b/services/common/src/interfaces/reports/mineReportDefinition.interface.ts @@ -10,7 +10,7 @@ export interface IMineReportDefinition { active_ind: boolean; categories: IMineReportDefinitionCategory[]; compliance_articles: IComplianceArticle[]; - default_due_date?: number; + default_due_date?: string; description: string; due_date_period_months?: number; is_common: boolean; diff --git a/services/common/src/redux/reducers/staticContentReducer.js b/services/common/src/redux/reducers/staticContentReducer.js index 08f51b56ff..681f92a2b2 100644 --- a/services/common/src/redux/reducers/staticContentReducer.js +++ b/services/common/src/redux/reducers/staticContentReducer.js @@ -27,7 +27,6 @@ const initialState = { informationRequirementsTableDocumentTypes: [], majorMineApplicationStatusCodes: [], majorMineApplicationDocumentTypes: [], - mineReportDefinitionOptions: [], mineReportStatusOptions: [], mineReportCategoryOptions: [], partyRelationshipTypes: [], @@ -113,8 +112,6 @@ export const getMajorMinesApplicationStatusCodes = (state) => state[STATIC_CONTENT].majorMineApplicationStatusCodes; export const getMajorMinesApplicationDocumentTypes = (state) => state[STATIC_CONTENT].majorMineApplicationDocumentTypes; -export const getMineReportDefinitionOptions = (state) => - state[STATIC_CONTENT].mineReportDefinitionOptions; export const getMineReportStatusOptions = (state) => state[STATIC_CONTENT].mineReportStatusOptions; export const getMineReportCategoryOptions = (state) => state[STATIC_CONTENT].mineReportCategoryOptions; diff --git a/services/common/src/redux/selectors/staticContentSelectors.ts b/services/common/src/redux/selectors/staticContentSelectors.ts index 9b88c10965..74bfedee61 100644 --- a/services/common/src/redux/selectors/staticContentSelectors.ts +++ b/services/common/src/redux/selectors/staticContentSelectors.ts @@ -5,11 +5,9 @@ import { createLabelHash, createDropDownList, compareCodes, - formatComplianceCodeReportName, } from "../utils/helpers"; import { RootState } from "@mds/common/redux/rootState"; import { getMunicipalityOptions } from "../reducers/staticContentReducer"; -import { IMineReportDefinition } from "@mds/common/interfaces/reports/mineReportDefinition.interface"; export const { getStaticContentLoadingIsComplete, @@ -18,7 +16,6 @@ export const { getMineTenureTypeOptions, getMineCommodityOptions, getMineDisturbanceOptions, - getMineReportDefinitionOptions, getMineReportStatusOptions, getMineReportCategoryOptions, getProvinceOptions, @@ -80,7 +77,7 @@ const getOptions = (transformOptionsFunc, showActiveOnly) => { : options; }; -const createSelectorWrapper = ( +export const createSelectorWrapper = ( getOptionsMethod, transformOptionsMethod, transformOptionsFuncArgs = [] @@ -460,46 +457,6 @@ export const getVarianceDocumentCategoryOptionsHash = createSelector( createLabelHash ); -export const getDropdownMineReportDefinitionOptions = createSelectorWrapper( - getMineReportDefinitionOptions, - createDropDownList, - ["report_name", "mine_report_definition_guid", "active_ind"] -); - -export const getFormattedMineReportDefinitionOptions = createSelectorWrapper( - getMineReportDefinitionOptions, - (options: IMineReportDefinition[]) => { - return options - .map((item) => { - return { - label: formatComplianceCodeReportName(item), - value: item.mine_report_definition_guid, - isActive: item.active_ind, - is_common: item.is_common, - report_name: item.report_name, - }; - }) - .sort((a, b) => a.label.localeCompare(b.label)); - } -); - -export const getMineReportDefinitionByGuid = (mineReportDefinitionGuid: string) => - createSelector([getMineReportDefinitionOptions], (reportDefs) => { - return reportDefs.find((r) => r.mine_report_definition_guid === mineReportDefinitionGuid); - }); - -export const getMineReportDefinitionHash = createSelector( - getMineReportDefinitionOptions, - (options) => - options.reduce( - (map, mine_report_definition) => ({ - [mine_report_definition.mine_report_definition_guid]: mine_report_definition, - ...map, - }), - {} - ) -); - export const getDropdownMineReportCategoryOptions = createSelectorWrapper( getMineReportCategoryOptions, createDropDownList, diff --git a/services/common/src/redux/slices/complianceReportsSlice.ts b/services/common/src/redux/slices/complianceReportsSlice.ts index 1827814997..16de071f43 100644 --- a/services/common/src/redux/slices/complianceReportsSlice.ts +++ b/services/common/src/redux/slices/complianceReportsSlice.ts @@ -4,18 +4,18 @@ import { createAppSlice, rejectHandler } from "@mds/common/redux/createAppSlice" import CustomAxios from "@mds/common/redux/customAxios"; import * as API from "@mds/common/constants/API"; import { IMineReportDefinition, IPageData, ItemMap } from "@mds/common/interfaces"; -import { createItemMap } from "@mds/common/redux/utils/helpers"; +import { createDropDownList, createItemMap, formatComplianceCodeReportName } from "@mds/common/redux/utils/helpers"; +import { createSelectorWrapper } from "../selectors/staticContentSelectors"; +import { createSelector } from "@reduxjs/toolkit"; export const complianceReportReducerType = 'complianceReports' interface ComplianceReportState { - complianceReportMap: ItemMap; reportPageData: IPageData, }; const initialState: ComplianceReportState = { - complianceReportMap: null, reportPageData: { records: [], current_page: 0, @@ -39,15 +39,12 @@ const complianceReportSlice = createAppSlice({ errorToastMessage: "Failed to load compliance reports", }).get(`${ENVIRONMENT.apiUrl}${API.MINE_REPORT_DEFINITIONS(searchParams)}`, headers); thunkApi.dispatch(hideLoading()); - console.log(resp); return resp.data; }, { fulfilled: (state: ComplianceReportState, action) => { const records: IMineReportDefinition[] = action.payload.records ?? []; const { current_page, items_per_page, total, total_pages } = action.payload; - const itemMap = createItemMap(records, "mine_report_definition_guid") - state.complianceReportMap = itemMap; state.reportPageData = { records, current_page, items_per_page, total, total_pages }; }, rejected: (_state: ComplianceReportState, action) => { @@ -57,22 +54,53 @@ const complianceReportSlice = createAppSlice({ ) }), selectors: { - getComplianceReports: (state): ItemMap => { - return state.complianceReportMap; + getMineReportDefinitionHash: (state): ItemMap => { + return createItemMap(state.reportPageData.records, "mine_report_definition_guid") }, - getComplianceReportsAsList: (state): IMineReportDefinition[] => { - return Object.values(state.complianceReportMap); + getMineReportDefinitionOptions: (state): IMineReportDefinition[] => { + return state.reportPageData.records; }, getComplianceReportPageData: (state): IPageData => { return state.reportPageData; - } + }, + } }); +export const { getMineReportDefinitionHash, getMineReportDefinitionOptions, getComplianceReportPageData, +} = complianceReportSlice.selectors; + +export const getDropdownMineReportDefinitionOptions = createSelectorWrapper( + getMineReportDefinitionOptions, + createDropDownList, ["report_name", "mine_report_definition_guid", "active_ind"] +); + +export const getFormattedMineReportDefinitionOptions = createSelectorWrapper( + getMineReportDefinitionOptions, + (options: IMineReportDefinition[]) => { + return options + .map((item) => { + return { + label: formatComplianceCodeReportName(item), + value: item.mine_report_definition_guid, + isActive: item.active_ind, + is_common: item.is_common, + report_name: item.report_name, + }; + }) + .sort((a, b) => a.label.localeCompare(b.label)); + } +); + +export const getMineReportDefinitionByGuid = (mineReportDefinitionGuid: string) => + createSelector([getMineReportDefinitionHash], (reportMap) => { + return reportMap[mineReportDefinitionGuid] + }); + export const { fetchComplianceReports } = complianceReportSlice.actions; -export const { getComplianceReports, getComplianceReportsAsList, getComplianceReportPageData } = complianceReportSlice.selectors; + const complianceReportReducer = complianceReportSlice.reducer; export default complianceReportReducer; \ No newline at end of file diff --git a/services/common/src/tests/mocks/dataMocks.tsx b/services/common/src/tests/mocks/dataMocks.tsx index 4b081a2e8e..69558ce084 100644 --- a/services/common/src/tests/mocks/dataMocks.tsx +++ b/services/common/src/tests/mocks/dataMocks.tsx @@ -10,6 +10,7 @@ import { IPermit, IMineDocument, IPermitAmendment, + IMineReportDefinition, } from "@mds/common/interfaces"; export const createMockHeader = () => ({ @@ -528,6 +529,529 @@ export const PAGE_DATA = { records: [], }; +export const MINE_REPORT_DEFINITION_OPTIONS: IMineReportDefinition[] = [ + { + mine_report_definition_guid: "5f4f4727-4ecd-4a04-8929-2e8a5e03996d", + report_name: "TSF, WSF or Dam As-built Report", + description: "", + due_date_period_months: 12, + mine_report_due_date_type: "FIS", + default_due_date: "2020-03-31", + categories: [ + { + mine_report_category: "GTC", description: "Geotechnical", + active_ind: true + }, + { + mine_report_category: "TSF", description: "Tailings Storage Facility", + active_ind: true + }, + ], + compliance_articles: [ + { + compliance_article_id: 157, + article_act_code: "HSRCM", + section: "10", + sub_section: "5", + paragraph: "1", + sub_paragraph: "1", + description: "General", + long_description: "General", + effective_date: "1970-01-01", + expiry_date: "2016-07-19", + help_reference_link: "", + cim_or_cpo: "Both", + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "6eda0c36-8748-4072-83c9-0fcdf270d36f", + report_name: "Annual DSI", + description: "", + due_date_period_months: 12, + mine_report_due_date_type: "FIS", + default_due_date: "2020-03-31", + categories: [ + { + mine_report_category: "GTC", description: "Geotechnical", + active_ind: true + }, + { + mine_report_category: "TSF", description: "Tailings Storage Facility", + active_ind: true + }, + ], + compliance_articles: [ + { + compliance_article_id: 155, + article_act_code: "HSRCM", + section: "10", + sub_section: "4", + paragraph: "4", + sub_paragraph: null, + description: "General", + long_description: "General", + effective_date: "2016-07-20", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: null, + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "a1f02190-908b-4459-9dfe-6382282dfd30", + report_name: "OHSC Annual Report", + description: "", + due_date_period_months: 12, + mine_report_due_date_type: "FIS", + default_due_date: "2020-03-31", + categories: [{ + mine_report_category: "H&S", description: "Health and Safety", + active_ind: true + }], + compliance_articles: [ + { + compliance_article_id: 114, + article_act_code: "HSRCM", + section: "1", + sub_section: "9", + paragraph: "3", + sub_paragraph: null, + description: "General", + long_description: "General", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: "CIM", + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "1f4dac68-2131-4b12-9cdd-9e2bb86e50a2", + report_name: "Right to Refuse Report", + description: "", + due_date_period_months: null, + mine_report_due_date_type: "EVT", + default_due_date: null, + categories: [{ + mine_report_category: "H&S", description: "Health and Safety", + active_ind: true + }], + compliance_articles: [ + { + compliance_article_id: 59, + article_act_code: "HSRCM", + section: "1", + sub_section: "10", + paragraph: "7", + sub_paragraph: null, + description: "Manager Investigates", + long_description: "Manager Investigates", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: "CPO", + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "f650d2b6-96e4-43f0-9d15-6fbead2d5978", + report_name: "Report of MERP Test", + description: "", + due_date_period_months: 12, + mine_report_due_date_type: "FIS", + default_due_date: "2020-03-31", + categories: [{ + mine_report_category: "H&S", description: "Health and Safety", + active_ind: true + }], + compliance_articles: [ + { + compliance_article_id: 370, + article_act_code: "HSRCM", + section: "3", + sub_section: "7", + paragraph: "1", + sub_paragraph: null, + description: "Mine Emergency Response Plan", + long_description: "Mine Emergency Response Plan", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: "Both", + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "c9baac63-1578-47eb-847d-a992e0aeba67", + report_name: "Underground Fueling Station Report", + description: "", + due_date_period_months: null, + mine_report_due_date_type: "PMT", + default_due_date: null, + categories: [ + { + mine_report_category: "GSE", description: "Geoscience and Environmental", + active_ind: true + }, + { + mine_report_category: "GTC", description: "Geotechnical", + active_ind: true + }, + ], + compliance_articles: [ + { + compliance_article_id: 510, + article_act_code: "HSRCM", + section: "4", + sub_section: "3", + paragraph: "3", + sub_paragraph: null, + description: "Underground Fuelling Stations", + long_description: "Underground Fuelling Stations", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: null, + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "f5dec476-cb13-430a-a85e-81e5bbe666e4", + report_name: "Underground Oil and Grease Storage Area Report", + description: "", + due_date_period_months: null, + mine_report_due_date_type: "PMT", + default_due_date: null, + categories: [ + { + mine_report_category: "GSE", description: "Geoscience and Environmental", + active_ind: true + }, + { + mine_report_category: "GTC", description: "Geotechnical", + active_ind: true + }, + ], + compliance_articles: [ + { + compliance_article_id: 511, + article_act_code: "HSRCM", + section: "4", + sub_section: "3", + paragraph: "4", + sub_paragraph: null, + description: "Underground Oil and Grease Storage Areas", + long_description: "Underground Oil and Grease Storage Areas", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: "CIM", + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "ec11deae-1187-42e7-a13c-17a25743448f", + report_name: "Flammable Gas Report", + description: "", + due_date_period_months: null, + mine_report_due_date_type: "EVT", + default_due_date: null, + categories: [{ + mine_report_category: "H&S", description: "Health and Safety", + active_ind: true + }], + compliance_articles: [ + { + compliance_article_id: 702, + article_act_code: "HSRCM", + section: "6", + sub_section: "42", + paragraph: "3", + sub_paragraph: null, + description: "Reporting", + long_description: "Reporting", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: "CPO", + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "e2f72b23-d7d9-4a11-9139-e86b3c6f4bc4", + report_name: "Free Fall Tests Report", + description: "", + due_date_period_months: null, + mine_report_due_date_type: "EVT", + default_due_date: null, + categories: [{ + mine_report_category: "H&S", description: "Health and Safety", + active_ind: true + }], + compliance_articles: [ + { + compliance_article_id: 793, + article_act_code: "HSRCM", + section: "7", + sub_section: "5", + paragraph: "13", + sub_paragraph: null, + description: "Free Fall Tests - Report", + long_description: "Free Fall Tests - Report", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: "Both", + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "b820a0e0-1d0c-4460-8787-c813484742c6", + report_name: "Defective Explosives Report", + description: "", + due_date_period_months: null, + mine_report_due_date_type: "EVT", + default_due_date: null, + categories: [{ + mine_report_category: "H&S", description: "Health and Safety", + active_ind: true + }], + compliance_articles: [ + { + compliance_article_id: 909, + article_act_code: "HSRCM", + section: "8", + sub_section: "3", + paragraph: "4", + sub_paragraph: null, + description: "Defective Explosives", + long_description: "Defective Explosives", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: "CIM", + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "82abcaf9-e432-423d-b110-73acbfa9c94f", + report_name: "Careless Acts Report", + description: "", + due_date_period_months: null, + mine_report_due_date_type: "EVT", + default_due_date: null, + categories: [{ + mine_report_category: "H&S", description: "Health and Safety", + active_ind: true + }], + compliance_articles: [ + { + compliance_article_id: 914, + article_act_code: "HSRCM", + section: "8", + sub_section: "3", + paragraph: "9", + sub_paragraph: null, + description: "Careless Acts", + long_description: "Careless Acts", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: "CPO", + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "d7f7b95c-4f60-4125-8f8c-f843d1be462e", + report_name: "Drilling Precaution Procedures Report", + description: "", + due_date_period_months: null, + mine_report_due_date_type: "PMT", + default_due_date: null, + categories: [{ + mine_report_category: "H&S", description: "Health and Safety", + active_ind: true + }], + compliance_articles: [ + { + compliance_article_id: 955, + article_act_code: "HSRCM", + section: "8", + sub_section: "7", + paragraph: "2", + sub_paragraph: null, + description: "Misfired Holes and Bootlegs - Drilling Precautions", + long_description: "Misfired Holes and Bootlegs - Drilling Precautions", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: "Both", + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "61b87acf-8604-4975-8172-282bbf2b59fc", + report_name: "Annual Summary of Exploration Activities", + description: "", + due_date_period_months: 12, + mine_report_due_date_type: "FIS", + default_due_date: "2020-03-31", + categories: [ + { + mine_report_category: "H&S", description: "Health and Safety", + active_ind: true + }, + { + mine_report_category: "GSE", description: "Geoscience and Environmental", + active_ind: true + }, + { + mine_report_category: "GTC", description: "Geotechnical", + active_ind: true + }, + ], + compliance_articles: [ + { + compliance_article_id: 969, + article_act_code: "HSRCM", + section: "9", + sub_section: "2", + paragraph: "1", + sub_paragraph: null, + description: "Notice Requirements", + long_description: "Notice Requirements", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: null, + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "ba6f37df-5ced-4664-9a5e-5a5e93a09748", + report_name: "Management Plan for Riparian Area", + description: "", + due_date_period_months: null, + mine_report_due_date_type: "PMT", + default_due_date: null, + categories: [{ + mine_report_category: "GSE", description: "Geoscience and Environmental", + active_ind: true + }], + compliance_articles: [ + { + compliance_article_id: 978, + article_act_code: "HSRCM", + section: "9", + sub_section: "5", + paragraph: "1", + sub_paragraph: null, + description: "Riparian Setback Distances", + long_description: "Riparian Setback Distances", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: "CIM", + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, + { + mine_report_definition_guid: "c387b2a2-7bf4-4a29-9e9a-faa38a838b2d", + report_name: "Terrain Stability Remediation Plan", + description: "", + due_date_period_months: null, + mine_report_due_date_type: "EVT", + default_due_date: null, + categories: [ + { + mine_report_category: "GSE", description: "Geoscience and Environmental", + active_ind: true + }, + { + mine_report_category: "H&S", description: "Health and Safety", + active_ind: true + }, + ], + compliance_articles: [ + { + compliance_article_id: 980, + article_act_code: "HSRCM", + section: "9", + sub_section: "7", + paragraph: "1", + sub_paragraph: null, + description: "Terrain", + long_description: "Terrain", + effective_date: "1970-01-01", + expiry_date: "9999-12-31", + help_reference_link: "", + cim_or_cpo: "CPO", + reports: [] + }, + ], + active_ind: false, + is_common: false, + is_prr_only: false + }, +]; + export const COORDINATES = [48.70707, -122.489504]; export const STATUS_OPTIONS = { records: [ @@ -3966,372 +4490,6 @@ export const BULK_STATIC_CONTENT_RESPONSE = { active_ind: true, }, ], - mineReportDefinitionOptions: [ - { - mine_report_definition_guid: "5f4f4727-4ecd-4a04-8929-2e8a5e03996d", - report_name: "TSF, WSF or Dam As-built Report", - description: "", - due_date_period_months: 12, - mine_report_due_date_type: "FIS", - default_due_date: "2020-03-31", - categories: [ - { mine_report_category: "GTC", description: "Geotechnical" }, - { mine_report_category: "TSF", description: "Tailings Storage Facility" }, - ], - compliance_articles: [ - { - compliance_article_id: 157, - article_act_code: "HSRCM", - section: "10", - sub_section: "5", - paragraph: "1", - sub_paragraph: "1", - description: "General", - long_description: "General", - effective_date: "1970-01-01", - expiry_date: "2016-07-19", - }, - ], - }, - { - mine_report_definition_guid: "6eda0c36-8748-4072-83c9-0fcdf270d36f", - report_name: "Annual DSI", - description: "", - due_date_period_months: 12, - mine_report_due_date_type: "FIS", - default_due_date: "2020-03-31", - categories: [ - { mine_report_category: "GTC", description: "Geotechnical" }, - { mine_report_category: "TSF", description: "Tailings Storage Facility" }, - ], - compliance_articles: [ - { - compliance_article_id: 155, - article_act_code: "HSRCM", - section: "10", - sub_section: "4", - paragraph: "4", - sub_paragraph: null, - description: "General", - long_description: "General", - effective_date: "2016-07-20", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "a1f02190-908b-4459-9dfe-6382282dfd30", - report_name: "OHSC Annual Report", - description: "", - due_date_period_months: 12, - mine_report_due_date_type: "FIS", - default_due_date: "2020-03-31", - categories: [{ mine_report_category: "H&S", description: "Health and Safety" }], - compliance_articles: [ - { - compliance_article_id: 114, - article_act_code: "HSRCM", - section: "1", - sub_section: "9", - paragraph: "3", - sub_paragraph: null, - description: "General", - long_description: "General", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "1f4dac68-2131-4b12-9cdd-9e2bb86e50a2", - report_name: "Right to Refuse Report", - description: "", - due_date_period_months: null, - mine_report_due_date_type: "EVT", - default_due_date: null, - categories: [{ mine_report_category: "H&S", description: "Health and Safety" }], - compliance_articles: [ - { - compliance_article_id: 59, - article_act_code: "HSRCM", - section: "1", - sub_section: "10", - paragraph: "7", - sub_paragraph: null, - description: "Manager Investigates", - long_description: "Manager Investigates", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "f650d2b6-96e4-43f0-9d15-6fbead2d5978", - report_name: "Report of MERP Test", - description: "", - due_date_period_months: 12, - mine_report_due_date_type: "FIS", - default_due_date: "2020-03-31", - categories: [{ mine_report_category: "H&S", description: "Health and Safety" }], - compliance_articles: [ - { - compliance_article_id: 370, - article_act_code: "HSRCM", - section: "3", - sub_section: "7", - paragraph: "1", - sub_paragraph: null, - description: "Mine Emergency Response Plan", - long_description: "Mine Emergency Response Plan", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "c9baac63-1578-47eb-847d-a992e0aeba67", - report_name: "Underground Fueling Station Report", - description: "", - due_date_period_months: null, - mine_report_due_date_type: "PMT", - default_due_date: null, - categories: [ - { mine_report_category: "GSE", description: "Geoscience and Environmental" }, - { mine_report_category: "GTC", description: "Geotechnical" }, - ], - compliance_articles: [ - { - compliance_article_id: 510, - article_act_code: "HSRCM", - section: "4", - sub_section: "3", - paragraph: "3", - sub_paragraph: null, - description: "Underground Fuelling Stations", - long_description: "Underground Fuelling Stations", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "f5dec476-cb13-430a-a85e-81e5bbe666e4", - report_name: "Underground Oil and Grease Storage Area Report", - description: "", - due_date_period_months: null, - mine_report_due_date_type: "PMT", - default_due_date: null, - categories: [ - { mine_report_category: "GSE", description: "Geoscience and Environmental" }, - { mine_report_category: "GTC", description: "Geotechnical" }, - ], - compliance_articles: [ - { - compliance_article_id: 511, - article_act_code: "HSRCM", - section: "4", - sub_section: "3", - paragraph: "4", - sub_paragraph: null, - description: "Underground Oil and Grease Storage Areas", - long_description: "Underground Oil and Grease Storage Areas", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "ec11deae-1187-42e7-a13c-17a25743448f", - report_name: "Flammable Gas Report", - description: "", - due_date_period_months: null, - mine_report_due_date_type: "EVT", - default_due_date: null, - categories: [{ mine_report_category: "H&S", description: "Health and Safety" }], - compliance_articles: [ - { - compliance_article_id: 702, - article_act_code: "HSRCM", - section: "6", - sub_section: "42", - paragraph: "3", - sub_paragraph: null, - description: "Reporting", - long_description: "Reporting", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "e2f72b23-d7d9-4a11-9139-e86b3c6f4bc4", - report_name: "Free Fall Tests Report", - description: "", - due_date_period_months: null, - mine_report_due_date_type: "EVT", - default_due_date: null, - categories: [{ mine_report_category: "H&S", description: "Health and Safety" }], - compliance_articles: [ - { - compliance_article_id: 793, - article_act_code: "HSRCM", - section: "7", - sub_section: "5", - paragraph: "13", - sub_paragraph: null, - description: "Free Fall Tests - Report", - long_description: "Free Fall Tests - Report", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "b820a0e0-1d0c-4460-8787-c813484742c6", - report_name: "Defective Explosives Report", - description: "", - due_date_period_months: null, - mine_report_due_date_type: "EVT", - default_due_date: null, - categories: [{ mine_report_category: "H&S", description: "Health and Safety" }], - compliance_articles: [ - { - compliance_article_id: 909, - article_act_code: "HSRCM", - section: "8", - sub_section: "3", - paragraph: "4", - sub_paragraph: null, - description: "Defective Explosives", - long_description: "Defective Explosives", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "82abcaf9-e432-423d-b110-73acbfa9c94f", - report_name: "Careless Acts Report", - description: "", - due_date_period_months: null, - mine_report_due_date_type: "EVT", - default_due_date: null, - categories: [{ mine_report_category: "H&S", description: "Health and Safety" }], - compliance_articles: [ - { - compliance_article_id: 914, - article_act_code: "HSRCM", - section: "8", - sub_section: "3", - paragraph: "9", - sub_paragraph: null, - description: "Careless Acts", - long_description: "Careless Acts", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "d7f7b95c-4f60-4125-8f8c-f843d1be462e", - report_name: "Drilling Precaution Procedures Report", - description: "", - due_date_period_months: null, - mine_report_due_date_type: "PMT", - default_due_date: null, - categories: [{ mine_report_category: "H&S", description: "Health and Safety" }], - compliance_articles: [ - { - compliance_article_id: 955, - article_act_code: "HSRCM", - section: "8", - sub_section: "7", - paragraph: "2", - sub_paragraph: null, - description: "Misfired Holes and Bootlegs - Drilling Precautions", - long_description: "Misfired Holes and Bootlegs - Drilling Precautions", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "61b87acf-8604-4975-8172-282bbf2b59fc", - report_name: "Annual Summary of Exploration Activities", - description: "", - due_date_period_months: 12, - mine_report_due_date_type: "FIS", - default_due_date: "2020-03-31", - categories: [ - { mine_report_category: "H&S", description: "Health and Safety" }, - { mine_report_category: "GSE", description: "Geoscience and Environmental" }, - { mine_report_category: "GTC", description: "Geotechnical" }, - ], - compliance_articles: [ - { - compliance_article_id: 969, - article_act_code: "HSRCM", - section: "9", - sub_section: "2", - paragraph: "1", - sub_paragraph: null, - description: "Notice Requirements", - long_description: "Notice Requirements", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "ba6f37df-5ced-4664-9a5e-5a5e93a09748", - report_name: "Management Plan for Riparian Area", - description: "", - due_date_period_months: null, - mine_report_due_date_type: "PMT", - default_due_date: null, - categories: [{ mine_report_category: "GSE", description: "Geoscience and Environmental" }], - compliance_articles: [ - { - compliance_article_id: 978, - article_act_code: "HSRCM", - section: "9", - sub_section: "5", - paragraph: "1", - sub_paragraph: null, - description: "Riparian Setback Distances", - long_description: "Riparian Setback Distances", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - { - mine_report_definition_guid: "c387b2a2-7bf4-4a29-9e9a-faa38a838b2d", - report_name: "Terrain Stability Remediation Plan", - description: "", - due_date_period_months: null, - mine_report_due_date_type: "EVT", - default_due_date: null, - categories: [ - { mine_report_category: "GSE", description: "Geoscience and Environmental" }, - { mine_report_category: "H&S", description: "Health and Safety" }, - ], - compliance_articles: [ - { - compliance_article_id: 980, - article_act_code: "HSRCM", - section: "9", - sub_section: "7", - paragraph: "1", - sub_paragraph: null, - description: "Terrain", - long_description: "Terrain", - effective_date: "1970-01-01", - expiry_date: "9999-12-31", - }, - ], - }, - ], mineReportStatusOptions: [ { mine_report_submission_status_code: "NRQ", description: "Not Requested" }, { mine_report_submission_status_code: "REQ", description: "Changes Requested" }, @@ -5995,9 +6153,9 @@ export const MINE_REPORT_SUBMISSIONS = [ comments: [], report_type: "CRR", mine_report_definition_guid: - BULK_STATIC_CONTENT_RESPONSE.mineReportDefinitionOptions[0].mine_report_definition_guid, + MINE_REPORT_DEFINITION_OPTIONS[0].mine_report_definition_guid, mine_report_category: ["H&S"], - report_name: BULK_STATIC_CONTENT_RESPONSE.mineReportDefinitionOptions[0].report_name, + report_name: MINE_REPORT_DEFINITION_OPTIONS[0].report_name, due_date: "2024-02-07", received_date: "2024-02-06", submission_year: 2024, diff --git a/services/core-api/app/api/compliance/response_models.py b/services/core-api/app/api/compliance/response_models.py index b57b623c0a..706a061b39 100644 --- a/services/core-api/app/api/compliance/response_models.py +++ b/services/core-api/app/api/compliance/response_models.py @@ -14,8 +14,7 @@ 'is_prr_only': fields.Boolean, }) - -COMPLIANCE_ARTICLE_MODEL = api.model( +COMPLIANCE_ARTICLE_BASE_MODEL = api.model( 'ComplianceArticle', { 'compliance_article_id': fields.Integer, 'article_act_code': fields.String, @@ -29,6 +28,10 @@ 'expiry_date': fields.Date, 'help_reference_link': fields.String, 'cim_or_cpo': fields.String, + }) + +COMPLIANCE_ARTICLE_MODEL = api.inherit( + 'ComplianceArticle', COMPLIANCE_ARTICLE_BASE_MODEL, { 'reports': fields.List(fields.Nested(MINE_REPORT_DEFINITION_BASE_MODEL)) }) diff --git a/services/core-api/app/api/mines/namespace.py b/services/core-api/app/api/mines/namespace.py index 9bb41094a3..9d09eaaa91 100644 --- a/services/core-api/app/api/mines/namespace.py +++ b/services/core-api/app/api/mines/namespace.py @@ -130,7 +130,7 @@ MineReportCommentListResource, MineReportCommentResource, ) -from app.api.mines.reports.resources.mine_report_definition import ( +from app.api.mines.reports.resources.mine_report_definition_resource import ( MineReportDefinitionListResource, ) from app.api.mines.reports.resources.mine_report_definition_compliance_article_xref_resource import ( diff --git a/services/core-api/app/api/mines/reports/models/mine_report_definition.py b/services/core-api/app/api/mines/reports/models/mine_report_definition.py index a134b842e0..d994430b5b 100644 --- a/services/core-api/app/api/mines/reports/models/mine_report_definition.py +++ b/services/core-api/app/api/mines/reports/models/mine_report_definition.py @@ -4,9 +4,13 @@ from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.schema import FetchedValue from sqlalchemy.ext.hybrid import hybrid_property +from sqlalchemy import or_, cast, Integer from sqlalchemy_filters import apply_pagination +from app.api.mines.reports.models.mine_report_permit_requirement import CimOrCpo from app.api.utils.models_mixins import Base, AuditMixin +from app.api.mines.reports.models.mine_report_definition_compliance_article_xref import MineReportDefinitionComplianceArticleXref +from app.api.compliance.models.compliance_article import ComplianceArticle from app.extensions import db @@ -69,6 +73,86 @@ def find_by_mine_report_definition_guid(cls, _id): except ValueError: return None + @classmethod + def _apply_sort(cls, query, sort_field, sort_dir): + if sort_field and sort_dir: + field = { + 'report_name': MineReportDefinition.report_name, + 'section': [cast(ComplianceArticle.section, Integer), cast(ComplianceArticle.sub_section, Integer)], + 'regulatory_authority': ComplianceArticle.cim_or_cpo + } + + if sort_field in ['section', 'regulatory_authority']: + if sort_field == 'section': + sort_func = [cast(ComplianceArticle.section, Integer), cast(ComplianceArticle.sub_section, Integer)] + if sort_dir == 'desc': + sort_func = [cast(ComplianceArticle.section, Integer).desc(), cast(ComplianceArticle.sub_section, Integer).desc()] + return query.order_by(*sort_func) + + sort_func = field[sort_field].asc() if sort_dir == 'asc' else field[sort_field].desc() + query = query.order_by(sort_func) + + + return query + + @classmethod + def _apply_filters(cls, query, regulatory_authority, is_prr_only, section): + filters = [] + if regulatory_authority: + reg_auth_filter = [] + if "NONE" in regulatory_authority: + regulatory_authority.remove("NONE") + reg_auth_filter.append(ComplianceArticle.cim_or_cpo.is_(None)) + if len(regulatory_authority) > 0: + reg_auth_filter.append(ComplianceArticle.cim_or_cpo.in_(regulatory_authority)) + + # if the query is either reg_auth = None or reg_auth in (list) + if len(reg_auth_filter) == 1: + reg_auth_filter = reg_auth_filter[0] + + # ex: query is reg auth in [None, CIM] + else: + reg_auth_filter = or_(*reg_auth_filter) + filters.append(reg_auth_filter) + if is_prr_only is not None: + filters.append(MineReportDefinition.is_prr_only.is_(is_prr_only)) + return query.filter(*filters) + + @classmethod + def _apply_pagination(cls, query, page, per_page): + if per_page != 0: + records, pagination_details = apply_pagination(query, page, per_page) + return { + 'records': records.all(), + 'current_page': pagination_details.page_number, + 'total_pages': pagination_details.num_pages, + 'items_per_page': pagination_details.page_size, + 'total': pagination_details.total_results, + } + return { + 'records': query.all(), + 'current_page': 1, + 'total_pages': 1, + 'items_per_page': query.count(), + 'total': query.count(), + } + + @classmethod + def apply_filters_and_pagination(cls, query, page, per_page, sort_field, sort_dir, regulatory_authority, is_prr_only, section): + + regulatory_authority = None if len(regulatory_authority) == 0 or len(regulatory_authority) == len(CimOrCpo) + 1 else regulatory_authority + + compliance_sort = True if sort_field in ['section', 'regulatory_authority'] else False + compliance_filter = True if regulatory_authority or section else False + + if compliance_sort or compliance_filter: + query = query.join(MineReportDefinitionComplianceArticleXref, MineReportDefinitionComplianceArticleXref.mine_report_definition_id == MineReportDefinition.mine_report_definition_id) + query = query.join(ComplianceArticle, ComplianceArticle.compliance_article_id == MineReportDefinitionComplianceArticleXref.compliance_article_id) + + query = cls._apply_sort(query, sort_field, sort_dir) + query = cls._apply_filters(query, regulatory_authority, is_prr_only, section) + return cls._apply_pagination(query, page, per_page) + @classmethod def get_all(cls): try: @@ -76,18 +160,6 @@ def get_all(cls): except ValueError: return None - @classmethod - def get_paginated(cls, page, per_page): - query = cls.query.filter_by(active_ind=True) - records, pagination_details = apply_pagination(query, page, per_page) - return { - 'records': records.all(), - 'current_page': pagination_details.page_number, - 'total_pages': pagination_details.num_pages, - 'items_per_page': pagination_details.page_size, - 'total': pagination_details.total_results, - } - @classmethod def find_required_reports_by_category(cls, _mine_report_category): try: diff --git a/services/core-api/app/api/mines/reports/resources/mine_report_definition.py b/services/core-api/app/api/mines/reports/resources/mine_report_definition.py deleted file mode 100644 index 020b0f25c2..0000000000 --- a/services/core-api/app/api/mines/reports/resources/mine_report_definition.py +++ /dev/null @@ -1,42 +0,0 @@ -import uuid -from flask_restx import Resource, reqparse, fields, inputs -from flask import request, current_app -from datetime import datetime -from werkzeug.exceptions import BadRequest, NotFound, InternalServerError - -from app.extensions import api, db -from app.api.utils.resources_mixins import UserMixin -from app.api.utils.access_decorators import requires_any_of, VIEW_ALL, MINESPACE_PROPONENT - -from app.api.mines.mine.models.mine import Mine -from app.api.mines.reports.models.mine_report_definition import MineReportDefinition -from app.api.mines.reports.models.mine_report_category_xref import MineReportCategoryXref -from app.api.mines.reports.models.mine_report_category import MineReportCategory -from app.api.mines.reports.models.mine_report_due_date_type import MineReportDueDateType -from app.api.mines.reports.models.mine_report_definition_compliance_article_xref import MineReportDefinitionComplianceArticleXref -from app.api.utils.custom_reqparser import CustomReqparser -from app.api.mines.response_models import PAGINATED_MINE_REPORT_DEFINITION_MODEL - -PAGE_DEFAULT = 1 -PER_PAGE_DEFAULT = 50 - -class MineReportDefinitionListResource(Resource, UserMixin): - parser = reqparse.RequestParser() - parser.add_argument( - 'page', type=int, help='page for pagination', location='args', store_missing=False - ) - parser.add_argument( - 'per_page', type=int, help='records per page', location='args', store_missing=False - ) - @api.doc( - params={ - 'page': f'The page number of paginated records to return. Default: {PAGE_DEFAULT}', - 'per_page': f'The number of records to return per page. Default: {PER_PAGE_DEFAULT}', - }, - description='returns the report definitions for possible reports.') - @api.marshal_with(PAGINATED_MINE_REPORT_DEFINITION_MODEL, code=200) - @requires_any_of([VIEW_ALL, MINESPACE_PROPONENT]) - def get(self): - page = request.args.get('page', PAGE_DEFAULT, type=int) - per_page = request.args.get('per_page', PER_PAGE_DEFAULT, type=int) - return MineReportDefinition.get_paginated(page, per_page) diff --git a/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py b/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py new file mode 100644 index 0000000000..959566e386 --- /dev/null +++ b/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py @@ -0,0 +1,63 @@ +import uuid +from flask_restx import Resource, reqparse +from flask import request +from werkzeug.exceptions import BadRequest, NotFound + +from app.extensions import api +from app.api.utils.resources_mixins import UserMixin +from app.api.utils.access_decorators import requires_any_of, VIEW_ALL, MINESPACE_PROPONENT + +from app.api.mines.reports.models.mine_report_definition import MineReportDefinition +from app.api.mines.response_models import PAGINATED_MINE_REPORT_DEFINITION_MODEL + +class MineReportDefinitionListResource(Resource, UserMixin): + parser = reqparse.RequestParser() + parser.add_argument( + 'page', type=int, help='page for pagination', location='args', store_missing=False + ) + parser.add_argument( + 'per_page', type=int, help='records per page- 0 to retrieve all records', location='args', store_missing=False + ) + parser.add_argument( + 'sort_field', type=str, help='field to sort by', location='args', store_missing=False + ) + parser.add_argument( + 'sort_dir', type=str, help='direction to sort by: asc or desc', location='args', store_missing=False + ) + parser.add_argument( + 'regulatory_authority', type=list, help='CIM, CPO, Both, NONE', location='args', store_missing=False + ) + parser.add_argument( + 'is_prr_only', type=bool, help='True for only PRR, False for only CRR', location='args', store_missing=False + ) + parser.add_argument( + 'section', type=str, help='article # of compliance report', location='args', store_missing=False + ) + @api.doc( + params={ + 'page': 'The page number of paginated records to return. Default', + 'per_page': 'The number of records to return per page', + }, + description='returns the report definitions for possible reports.') + @api.marshal_with(PAGINATED_MINE_REPORT_DEFINITION_MODEL, code=200) + @requires_any_of([VIEW_ALL, MINESPACE_PROPONENT]) + def get(self): + page = request.args.get('page', None, type=int) + per_page = request.args.get('per_page', 0, type=int) + sort_field = request.args.get('sort_field', None, type=str) + sort_dir = request.args.get('sort_dir', None, type=str) + regulatory_authority = request.args.getlist('regulatory_authority', type=str) + is_prr_only = request.args.get('is_prr_only', type=bool) + section = request.args.get('section', None, type=str) + + base_query = MineReportDefinition.query.filter_by(active_ind=True) + return MineReportDefinition.apply_filters_and_pagination( + base_query, + page, + per_page, + sort_field, + sort_dir, + regulatory_authority, + is_prr_only, + section + ) diff --git a/services/core-api/app/api/mines/response_models.py b/services/core-api/app/api/mines/response_models.py index 5cae49bbb0..455089a416 100644 --- a/services/core-api/app/api/mines/response_models.py +++ b/services/core-api/app/api/mines/response_models.py @@ -1,4 +1,4 @@ -from app.api.compliance.response_models import COMPLIANCE_ARTICLE_MODEL +from app.api.compliance.response_models import COMPLIANCE_ARTICLE_BASE_MODEL from app.api.dams.dto import DAM_MODEL from app.api.mines.reports.models.mine_report_permit_requirement import ( CimOrCpo, @@ -10,7 +10,6 @@ ) from app.api.parties.response_models import PARTY from app.api.users.response_models import USER_MODEL -from app.api.utils.feature_flag import Feature, is_feature_enabled from app.extensions import api from flask_restx import fields, marshal @@ -806,7 +805,7 @@ def format(self, value): 'active_ind': fields.Boolean }) -MINE_REPORT_DEFINITION_BASE_MODEL = api.model( +MINE_REPORT_DEFINITION_MODEL = api.model( 'MineReportDefinitionBase', { 'mine_report_definition_guid': fields.String, 'report_name': fields.String, @@ -818,6 +817,7 @@ def format(self, value): 'categories': fields.List(fields.Nested(MINE_REPORT_DEFINITION_CATEGORIES)), 'is_common': fields.Boolean, 'is_prr_only': fields.Boolean, + 'compliance_articles': fields.List(fields.Nested(COMPLIANCE_ARTICLE_BASE_MODEL)) }) PAGINATED_LIST = api.model( @@ -829,7 +829,7 @@ def format(self, value): }) PAGINATED_MINE_REPORT_DEFINITION_MODEL = api.inherit('MineReportDefinition', PAGINATED_LIST, { - 'records': fields.List(fields.Nested(MINE_REPORT_DEFINITION_BASE_MODEL)), + 'records': fields.List(fields.Nested(MINE_REPORT_DEFINITION_MODEL)), }) PAGINATED_REPORT_LIST = api.inherit('ReportList', PAGINATED_LIST, { diff --git a/services/core-web/src/components/Forms/reports/ReportFilterForm.tsx b/services/core-web/src/components/Forms/reports/ReportFilterForm.tsx index 09c7faf68c..ee11d83db1 100644 --- a/services/core-web/src/components/Forms/reports/ReportFilterForm.tsx +++ b/services/core-web/src/components/Forms/reports/ReportFilterForm.tsx @@ -6,8 +6,8 @@ import { getDropdownMineReportCategoryOptions, getDropdownMineReportStatusOptions, getDropdownPermitConditionCategoryOptions, - getMineReportDefinitionOptions, } from "@mds/common/redux/selectors/staticContentSelectors"; +import { getMineReportDefinitionOptions } from "@mds/common/redux/slices/complianceReportsSlice"; import { createDropDownList, sortListObjectsByPropertyLocaleCompare } from "@common/utils/helpers"; import * as FORM from "@/constants/forms"; import { renderConfig } from "@/components/common/config"; diff --git a/services/core-web/src/components/Forms/reports/ReportSearchForm.js b/services/core-web/src/components/Forms/reports/ReportSearchForm.js index b30cd398c5..d07a3faebc 100644 --- a/services/core-web/src/components/Forms/reports/ReportSearchForm.js +++ b/services/core-web/src/components/Forms/reports/ReportSearchForm.js @@ -9,9 +9,9 @@ import { Button, Col, Row } from "antd"; import { getDropdownMineReportStatusOptions, getDropdownMineReportCategoryOptions, - getDropdownMineReportDefinitionOptions, getMineRegionDropdownOptions, } from "@mds/common/redux/selectors/staticContentSelectors"; +import { getDropdownMineReportDefinitionOptions } from "@mds/common/redux/slices/complianceReportsSlice"; import { sortListObjectsByPropertyLocaleCompare } from "@common/utils/helpers"; import * as FORM from "@/constants/forms"; import { renderConfig } from "@/components/common/config"; diff --git a/services/core-web/src/components/Forms/reports/RequestReportForm.tsx b/services/core-web/src/components/Forms/reports/RequestReportForm.tsx index 891b4bf3bf..4cbafe1925 100644 --- a/services/core-web/src/components/Forms/reports/RequestReportForm.tsx +++ b/services/core-web/src/components/Forms/reports/RequestReportForm.tsx @@ -7,10 +7,7 @@ import FormWrapper from "@mds/common/components/forms/FormWrapper"; import RenderSelect from "@mds/common/components/forms/RenderSelect"; import RenderDate from "@mds/common/components/forms/RenderDate"; import RenderCancelButton from "@mds/common/components/forms/RenderCancelButton"; -import { - getFormattedMineReportDefinitionOptions, - getMineReportDefinitionByGuid, -} from "@mds/common/redux/selectors/staticContentSelectors"; +import { getFormattedMineReportDefinitionOptions, getMineReportDefinitionByGuid } from "@mds/common/redux/slices/complianceReportsSlice"; import { RenderPRRFields, ReportInfoBox } from "@mds/common/components/reports/ReportGetStarted"; import { IMineReport, IMineReportDefinition } from "@mds/common/interfaces"; import { MINE_REPORT_SUBMISSION_CODES, REPORT_TYPE_CODES } from "@mds/common/constants/enums"; diff --git a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx index af1ea30865..f61c310fe6 100644 --- a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx +++ b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx @@ -2,28 +2,55 @@ import React, { FC, useEffect, useState } from "react"; import { Button, Row, Typography } from "antd"; import PlusOutlined from "@ant-design/icons/PlusOutlined"; import CoreTable from "@mds/common/components/common/CoreTable"; -import { useDispatch, useSelector } from "react-redux"; +import { useAppDispatch as useDispatch, useAppSelector as useSelector } from "@mds/common/redux/rootState"; import { fetchComplianceReports, getComplianceReportPageData } from "@mds/common/redux/slices/complianceReportsSlice"; import { renderActionsColumn, renderTextColumn } from "@mds/common/components/common/CoreTableCommonColumns"; -import { IMineReportDefinition } from "@mds/common/interfaces"; +import { IComplianceArticle, IMineReportDefinition } from "@mds/common/interfaces"; +import { REPORT_REGULATORY_AUTHORITY_CODES, REPORT_REGULATORY_AUTHORITY_ENUM } from "@mds/common/constants/enums"; +import { formatComplianceCodeArticleNumber } from "@mds/common/redux/utils/helpers"; +import { EMPTY_FIELD, REPORT_REGULATORY_AUTHORITY_CODES_HASH } from "@mds/common/constants/strings"; +import { removeNullValues } from "@mds/common/constants/utils"; +const defaultParams = { + page: 1, + per_page: 50 +}; + const ComplianceReportManagement: FC = () => { const dispatch = useDispatch(); const reportPageData = useSelector(getComplianceReportPageData); const reportDefinitions = reportPageData?.records ?? []; const defaultLoaded = reportPageData.total > 0; - console.log(reportPageData); - const [isLoading, setIsLoading] = useState(defaultLoaded); + const [isLoading, setIsLoading] = useState(false); - - const fetchData = (page = 1, per_page = 50) => { + const fetchData = (params = defaultParams) => { setIsLoading(true); - dispatch(fetchComplianceReports({ page, per_page })).then(setIsLoading(false)) - } + dispatch(fetchComplianceReports(params)).then(() => setIsLoading(false)) + }; + + const onTableChange = (pagination, filters, sorter) => { + const { current: page, pageSize: per_page } = pagination; + const { order, field: sort_field } = sorter; + const activeFilters = removeNullValues(filters); + + const sortParams = order ? { + sort_dir: order.replace("end", ""), + sort_field + } : {}; + + const newParams = { + page, + per_page, + sort_field, + ...sortParams, + ...activeFilters, + } + fetchData(newParams); + }; useEffect(() => { - if (!isLoading && !defaultLoaded) { + if (!defaultLoaded) { fetchData(); } }, []); @@ -42,24 +69,47 @@ const ComplianceReportManagement: FC = () => { clickFunction: (_, record) => openViewModal(record) }]; - // TODO: the sorting for report name only sorts the data that is currently on the page - // and also I don't have data for the columns that are currently commented out + const regulatoryAuthorityFilter = Object.entries(REPORT_REGULATORY_AUTHORITY_CODES_HASH).map(([value, text]) => ({ value, text })); const columns = [ - renderTextColumn("report_name", "Report Name", true), // sort - // renderTextColumn("section", "Section"), // sort, filter - renderTextColumn("report_type", "Report Type"), // filter - // renderTextColumn("regulatory_authority", "Regulatory Authority"), // sort, filter - // renderTextColumn("office", "Office"), // sort, filter + renderTextColumn("report_name", "Report Name", true), + { ...renderTextColumn("section", "Section"), sorter: true }, // filter + { + ...renderTextColumn("is_prr_only", "Report Type"), + filters: [ + { value: false, text: "Code Required Report" }, + { value: true, text: "Permit Required Report" } + ] + }, + { + ...renderTextColumn("regulatory_authority", "Regulatory Authority"), + filters: regulatoryAuthorityFilter, + sorter: true + }, renderActionsColumn({ actions }) ]; + const formatCode = (article: IComplianceArticle) => { + if (!article) { + return { + regulatory_authority: EMPTY_FIELD, + section: EMPTY_FIELD + } + } + return { + regulatory_authority: REPORT_REGULATORY_AUTHORITY_ENUM[article.cim_or_cpo] ?? REPORT_REGULATORY_AUTHORITY_CODES.NONE, + section: formatComplianceCodeArticleNumber(article) + } + } + const transformData = (reports: IMineReportDefinition[]) => { return reports.map((r) => { - const report_type = r.is_prr_only ? "Permit Required Report" : "Core Required Report"; + const formattedComplianceArticle = formatCode(r.compliance_articles[0]); + const is_prr_only = r.is_prr_only ? "Permit Required Report" : "Code Required Report"; return { ...r, - report_type + is_prr_only, + ...formattedComplianceArticle }; }) } @@ -83,15 +133,14 @@ const ComplianceReportManagement: FC = () => { dataSource={transformData(reportDefinitions)} columns={columns} rowKey="mine_report_definition_guid" + onChange={onTableChange} pagination={!isLoading && { total: reportPageData.total, defaultPageSize: 50, position: ['bottomCenter'], disabled: isLoading, - onChange: fetchData }} /> -
); }; diff --git a/services/core-web/src/components/admin/complianceCodes/ReportEditForm.tsx b/services/core-web/src/components/admin/complianceCodes/ReportEditForm.tsx index bbf4a35112..d339ad7990 100644 --- a/services/core-web/src/components/admin/complianceCodes/ReportEditForm.tsx +++ b/services/core-web/src/components/admin/complianceCodes/ReportEditForm.tsx @@ -6,11 +6,9 @@ import { ReportDefinitionFieldSelect } from "@mds/common/components/reports/Repo import { ReportInfoBox } from "@mds/common/components/reports/ReportGetStarted"; import * as FORM from "@/constants/forms"; import { TRASHCAN } from "@/constants/assets"; -import { getMineReportDefinitionOptions } from "@mds/common/redux/selectors/staticContentSelectors"; -import { IComplianceArticle } from "@mds/common/interfaces"; +import { getMineReportDefinitionOptions } from "@mds/common/redux/slices/complianceReportsSlice"; export interface ReportEditProps { - complianceCodes: IComplianceArticle[]; isEditMode: boolean; } @@ -74,14 +72,12 @@ export const ReportEditForm = (props: ReportEditProps) => { > - <> - - + {reportData?.mine_report_definition_guid ? ( diff --git a/services/core-web/src/components/mine/Reports/MineReportInfo.tsx b/services/core-web/src/components/mine/Reports/MineReportInfo.tsx index a49e43a2c6..84c306e705 100644 --- a/services/core-web/src/components/mine/Reports/MineReportInfo.tsx +++ b/services/core-web/src/components/mine/Reports/MineReportInfo.tsx @@ -12,7 +12,7 @@ import { } from "@mds/common/redux/actionCreators/reportActionCreator"; import { closeModal, openModal } from "@mds/common/redux/actions/modalActions"; import { getMineReports, getReportsPageData } from "@mds/common/redux/selectors/reportSelectors"; -import { getMineReportDefinitionOptions } from "@mds/common/redux/selectors/staticContentSelectors"; +import { getMineReportDefinitionOptions } from "@mds/common/redux/slices/complianceReportsSlice"; import * as Strings from "@mds/common/constants/strings"; import * as Permission from "@/constants/permissions"; import AuthorizationWrapper from "@/components/common/wrappers/AuthorizationWrapper"; diff --git a/services/core-web/src/components/mine/Reports/MineReportTable.js b/services/core-web/src/components/mine/Reports/MineReportTable.js index 1d062a3f1f..68de08ae84 100644 --- a/services/core-web/src/components/mine/Reports/MineReportTable.js +++ b/services/core-web/src/components/mine/Reports/MineReportTable.js @@ -11,8 +11,8 @@ import { import { getMineReportCategoryOptionsHash, getMineReportStatusOptionsHash, - getMineReportDefinitionHash, } from "@mds/common/redux/selectors/staticContentSelectors"; +import { getMineReportDefinitionHash } from "@mds/common/redux/slices/complianceReportsSlice"; import { Link, useHistory } from "react-router-dom"; import { Badge, notification } from "antd"; import CustomPropTypes from "@/customPropTypes"; diff --git a/services/core-web/src/components/mine/Tailings/MineTailingsInfoTabs.tsx b/services/core-web/src/components/mine/Tailings/MineTailingsInfoTabs.tsx index 2277df8ff5..a2809b9280 100644 --- a/services/core-web/src/components/mine/Tailings/MineTailingsInfoTabs.tsx +++ b/services/core-web/src/components/mine/Tailings/MineTailingsInfoTabs.tsx @@ -20,7 +20,7 @@ import { import { getMineReports, getReportsPageData } from "@mds/common/redux/selectors/reportSelectors"; import { getMineGuid, getMines } from "@mds/common/redux/selectors/mineSelectors"; import { closeModal, openModal } from "@mds/common/redux/actions/modalActions"; -import { getMineReportDefinitionOptions } from "@mds/common/redux/reducers/staticContentReducer"; +import { getMineReportDefinitionOptions } from "@mds/common/redux/slices/complianceReportsSlice"; import * as Strings from "@mds/common/constants/strings"; import DamsPage from "@common/components/tailings/dam/DamsPage"; import MineReportTable from "@/components/mine/Reports/MineReportTable"; diff --git a/services/core-web/src/tests/components/Forms/reports/ReportPage-prr.spec.tsx b/services/core-web/src/tests/components/Forms/reports/ReportPage-prr.spec.tsx index d4e2b48a03..e2728e1954 100644 --- a/services/core-web/src/tests/components/Forms/reports/ReportPage-prr.spec.tsx +++ b/services/core-web/src/tests/components/Forms/reports/ReportPage-prr.spec.tsx @@ -7,6 +7,7 @@ import ReportPage from "@/components/mine/Reports/ReportPage"; import { BrowserRouter } from "react-router-dom"; import { USER_ROLES } from "@mds/common/constants/environment"; import { SystemFlagEnum } from "@mds/common/constants/enums"; +import { complianceReportReducerType } from "@mds/common/redux/slices/complianceReportsSlice"; const mineReportSubmission = MOCK.MINE_REPORT_SUBMISSIONS[1]; const initialState = { @@ -17,10 +18,18 @@ const initialState = { [MINES]: MOCK.MINES, [STATIC_CONTENT]: { mineReportStatusOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.mineReportStatusOptions, - mineReportDefinitionOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.mineReportDefinitionOptions, permitConditionCategoryOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.permitConditionCategoryOptions, }, + [complianceReportReducerType]: { + reportPageData: { + records: MOCK.MINE_REPORT_DEFINITION_OPTIONS, + current_page: 1, + items_per_page: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, + total: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, + total_pages: 1 + } + }, [AUTHENTICATION]: { systemFlag: SystemFlagEnum.core, userAccessData: [USER_ROLES.role_edit_reports], diff --git a/services/core-web/src/tests/components/Forms/reports/ReportPage.spec.tsx b/services/core-web/src/tests/components/Forms/reports/ReportPage.spec.tsx index 625c6bdb6e..6afe29814f 100644 --- a/services/core-web/src/tests/components/Forms/reports/ReportPage.spec.tsx +++ b/services/core-web/src/tests/components/Forms/reports/ReportPage.spec.tsx @@ -7,6 +7,7 @@ import { USER_ROLES } from "@mds/common/constants/environment"; import { SystemFlagEnum } from "@mds/common/constants/enums"; import ReportPage from "@/components/mine/Reports/ReportPage"; import { BrowserRouter } from "react-router-dom"; +import { complianceReportReducerType } from "@mds/common/redux/slices/complianceReportsSlice"; const mineReportSubmission = MOCK.MINE_REPORT_SUBMISSIONS[0]; const initialState = { @@ -17,10 +18,18 @@ const initialState = { [MINES]: MOCK.MINES, [STATIC_CONTENT]: { mineReportStatusOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.mineReportStatusOptions, - mineReportDefinitionOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.mineReportDefinitionOptions, permitConditionCategoryOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.permitConditionCategoryOptions, }, + [complianceReportReducerType]: { + reportPageData: { + records: MOCK.MINE_REPORT_DEFINITION_OPTIONS, + current_page: 1, + items_per_page: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, + total: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, + total_pages: 1 + } + }, [AUTHENTICATION]: { systemFlag: SystemFlagEnum.core, userAccessData: [USER_ROLES.role_edit_reports], diff --git a/services/core-web/src/tests/components/Forms/reports/ReportPermitRequirementForm.spec.tsx b/services/core-web/src/tests/components/Forms/reports/ReportPermitRequirementForm.spec.tsx index c967b929fa..6f2cb87fd5 100644 --- a/services/core-web/src/tests/components/Forms/reports/ReportPermitRequirementForm.spec.tsx +++ b/services/core-web/src/tests/components/Forms/reports/ReportPermitRequirementForm.spec.tsx @@ -6,14 +6,23 @@ import * as MOCK from "@mds/common/tests/mocks/dataMocks"; import { USER_ROLES } from "@mds/common/constants/environment"; import { SystemFlagEnum } from "@mds/common/constants/enums"; import { ReportPermitRequirementForm } from "@/components/Forms/reports/ReportPermitRequirementForm"; +import { complianceReportReducerType } from "@mds/common/redux/slices/complianceReportsSlice"; const initialState = { [STATIC_CONTENT]: { mineReportStatusOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.mineReportStatusOptions, - mineReportDefinitionOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.mineReportDefinitionOptions, permitConditionCategoryOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.permitConditionCategoryOptions, }, + [complianceReportReducerType]: { + reportPageData: { + records: MOCK.MINE_REPORT_DEFINITION_OPTIONS, + current_page: 1, + items_per_page: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, + total: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, + total_pages: 1 + } + }, [MINES]: MOCK.MINES, [PERMITS]: { permits: MOCK.PERMITS, diff --git a/services/core-web/src/tests/components/Forms/reports/RequestReportForm.spec.tsx b/services/core-web/src/tests/components/Forms/reports/RequestReportForm.spec.tsx index 3278eace65..b814fc19fd 100644 --- a/services/core-web/src/tests/components/Forms/reports/RequestReportForm.spec.tsx +++ b/services/core-web/src/tests/components/Forms/reports/RequestReportForm.spec.tsx @@ -6,14 +6,23 @@ import * as MOCK from "@mds/common/tests/mocks/dataMocks"; import { RequestReportForm } from "@/components/Forms/reports/RequestReportForm"; import { USER_ROLES } from "@mds/common/constants/environment"; import { REPORT_TYPE_CODES, SystemFlagEnum } from "@mds/common/constants/enums"; +import { complianceReportReducerType } from "@mds/common/redux/slices/complianceReportsSlice"; const initialState = { [STATIC_CONTENT]: { mineReportStatusOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.mineReportStatusOptions, - mineReportDefinitionOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.mineReportDefinitionOptions, permitConditionCategoryOptions: MOCK.BULK_STATIC_CONTENT_RESPONSE.permitConditionCategoryOptions, }, + [complianceReportReducerType]: { + reportPageData: { + records: MOCK.MINE_REPORT_DEFINITION_OPTIONS, + current_page: 1, + items_per_page: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, + total: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, + total_pages: 1 + } + }, [MINES]: MOCK.MINES, [AUTHENTICATION]: { systemFlag: SystemFlagEnum.core, diff --git a/services/core-web/src/tests/components/Forms/reports/__snapshots__/ReportPage.spec.tsx.snap b/services/core-web/src/tests/components/Forms/reports/__snapshots__/ReportPage.spec.tsx.snap index b82ff60807..da5e63c188 100644 --- a/services/core-web/src/tests/components/Forms/reports/__snapshots__/ReportPage.spec.tsx.snap +++ b/services/core-web/src/tests/components/Forms/reports/__snapshots__/ReportPage.spec.tsx.snap @@ -388,10 +388,10 @@ exports[`ReportPage renders view mode properly 1`] = ` From 8247171ba0080c5a20fb44f7d4de52ee968fccee Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Fri, 31 Jan 2025 18:12:23 +0000 Subject: [PATCH 08/22] make fetch happen in other places that use report definitions --- .../reports/ReportDefinitionFieldSelect.tsx | 17 +- .../reports/ReportDetailsForm-view.spec.tsx | 5 +- .../components/reports/ReportDetailsForm.tsx | 16 +- .../components/reports/ReportGetStarted.tsx | 13 +- .../redux/slices/complianceReportsSlice.ts | 5 + .../Forms/reports/ReportFilterForm.tsx | 2 +- .../Forms/reports/ReportSearchForm.js | 262 ----------- .../Forms/reports/ReportSearchForm.tsx | 249 ++++++++++ .../Forms/reports/ReportPage-prr.spec.tsx | 5 +- .../Forms/reports/ReportPage.spec.tsx | 5 +- .../ReportPermitRequirementForm.spec.tsx | 8 +- .../Forms/reports/RequestReportForm.spec.tsx | 5 +- .../ReportPermitRequirementForm.spec.tsx.snap | 437 +++++++++++++++--- .../dashboard/mine/reports/Reports.spec.tsx | 6 +- 14 files changed, 672 insertions(+), 363 deletions(-) delete mode 100644 services/core-web/src/components/Forms/reports/ReportSearchForm.js create mode 100644 services/core-web/src/components/Forms/reports/ReportSearchForm.tsx diff --git a/services/common/src/components/reports/ReportDefinitionFieldSelect.tsx b/services/common/src/components/reports/ReportDefinitionFieldSelect.tsx index e495c161a9..a57027679f 100644 --- a/services/common/src/components/reports/ReportDefinitionFieldSelect.tsx +++ b/services/common/src/components/reports/ReportDefinitionFieldSelect.tsx @@ -1,8 +1,8 @@ import { formatComplianceCodeReportName } from "@mds/common/redux/utils/helpers"; import React, { useEffect, useState } from "react"; -import { useSelector } from "react-redux"; +import { useAppDispatch as useDispatch, useAppSelector as useSelector } from "@mds/common/redux/rootState"; import { Field } from "redux-form"; -import { getMineReportDefinitionOptions } from "@mds/common/redux/slices/complianceReportsSlice"; +import { fetchComplianceReports, getMineReportDefinitionOptions, getReportDefinitionsLoaded, reportParamsGetAll } from "@mds/common/redux/slices/complianceReportsSlice"; import RenderSelect from "../forms/RenderSelect"; import { uniqBy } from "lodash"; import moment from "moment"; @@ -18,9 +18,10 @@ export interface ReportDefinitionFieldSelectProps { } export const ReportDefinitionFieldSelect = (props: ReportDefinitionFieldSelectProps) => { + const dispatch = useDispatch(); const mineReportDefinitionOptions = useSelector(getMineReportDefinitionOptions); - - const [formattedMineReportDefinitionOptions, setFormatMineReportDefinitionOptions] = useState([]); + const [formattedMineReportDefinitionOptions, setFormattedMineReportDefinitionOptions] = useState([]); + const reportDefinitionsLoaded = useSelector(getReportDefinitionsLoaded(reportParamsGetAll)); useEffect(() => { // Format the mine report definition options for the search bar @@ -37,9 +38,15 @@ export const ReportDefinitionFieldSelect = (props: ReportDefinitionFieldSelectPr }; }) .sort((a, b) => a.label.localeCompare(b.label)); - setFormatMineReportDefinitionOptions(uniqBy(newFormattedMineReportDefinitionOptions, "value")); + setFormattedMineReportDefinitionOptions(uniqBy(newFormattedMineReportDefinitionOptions, "value")); }, [mineReportDefinitionOptions]); + useEffect(() => { + if (!reportDefinitionsLoaded) { + dispatch(fetchComplianceReports(reportParamsGetAll)); + } + }, []); + return ( = ({ } = formValues; const [selectedReportCode, setSelectedReportCode] = useState(""); - const [formattedMineReportDefinitionOptions, setFormatMineReportDefinitionOptions] = useState([]); + const [formattedMineReportDefinitionOptions, setFormattedMineReportDefinitionOptions] = useState([]); const [isLoading, setIsLoading] = useState(false); const partyRelationships: IPartyAppt[] = useSelector((state) => getPartyRelationships(state)); @@ -163,6 +163,8 @@ const ReportDetailsForm: FC = ({ const MinistryContactsByRegion: IMinistryContact[] = useSelector(getMinistryContactsByRegion); const [contactEmail, setContactEmail] = useState(); + const reportDefinitionsLoaded = useSelector(getReportDefinitionsLoaded(reportParamsGetAll)); + // PRR const permit = useSelector(getPermitByGuid(permit_guid)); const dropdownPermitConditionCategoryOptions = useSelector( @@ -243,7 +245,7 @@ const ReportDetailsForm: FC = ({ }; }) .sort((a, b) => a.label.localeCompare(b.label)); - setFormatMineReportDefinitionOptions(uniqBy(newFormattedMineReportDefinitionOptions, "value")); + setFormattedMineReportDefinitionOptions(uniqBy(newFormattedMineReportDefinitionOptions, "value")); }, [mineReportDefinitionOptions]); useEffect(() => { @@ -285,6 +287,12 @@ const ReportDetailsForm: FC = ({ } }, [formValues.mine_report_guid, formValues.mine_report_submission_guid]); + useEffect(() => { + if (!reportDefinitionsLoaded) { + dispatch(fetchComplianceReports(reportParamsGetAll)); + } + }, []); + const handleAddComment = async (values) => { const formVals = { report_comment: values.comment, diff --git a/services/common/src/components/reports/ReportGetStarted.tsx b/services/common/src/components/reports/ReportGetStarted.tsx index 9e16db7042..7051ae80fb 100644 --- a/services/common/src/components/reports/ReportGetStarted.tsx +++ b/services/common/src/components/reports/ReportGetStarted.tsx @@ -2,7 +2,7 @@ import { Alert, Button, Col, Row, Typography } from "antd"; import React, { FC, ReactNode, useEffect, useState } from "react"; import { Field, getFormValues, change } from "redux-form"; import ArrowRightOutlined from "@ant-design/icons/ArrowRightOutlined"; -import { useSelector, useDispatch } from "react-redux"; +import { useAppDispatch as useDispatch, useAppSelector as useSelector } from "@mds/common/redux/rootState"; import { IMine, IMineReportDefinition, IMineReportSubmission } from "@mds/common/interfaces"; import { createDropDownList, @@ -16,7 +16,7 @@ import RenderSelect from "../forms/RenderSelect"; import { getDropdownPermitConditionCategoryOptions, } from "@mds/common/redux/selectors/staticContentSelectors"; -import { getFormattedMineReportDefinitionOptions, getMineReportDefinitionByGuid } from "@mds/common/redux/slices/complianceReportsSlice"; +import { fetchComplianceReports, getFormattedMineReportDefinitionOptions, getMineReportDefinitionByGuid, getReportDefinitionsLoaded, reportParamsGetAll } from "@mds/common/redux/slices/complianceReportsSlice"; import { getPermits } from "@mds/common/redux/selectors/permitSelectors"; import { fetchPermits } from "@mds/common/redux/actionCreators/permitActionCreator"; import { getSystemFlag } from "@mds/common/redux/selectors/authenticationSelectors"; @@ -170,9 +170,16 @@ const ReportGetStarted: FC = ({ const selectedReportDefinition: IMineReportDefinition = useSelector( getMineReportDefinitionByGuid(formValues?.mine_report_definition_guid) ); + const reportDefinitionsLoaded = useSelector(getReportDefinitionsLoaded(reportParamsGetAll)); useEffect(() => { - if (selectedReportDefinition && selectedReportDefinition.is_prr_only) { + if (!reportDefinitionsLoaded) { + dispatch(fetchComplianceReports(reportParamsGetAll)); + } + }, []); + + useEffect(() => { + if (selectedReportDefinition?.is_prr_only) { setDisableNextButton(true); } else { setDisableNextButton(false); diff --git a/services/common/src/redux/slices/complianceReportsSlice.ts b/services/common/src/redux/slices/complianceReportsSlice.ts index 5151a53c60..5afcc02350 100644 --- a/services/common/src/redux/slices/complianceReportsSlice.ts +++ b/services/common/src/redux/slices/complianceReportsSlice.ts @@ -18,6 +18,11 @@ export interface ComplianceReportParams extends ISearchParams { active_ind?: boolean[]; }; +export const reportParamsGetAll: ComplianceReportParams = { + per_page: 0, + active_ind: [true, false] +}; + interface ComplianceReportState { reportPageData: IPageData, params: ComplianceReportParams; diff --git a/services/core-web/src/components/Forms/reports/ReportFilterForm.tsx b/services/core-web/src/components/Forms/reports/ReportFilterForm.tsx index ee11d83db1..99ff60c70e 100644 --- a/services/core-web/src/components/Forms/reports/ReportFilterForm.tsx +++ b/services/core-web/src/components/Forms/reports/ReportFilterForm.tsx @@ -49,7 +49,7 @@ export const ReportFilterForm: FC = ({ const { report_type: selectedMineReportCategory, report_name: selectedMineReportDefinitionGuid, - } = useSelector((state) => getFormValues(FORM.FILTER_REPORTS)(state) ?? {}); + } = useSelector(getFormValues(FORM.FILTER_REPORTS)) ?? {}; const updateMineReportDefinitionOptions = ( mineReportDefinitionOptions, diff --git a/services/core-web/src/components/Forms/reports/ReportSearchForm.js b/services/core-web/src/components/Forms/reports/ReportSearchForm.js deleted file mode 100644 index d07a3faebc..0000000000 --- a/services/core-web/src/components/Forms/reports/ReportSearchForm.js +++ /dev/null @@ -1,262 +0,0 @@ -import React, { Component } from "react"; -import PropTypes from "prop-types"; -import { connect } from "react-redux"; -import { compose } from "redux"; -import { UpOutlined, DownOutlined } from "@ant-design/icons"; -import { isEmpty, some, negate } from "lodash"; -import { Field } from "redux-form"; -import { Button, Col, Row } from "antd"; -import { - getDropdownMineReportStatusOptions, - getDropdownMineReportCategoryOptions, - getMineRegionDropdownOptions, -} from "@mds/common/redux/selectors/staticContentSelectors"; -import { getDropdownMineReportDefinitionOptions } from "@mds/common/redux/slices/complianceReportsSlice"; -import { sortListObjectsByPropertyLocaleCompare } from "@common/utils/helpers"; -import * as FORM from "@/constants/forms"; -import { renderConfig } from "@/components/common/config"; -import CustomPropTypes from "@/customPropTypes"; -import FormWrapper from "@mds/common/components/forms/FormWrapper"; -import RenderResetButton from "@mds/common/components/forms/RenderResetButton"; - -const propTypes = { - onSubmit: PropTypes.func.isRequired, - handleReset: PropTypes.func.isRequired, - initialValues: PropTypes.objectOf(PropTypes.any).isRequired, - mineRegionOptions: CustomPropTypes.options.isRequired, - dropdownMineReportStatusOptions: PropTypes.arrayOf(CustomPropTypes.dropdownListItem).isRequired, - dropdownMineReportDefinitionOptions: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.any)) - .isRequired, - dropdownMineReportCategoryOptions: PropTypes.arrayOf(CustomPropTypes.dropdownListItem).isRequired, -}; - -export class ReportSearchForm extends Component { - state = { - receivedFirstInitialValues: false, - expandAdvancedSearch: false, - }; - - handleReset = () => { - this.props.handleReset(); - }; - - toggleIsAdvancedSearch = () => - this.setState((prevState) => ({ - expandAdvancedSearch: !prevState.expandAdvancedSearch, - })); - - haveAdvancedSearchFilters = ({ - report_type, - report_name, - due_date_after, - due_date_before, - received_date_after, - received_date_before, - received_only, - compliance_year, - status, - requested_by, - major, - region, - }) => - due_date_after || - due_date_before || - received_date_after || - received_date_before || - received_only || - compliance_year || - requested_by || - major || - some([report_type, report_name, status, region], negate(isEmpty)); - - componentWillReceiveProps = (nextProps) => { - if ( - !this.state.receivedFirstInitialValues && - this.props.initialValues !== nextProps.initialValues - ) { - this.setState({ - receivedFirstInitialValues: true, - expandAdvancedSearch: this.haveAdvancedSearchFilters(nextProps.initialValues), - }); - } - }; - - render() { - return ( - - - - - - - {this.state.expandAdvancedSearch && ( -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- )} -
- -
-
- - -
-
- ); - } -} - -ReportSearchForm.propTypes = propTypes; - -export default compose( - connect((state) => ({ - mineRegionOptions: getMineRegionDropdownOptions(state), - dropdownMineReportStatusOptions: getDropdownMineReportStatusOptions(state, false), - dropdownMineReportCategoryOptions: getDropdownMineReportCategoryOptions(state, false), - dropdownMineReportDefinitionOptions: getDropdownMineReportDefinitionOptions(state, false), - })) -)(ReportSearchForm); diff --git a/services/core-web/src/components/Forms/reports/ReportSearchForm.tsx b/services/core-web/src/components/Forms/reports/ReportSearchForm.tsx new file mode 100644 index 0000000000..f7f1263e7c --- /dev/null +++ b/services/core-web/src/components/Forms/reports/ReportSearchForm.tsx @@ -0,0 +1,249 @@ +import React, { FC, useEffect, useState } from "react"; +import { UpOutlined, DownOutlined } from "@ant-design/icons"; +import { isEmpty, some, negate } from "lodash"; +import { Field } from "redux-form"; +import { Button, Col, Row } from "antd"; +import { + getDropdownMineReportStatusOptions, + getDropdownMineReportCategoryOptions, + getMineRegionDropdownOptions, +} from "@mds/common/redux/selectors/staticContentSelectors"; +import { fetchComplianceReports, getDropdownMineReportDefinitionOptions, getReportDefinitionsLoaded, reportParamsGetAll } from "@mds/common/redux/slices/complianceReportsSlice"; +import { sortListObjectsByPropertyLocaleCompare } from "@common/utils/helpers"; +import * as FORM from "@/constants/forms"; +import { renderConfig } from "@/components/common/config"; +import FormWrapper from "@mds/common/components/forms/FormWrapper"; +import RenderResetButton from "@mds/common/components/forms/RenderResetButton"; +import { useAppSelector as useSelector, useAppDispatch as useDispatch } from "@mds/common/redux/rootState"; + +interface ReportSearchFormProps { + onSubmit: (values) => void | Promise; + handleReset: () => void | Promise; + initialValues: any; +} + +export const ReportSearchForm: FC = (props) => { + const dispatch = useDispatch(); + const mineRegionOptions = useSelector(getMineRegionDropdownOptions); + const dropdownMineReportStatusOptions = useSelector((state) => getDropdownMineReportStatusOptions(state, false)); + const dropdownMineReportCategoryOptions = useSelector((state) => getDropdownMineReportCategoryOptions(state, false)); + const dropdownMineReportDefinitionOptions = useSelector((state) => getDropdownMineReportDefinitionOptions(state, false)); + const [receivedFirstInitialValues, setReceivedFirstInitialValues] = useState(false); + const [expandAdvancedSearch, setExpandAdvancedSearch] = useState(false); + + const reportsLoaded = useSelector(getReportDefinitionsLoaded(reportParamsGetAll)); + + useEffect(() => { + if (!reportsLoaded) { + dispatch(fetchComplianceReports(reportParamsGetAll)); + } + }, []); + + const handleReset = () => { + props.handleReset(); + }; + + const toggleIsAdvancedSearch = () => { + setExpandAdvancedSearch(!expandAdvancedSearch); + } + + const haveAdvancedSearchFilters = ({ + report_type, + report_name, + due_date_after, + due_date_before, + received_date_after, + received_date_before, + received_only, + compliance_year, + status, + requested_by, + major, + region, + }) => + due_date_after || + due_date_before || + received_date_after || + received_date_before || + received_only || + compliance_year || + requested_by || + major || + some([report_type, report_name, status, region], negate(isEmpty)); + + useEffect(() => { + if (!receivedFirstInitialValues) { + setReceivedFirstInitialValues(true); + const hasAdvancedFilters = haveAdvancedSearchFilters(props.initialValues); + setExpandAdvancedSearch(hasAdvancedFilters && hasAdvancedFilters !== "false"); + } + }, [props.initialValues]); + + return ( + + + + + + + {expandAdvancedSearch && ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ )} +
+ +
+
+ + +
+
+ ); +} + +export default ReportSearchForm; diff --git a/services/core-web/src/tests/components/Forms/reports/ReportPage-prr.spec.tsx b/services/core-web/src/tests/components/Forms/reports/ReportPage-prr.spec.tsx index e2728e1954..8bf860a745 100644 --- a/services/core-web/src/tests/components/Forms/reports/ReportPage-prr.spec.tsx +++ b/services/core-web/src/tests/components/Forms/reports/ReportPage-prr.spec.tsx @@ -7,7 +7,7 @@ import ReportPage from "@/components/mine/Reports/ReportPage"; import { BrowserRouter } from "react-router-dom"; import { USER_ROLES } from "@mds/common/constants/environment"; import { SystemFlagEnum } from "@mds/common/constants/enums"; -import { complianceReportReducerType } from "@mds/common/redux/slices/complianceReportsSlice"; +import { complianceReportReducerType, reportParamsGetAll } from "@mds/common/redux/slices/complianceReportsSlice"; const mineReportSubmission = MOCK.MINE_REPORT_SUBMISSIONS[1]; const initialState = { @@ -28,7 +28,8 @@ const initialState = { items_per_page: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, total: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, total_pages: 1 - } + }, + params: reportParamsGetAll }, [AUTHENTICATION]: { systemFlag: SystemFlagEnum.core, diff --git a/services/core-web/src/tests/components/Forms/reports/ReportPage.spec.tsx b/services/core-web/src/tests/components/Forms/reports/ReportPage.spec.tsx index 6afe29814f..4869540165 100644 --- a/services/core-web/src/tests/components/Forms/reports/ReportPage.spec.tsx +++ b/services/core-web/src/tests/components/Forms/reports/ReportPage.spec.tsx @@ -7,7 +7,7 @@ import { USER_ROLES } from "@mds/common/constants/environment"; import { SystemFlagEnum } from "@mds/common/constants/enums"; import ReportPage from "@/components/mine/Reports/ReportPage"; import { BrowserRouter } from "react-router-dom"; -import { complianceReportReducerType } from "@mds/common/redux/slices/complianceReportsSlice"; +import { complianceReportReducerType, reportParamsGetAll } from "@mds/common/redux/slices/complianceReportsSlice"; const mineReportSubmission = MOCK.MINE_REPORT_SUBMISSIONS[0]; const initialState = { @@ -28,7 +28,8 @@ const initialState = { items_per_page: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, total: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, total_pages: 1 - } + }, + params: reportParamsGetAll, }, [AUTHENTICATION]: { systemFlag: SystemFlagEnum.core, diff --git a/services/core-web/src/tests/components/Forms/reports/ReportPermitRequirementForm.spec.tsx b/services/core-web/src/tests/components/Forms/reports/ReportPermitRequirementForm.spec.tsx index 6f2cb87fd5..59f5ff9918 100644 --- a/services/core-web/src/tests/components/Forms/reports/ReportPermitRequirementForm.spec.tsx +++ b/services/core-web/src/tests/components/Forms/reports/ReportPermitRequirementForm.spec.tsx @@ -6,7 +6,7 @@ import * as MOCK from "@mds/common/tests/mocks/dataMocks"; import { USER_ROLES } from "@mds/common/constants/environment"; import { SystemFlagEnum } from "@mds/common/constants/enums"; import { ReportPermitRequirementForm } from "@/components/Forms/reports/ReportPermitRequirementForm"; -import { complianceReportReducerType } from "@mds/common/redux/slices/complianceReportsSlice"; +import { complianceReportReducerType, reportParamsGetAll } from "@mds/common/redux/slices/complianceReportsSlice"; const initialState = { [STATIC_CONTENT]: { @@ -21,7 +21,8 @@ const initialState = { items_per_page: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, total: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, total_pages: 1 - } + }, + params: reportParamsGetAll, }, [MINES]: MOCK.MINES, [PERMITS]: { @@ -42,6 +43,9 @@ describe("RequestReportForm", () => { permitGuid={MOCK.PERMITS[0].permit_guid} onSubmit={() => { }} condition={MOCK.PERMITS[0].permit_amendments[0].conditions[0]} + canEditPermitConditions + refreshData={jest.fn()} + mineGuid={MOCK.PERMITS[0].mine_guid} /> ); diff --git a/services/core-web/src/tests/components/Forms/reports/RequestReportForm.spec.tsx b/services/core-web/src/tests/components/Forms/reports/RequestReportForm.spec.tsx index b814fc19fd..9e8869c6fd 100644 --- a/services/core-web/src/tests/components/Forms/reports/RequestReportForm.spec.tsx +++ b/services/core-web/src/tests/components/Forms/reports/RequestReportForm.spec.tsx @@ -6,7 +6,7 @@ import * as MOCK from "@mds/common/tests/mocks/dataMocks"; import { RequestReportForm } from "@/components/Forms/reports/RequestReportForm"; import { USER_ROLES } from "@mds/common/constants/environment"; import { REPORT_TYPE_CODES, SystemFlagEnum } from "@mds/common/constants/enums"; -import { complianceReportReducerType } from "@mds/common/redux/slices/complianceReportsSlice"; +import { complianceReportReducerType, reportParamsGetAll } from "@mds/common/redux/slices/complianceReportsSlice"; const initialState = { [STATIC_CONTENT]: { @@ -21,7 +21,8 @@ const initialState = { items_per_page: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, total: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, total_pages: 1 - } + }, + params: reportParamsGetAll, }, [MINES]: MOCK.MINES, [AUTHENTICATION]: { diff --git a/services/core-web/src/tests/components/Forms/reports/__snapshots__/ReportPermitRequirementForm.spec.tsx.snap b/services/core-web/src/tests/components/Forms/reports/__snapshots__/ReportPermitRequirementForm.spec.tsx.snap index c3cd7c602e..f062e7c4cc 100644 --- a/services/core-web/src/tests/components/Forms/reports/__snapshots__/ReportPermitRequirementForm.spec.tsx.snap +++ b/services/core-web/src/tests/components/Forms/reports/__snapshots__/ReportPermitRequirementForm.spec.tsx.snap @@ -5,7 +5,7 @@ exports[`RequestReportForm renders form properly 1`] = ` style="min-height: 380px;" >
- Condition -
-
- N/A +
+ +
+
+
+
+ +
+
+
+ +
@@ -36,17 +78,62 @@ exports[`RequestReportForm renders form properly 1`] = ` style="padding: 8px 8px 8px 8px;" >
- Report Type -
-
- N/A +
+ +
+
+
+
+ +
+
+
+ +
@@ -55,17 +142,110 @@ exports[`RequestReportForm renders form properly 1`] = ` style="padding: 8px 8px 8px 8px;" >
- Report Frequency -
-
- N/A +
+ +
+
+
+
+ +
+
+
+ +
@@ -74,17 +254,96 @@ exports[`RequestReportForm renders form properly 1`] = ` style="padding: 8px 8px 8px 8px;" >
- Initial Due Date -
-
- N/A +
+ +
+
+
+
+
+
+ + + + + + +
+
+
+
+
+ +
@@ -93,7 +352,7 @@ exports[`RequestReportForm renders form properly 1`] = ` style="padding: 8px 8px 8px 8px;" >
@@ -127,17 +382,16 @@ exports[`RequestReportForm renders form properly 1`] = ` class="ant-form-item-control-input-content" >
@@ -228,7 +489,7 @@ exports[`RequestReportForm renders form properly 1`] = ` style="padding: 8px 8px 8px 8px;" >
-
- What office is the report for? -
+ What office is the report for?
@@ -359,7 +622,27 @@ exports[`RequestReportForm renders form properly 1`] = `
+ > +
+ + +
+
`; diff --git a/services/minespace-web/src/tests/components/dashboard/mine/reports/Reports.spec.tsx b/services/minespace-web/src/tests/components/dashboard/mine/reports/Reports.spec.tsx index e21eed4978..e59b655ca3 100644 --- a/services/minespace-web/src/tests/components/dashboard/mine/reports/Reports.spec.tsx +++ b/services/minespace-web/src/tests/components/dashboard/mine/reports/Reports.spec.tsx @@ -5,7 +5,10 @@ import * as MOCK from "@mds/common/tests/mocks/dataMocks"; import { ReduxWrapper } from "@/tests/utils/ReduxWrapper"; import { SidebarProvider } from "@mds/common/components/common/SidebarWrapper"; import { REPORTS } from "@mds/common/constants/reducerTypes"; -import { complianceReportReducerType } from "@mds/common/redux/slices/complianceReportsSlice"; +import { + complianceReportReducerType, + reportParamsGetAll, +} from "@mds/common/redux/slices/complianceReportsSlice"; const initialState = { [REPORTS]: { mineReports: MOCK.MINE_REPORTS, reportsPageData: MOCK.PAGE_DATA }, @@ -17,6 +20,7 @@ const initialState = { total: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, total_pages: 1, }, + params: reportParamsGetAll, }, }; From 880fc4feec4293f9409fd0f4e0fc17d4c285574b Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Fri, 31 Jan 2025 19:10:54 +0000 Subject: [PATCH 09/22] fix BE filters for boolean values --- .../reports/models/mine_report_definition.py | 19 +++++++++++++----- .../mine_report_definition_resource.py | 20 +++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/services/core-api/app/api/mines/reports/models/mine_report_definition.py b/services/core-api/app/api/mines/reports/models/mine_report_definition.py index cf3566f4ed..d65e511846 100644 --- a/services/core-api/app/api/mines/reports/models/mine_report_definition.py +++ b/services/core-api/app/api/mines/reports/models/mine_report_definition.py @@ -97,7 +97,7 @@ def _apply_sort(cls, query, sort_field, sort_dir): return query @classmethod - def _apply_filters(cls, query, regulatory_authority, is_prr_only, section): + def _apply_filters(cls, query, regulatory_authority, is_prr_only, active_ind, section): filters = [] if regulatory_authority: reg_auth_filter = [] @@ -115,8 +115,17 @@ def _apply_filters(cls, query, regulatory_authority, is_prr_only, section): else: reg_auth_filter = or_(*reg_auth_filter) filters.append(reg_auth_filter) - if is_prr_only is not None: - filters.append(MineReportDefinition.is_prr_only.is_(is_prr_only)) + # only filter if there is one value for is_prr + if len(is_prr_only) == 1: + is_prr_value = True if is_prr_only[0] == "true" else False + filters.append(MineReportDefinition.is_prr_only.is_(is_prr_value)) + # filter active index unless value is [true, false] + if len(active_ind) < 2: + current_app.logger.info('HI TARA') + current_app.logger.info(active_ind) + active_filter_value = True if False not in active_ind else False + filters.append(MineReportDefinition.active_ind.is_(active_filter_value)) + return query.filter(*filters) @classmethod @@ -139,7 +148,7 @@ def _apply_pagination(cls, query, page, per_page): } @classmethod - def apply_filters_and_pagination(cls, query, page, per_page, sort_field, sort_dir, regulatory_authority, is_prr_only, section): + def apply_filters_and_pagination(cls, query, page, per_page, sort_field, sort_dir, regulatory_authority, is_prr_only, active_ind, section): regulatory_authority = None if len(regulatory_authority) == 0 or len(regulatory_authority) == len(CimOrCpo) + 1 else regulatory_authority @@ -151,7 +160,7 @@ def apply_filters_and_pagination(cls, query, page, per_page, sort_field, sort_di query = query.join(ComplianceArticle, ComplianceArticle.compliance_article_id == MineReportDefinitionComplianceArticleXref.compliance_article_id) query = cls._apply_sort(query, sort_field, sort_dir) - query = cls._apply_filters(query, regulatory_authority, is_prr_only, section) + query = cls._apply_filters(query, regulatory_authority, is_prr_only, active_ind, section) return cls._apply_pagination(query, page, per_page) @classmethod diff --git a/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py b/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py index 959566e386..eed66e2d3f 100644 --- a/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py +++ b/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py @@ -1,6 +1,6 @@ import uuid from flask_restx import Resource, reqparse -from flask import request +from flask import request, current_app from werkzeug.exceptions import BadRequest, NotFound from app.extensions import api @@ -28,7 +28,10 @@ class MineReportDefinitionListResource(Resource, UserMixin): 'regulatory_authority', type=list, help='CIM, CPO, Both, NONE', location='args', store_missing=False ) parser.add_argument( - 'is_prr_only', type=bool, help='True for only PRR, False for only CRR', location='args', store_missing=False + 'is_prr_only', type=list, help='[true] for only prr, [false] to exclude, [true, false] for both', location='args', store_missing=False + ) + parser.add_argument( + 'active_ind', type=list, help='[true] for only active (default), [false] for only inactive, [true, false] for both', location='args', store_missing=False ) parser.add_argument( 'section', type=str, help='article # of compliance report', location='args', store_missing=False @@ -47,10 +50,18 @@ def get(self): sort_field = request.args.get('sort_field', None, type=str) sort_dir = request.args.get('sort_dir', None, type=str) regulatory_authority = request.args.getlist('regulatory_authority', type=str) - is_prr_only = request.args.get('is_prr_only', type=bool) + is_prr_only = request.args.getlist('is_prr_only', type=str) + active_ind = request.args.getlist('active_ind', type=str) section = request.args.get('section', None, type=str) - base_query = MineReportDefinition.query.filter_by(active_ind=True) + if (page and page < 1) or per_page and per_page < 0: + raise BadRequest(f'Invalid pagination values: page {page}, per_page {per_page}') + + valid_sort_fields = ['report_name', 'section', 'regulatory_authority', ] + if sort_field and sort_field not in valid_sort_fields: + raise BadRequest(f'Invalid sort_field. Valid options: {valid_sort_fields}') + + base_query = MineReportDefinition.query return MineReportDefinition.apply_filters_and_pagination( base_query, page, @@ -59,5 +70,6 @@ def get(self): sort_dir, regulatory_authority, is_prr_only, + active_ind, section ) From 293a3725756366966feb6b4197b2313f88a568de Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Fri, 31 Jan 2025 19:15:29 +0000 Subject: [PATCH 10/22] remove logs --- .../app/api/mines/reports/models/mine_report_definition.py | 2 -- .../mines/reports/resources/mine_report_definition_resource.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/services/core-api/app/api/mines/reports/models/mine_report_definition.py b/services/core-api/app/api/mines/reports/models/mine_report_definition.py index d65e511846..09fc0424cf 100644 --- a/services/core-api/app/api/mines/reports/models/mine_report_definition.py +++ b/services/core-api/app/api/mines/reports/models/mine_report_definition.py @@ -121,8 +121,6 @@ def _apply_filters(cls, query, regulatory_authority, is_prr_only, active_ind, se filters.append(MineReportDefinition.is_prr_only.is_(is_prr_value)) # filter active index unless value is [true, false] if len(active_ind) < 2: - current_app.logger.info('HI TARA') - current_app.logger.info(active_ind) active_filter_value = True if False not in active_ind else False filters.append(MineReportDefinition.active_ind.is_(active_filter_value)) diff --git a/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py b/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py index eed66e2d3f..b0d5dd2231 100644 --- a/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py +++ b/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py @@ -1,6 +1,6 @@ import uuid from flask_restx import Resource, reqparse -from flask import request, current_app +from flask import request from werkzeug.exceptions import BadRequest, NotFound from app.extensions import api From a07ec85abfa7aba8ea34b68de7d4c63e6163601d Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Fri, 31 Jan 2025 19:16:16 +0000 Subject: [PATCH 11/22] use string because boolean doesn't work --- .../app/api/mines/reports/models/mine_report_definition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core-api/app/api/mines/reports/models/mine_report_definition.py b/services/core-api/app/api/mines/reports/models/mine_report_definition.py index 09fc0424cf..10107dc3f5 100644 --- a/services/core-api/app/api/mines/reports/models/mine_report_definition.py +++ b/services/core-api/app/api/mines/reports/models/mine_report_definition.py @@ -121,7 +121,7 @@ def _apply_filters(cls, query, regulatory_authority, is_prr_only, active_ind, se filters.append(MineReportDefinition.is_prr_only.is_(is_prr_value)) # filter active index unless value is [true, false] if len(active_ind) < 2: - active_filter_value = True if False not in active_ind else False + active_filter_value = True if "false" not in active_ind else False filters.append(MineReportDefinition.active_ind.is_(active_filter_value)) return query.filter(*filters) From 83297dff355013bbc06eb2b410f2eb9d43f32d85 Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Fri, 31 Jan 2025 19:40:48 +0000 Subject: [PATCH 12/22] fix some sorting on BE, fix page size on FE --- .../reports/models/mine_report_definition.py | 21 +++++++------------ .../ComplianceReportManagement.tsx | 1 + 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/services/core-api/app/api/mines/reports/models/mine_report_definition.py b/services/core-api/app/api/mines/reports/models/mine_report_definition.py index 10107dc3f5..e06bfe01e5 100644 --- a/services/core-api/app/api/mines/reports/models/mine_report_definition.py +++ b/services/core-api/app/api/mines/reports/models/mine_report_definition.py @@ -78,21 +78,14 @@ def find_by_mine_report_definition_guid(cls, _id): def _apply_sort(cls, query, sort_field, sort_dir): if sort_field and sort_dir: field = { - 'report_name': MineReportDefinition.report_name, - 'section': [cast(ComplianceArticle.section, Integer), cast(ComplianceArticle.sub_section, Integer)], - 'regulatory_authority': ComplianceArticle.cim_or_cpo + 'report_name': [MineReportDefinition.report_name], + 'section': [cast(ComplianceArticle.section, Integer), cast(ComplianceArticle.sub_section, Integer), cast(ComplianceArticle.paragraph, Integer), ComplianceArticle.sub_paragraph], + 'regulatory_authority': [ComplianceArticle.cim_or_cpo] } - - if sort_field in ['section', 'regulatory_authority']: - if sort_field == 'section': - sort_func = [cast(ComplianceArticle.section, Integer), cast(ComplianceArticle.sub_section, Integer)] - if sort_dir == 'desc': - sort_func = [cast(ComplianceArticle.section, Integer).desc(), cast(ComplianceArticle.sub_section, Integer).desc()] - return query.order_by(*sort_func) - - sort_func = field[sort_field].asc() if sort_dir == 'asc' else field[sort_field].desc() - query = query.order_by(sort_func) - + sort_func = field[sort_field] + if sort_dir == 'desc': + sort_func = [field.desc() for field in sort_func] + query = query.order_by(*sort_func) return query diff --git a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx index c905892a3c..226f67c562 100644 --- a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx +++ b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx @@ -136,6 +136,7 @@ const ComplianceReportManagement: FC = () => { pagination={isLoaded && { total: reportPageData.total, defaultPageSize: 50, + pageSize: queryParams.per_page, position: ['bottomCenter'], disabled: !isLoaded, }} From 34d0dcdbd646d29efb9dba9d0596543a1e771e17 Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Fri, 31 Jan 2025 20:50:42 +0000 Subject: [PATCH 13/22] do FE part of section filter --- .../redux/slices/complianceReportsSlice.ts | 5 +- .../ComplianceReportManagement.tsx | 55 ++++++++++++++++++- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/services/common/src/redux/slices/complianceReportsSlice.ts b/services/common/src/redux/slices/complianceReportsSlice.ts index 5afcc02350..d095084d13 100644 --- a/services/common/src/redux/slices/complianceReportsSlice.ts +++ b/services/common/src/redux/slices/complianceReportsSlice.ts @@ -16,6 +16,7 @@ export interface ComplianceReportParams extends ISearchParams { is_prr_only?: boolean[]; regulatory_authority?: "CPO" | "CIM" | "Both" | "NONE"[]; active_ind?: boolean[]; + section?: string; }; export const reportParamsGetAll: ComplianceReportParams = { @@ -62,8 +63,10 @@ const complianceReportSlice = createAppSlice({ state.reportPageData = { records, current_page, items_per_page, total, total_pages }; state.params = action.meta.arg; }, - rejected: (_state: ComplianceReportState, action) => { + rejected: (state: ComplianceReportState, action) => { rejectHandler(action); + // don't show loading forever if there's an error + state.params = action.meta.arg; } } ) diff --git a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx index 226f67c562..c0aef94513 100644 --- a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx +++ b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx @@ -1,5 +1,5 @@ import React, { FC, useEffect, useState } from "react"; -import { Button, Row, Typography } from "antd"; +import { Button, Input, Row, Typography } from "antd"; import PlusOutlined from "@ant-design/icons/PlusOutlined"; import CoreTable from "@mds/common/components/common/CoreTable"; import { useAppDispatch as useDispatch, useAppSelector as useSelector } from "@mds/common/redux/rootState"; @@ -10,6 +10,7 @@ import { REPORT_REGULATORY_AUTHORITY_CODES, REPORT_REGULATORY_AUTHORITY_ENUM } f import { formatComplianceCodeArticleNumber } from "@mds/common/redux/utils/helpers"; import { EMPTY_FIELD, REPORT_REGULATORY_AUTHORITY_CODES_HASH } from "@mds/common/constants/strings"; import { removeNullValues } from "@mds/common/constants/utils"; +import SearchOutlined from "@ant-design/icons/SearchOutlined"; const defaultParams = { @@ -21,6 +22,7 @@ const ComplianceReportManagement: FC = () => { const dispatch = useDispatch(); const reportPageData = useSelector(getComplianceReportPageData); const reportDefinitions = reportPageData?.records ?? []; + const [sectionSearchText, setSectionSearchText] = useState(null); const [queryParams, setQueryParams] = useState(defaultParams); const isLoaded = useSelector(getReportDefinitionsLoaded(queryParams)); @@ -54,6 +56,15 @@ const ComplianceReportManagement: FC = () => { setQueryParams(newParams) }; + const handleSectionSearch = (confirm, searchInputText: string) => { + const newParams = { + ...queryParams, + section: searchInputText + }; + setQueryParams(removeNullValues(newParams)); + confirm(); + }; + const openAddModal = () => { console.log('not implemented'); }; @@ -69,10 +80,48 @@ const ComplianceReportManagement: FC = () => { }]; const regulatoryAuthorityFilter = Object.entries(REPORT_REGULATORY_AUTHORITY_CODES_HASH).map(([value, text]) => ({ value, text })); - + const sectionSearchActive = queryParams?.section?.length > 0; + + const sectionFilter = { + filterIcon: () => , + filterDropdown: ({ confirm }) => { + return ( +
+ + setSectionSearchText(e.target.value)} + onPressEnter={() => handleSectionSearch(confirm, sectionSearchText)} + /> + + + +
+ ); + } + } const columns = [ renderTextColumn("report_name", "Report Name", true), - { ...renderTextColumn("section", "Section"), sorter: true }, // filter + { + ...renderTextColumn("section", "Section"), + sorter: true, + ...sectionFilter + }, { ...renderTextColumn("is_prr_only", "Report Type"), filters: [ From 68058751b6b45e827668f48a6d5dfebfa5310b8d Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Fri, 31 Jan 2025 22:47:43 +0000 Subject: [PATCH 14/22] BE compliance article # search, little bit of clean up on codes page --- .../reports/models/mine_report_definition.py | 16 +++++++- .../ComplianceCodeManagement.tsx | 6 +-- .../ComplianceCodeManagement.spec.tsx.snap | 37 +------------------ 3 files changed, 17 insertions(+), 42 deletions(-) diff --git a/services/core-api/app/api/mines/reports/models/mine_report_definition.py b/services/core-api/app/api/mines/reports/models/mine_report_definition.py index e06bfe01e5..28681728ef 100644 --- a/services/core-api/app/api/mines/reports/models/mine_report_definition.py +++ b/services/core-api/app/api/mines/reports/models/mine_report_definition.py @@ -86,7 +86,7 @@ def _apply_sort(cls, query, sort_field, sort_dir): if sort_dir == 'desc': sort_func = [field.desc() for field in sort_func] query = query.order_by(*sort_func) - + return query @classmethod @@ -116,7 +116,19 @@ def _apply_filters(cls, query, regulatory_authority, is_prr_only, active_ind, se if len(active_ind) < 2: active_filter_value = True if "false" not in active_ind else False filters.append(MineReportDefinition.active_ind.is_(active_filter_value)) - + + if section: + section_parts = section.split(".") + section_order = [ + ComplianceArticle.section, + ComplianceArticle.sub_section, + ComplianceArticle.paragraph, + ComplianceArticle.sub_paragraph] + + for index, part in enumerate(section_parts): + field_name = section_order[index] + filters.append(field_name.ilike(part)) + return query.filter(*filters) @classmethod diff --git a/services/core-web/src/components/admin/complianceCodes/ComplianceCodeManagement.tsx b/services/core-web/src/components/admin/complianceCodes/ComplianceCodeManagement.tsx index cb3a41c0bb..ba4c8e4b61 100644 --- a/services/core-web/src/components/admin/complianceCodes/ComplianceCodeManagement.tsx +++ b/services/core-web/src/components/admin/complianceCodes/ComplianceCodeManagement.tsx @@ -1,5 +1,5 @@ import React, { FC, useEffect, useState } from "react"; -import { useDispatch, useSelector } from "react-redux"; +import { useAppDispatch as useDispatch, useAppSelector as useSelector } from "@mds/common/redux/rootState"; import { change, Field, initialize, reset } from "redux-form"; import SearchOutlined from "@ant-design/icons/SearchOutlined"; import PlusOutlined from "@ant-design/icons/PlusOutlined"; @@ -8,7 +8,6 @@ import CoreTable from "@mds/common/components/common/CoreTable"; import { renderActionsColumn, renderDateColumn, - renderTextColumn, } from "@mds/common/components/common/CoreTableCommonColumns"; import FormWrapper from "@mds/common/components/forms/FormWrapper"; import { required } from "@mds/common/redux/utils/Validate"; @@ -131,7 +130,7 @@ const ComplianceCodeManagement: FC = () => { }; const handleSearch = (confirm, field, searchInputText) => { - if (searchInputText && searchInputText.length) { + if (searchInputText?.length) { const filteredRecords = Object.values(complianceCodes) .filter((code) => { return code[field] @@ -232,7 +231,6 @@ const ComplianceCodeManagement: FC = () => { ); }, }, - renderTextColumn("description", "Description"), { ...renderDateColumn("effective_date", "Date Active"), width: 150 }, { title: "Date Expire", diff --git a/services/core-web/src/tests/components/admin/__snapshots__/ComplianceCodeManagement.spec.tsx.snap b/services/core-web/src/tests/components/admin/__snapshots__/ComplianceCodeManagement.spec.tsx.snap index 5b301bb373..29e01df8ad 100644 --- a/services/core-web/src/tests/components/admin/__snapshots__/ComplianceCodeManagement.spec.tsx.snap +++ b/services/core-web/src/tests/components/admin/__snapshots__/ComplianceCodeManagement.spec.tsx.snap @@ -126,11 +126,6 @@ exports[`PermitConditionsNavigation renders properly 1`] = ` > Description - - Description - @@ -160,7 +155,6 @@ exports[`PermitConditionsNavigation renders properly 1`] = ` style="width: 150px;" /> - @@ -221,15 +215,6 @@ exports[`PermitConditionsNavigation renders properly 1`] = `   - -
-   -
- - -
- Spills -
- @@ -335,16 +310,6 @@ exports[`PermitConditionsNavigation renders properly 1`] = ` - -
- Flammable Waste Storage -
- @@ -418,7 +383,7 @@ exports[`PermitConditionsNavigation renders properly 1`] = `
Date: Tue, 4 Feb 2025 17:11:35 +0000 Subject: [PATCH 15/22] add backend search, tweak ordering of section sort --- .../reports/models/mine_report_definition.py | 6 +- .../test_mine_report_definition_resource.py | 244 ++++++++++++++++++ 2 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 services/core-api/tests/reports/resource/test_mine_report_definition_resource.py diff --git a/services/core-api/app/api/mines/reports/models/mine_report_definition.py b/services/core-api/app/api/mines/reports/models/mine_report_definition.py index 28681728ef..20f868ca33 100644 --- a/services/core-api/app/api/mines/reports/models/mine_report_definition.py +++ b/services/core-api/app/api/mines/reports/models/mine_report_definition.py @@ -4,7 +4,7 @@ from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.schema import FetchedValue from sqlalchemy.ext.hybrid import hybrid_property -from sqlalchemy import or_, cast, Integer +from sqlalchemy import or_, cast, Integer, nullsfirst, nullslast from sqlalchemy_filters import apply_pagination from app.api.mines.reports.models.mine_report_permit_requirement import CimOrCpo @@ -85,6 +85,10 @@ def _apply_sort(cls, query, sort_field, sort_dir): sort_func = field[sort_field] if sort_dir == 'desc': sort_func = [field.desc() for field in sort_func] + # sort section asc with nulls first because linguistically we want section 1 before 1.1 but we don't want None before CPO + if sort_field == 'section': + nullfunc = nullsfirst if sort_dir == 'asc' else nullslast + sort_func = [nullfunc(field) for field in sort_func] query = query.order_by(*sort_func) return query diff --git a/services/core-api/tests/reports/resource/test_mine_report_definition_resource.py b/services/core-api/tests/reports/resource/test_mine_report_definition_resource.py new file mode 100644 index 0000000000..7409234b69 --- /dev/null +++ b/services/core-api/tests/reports/resource/test_mine_report_definition_resource.py @@ -0,0 +1,244 @@ +import json +import math +from urllib import parse +from app.api.mines.reports.models.mine_report_definition import MineReportDefinition + +# test non-paginated results +def test_post_mine_report_definition_no_pagination(test_client, db_session, auth_headers): + active_records = db_session.query(MineReportDefinition).filter_by(active_ind=True).all() + record_count = len(active_records) + request_data = {} + + get_resp = test_client.get( + f'/mines/reports/definitions?{parse.urlencode(request_data)}', + headers=auth_headers['full_auth_header'], + ) + get_data = json.loads(get_resp.data.decode()) + + assert get_resp.status_code == 200 + assert len(get_data['records']) == record_count + assert(get_data['current_page']) == 1 + assert(get_data['total_pages']) == 1 + assert(get_data['items_per_page']) == record_count + assert(get_data['total']) == record_count + + +# test pagination +def test_post_mine_report_definition_pagination(test_client, db_session, auth_headers): + PAGE = 2 + PER_PAGE = 10 + active_records = db_session.query(MineReportDefinition).filter_by(active_ind=True).all() + record_count = len(active_records) + request_data = {"page": PAGE, "per_page": PER_PAGE} + + get_resp = test_client.get( + f'/mines/reports/definitions?{parse.urlencode(request_data)}', + headers=auth_headers['full_auth_header'], + ) + get_data = json.loads(get_resp.data.decode()) + + assert get_resp.status_code == 200 + assert len(get_data['records']) == PER_PAGE + assert(get_data['current_page']) == PAGE + assert(get_data['total_pages']) == math.ceil(record_count / PER_PAGE) + assert(get_data['items_per_page']) == PER_PAGE + assert(get_data['total']) == record_count + +# test filters +def test_mine_report_definition_active_filter(test_client, db_session, auth_headers): + all_records = db_session.query(MineReportDefinition).all() + all_record_count = len(all_records) + request_all_data = "active_ind=true&active_ind=false" + request_inactive_data = "active_ind=false" + + get_all_resp = test_client.get( + f'/mines/reports/definitions?{request_all_data}', + headers=auth_headers['full_auth_header'], + ) + get_all_data = json.loads(get_all_resp.data.decode()) + + assert get_all_resp.status_code == 200 + assert len(get_all_data['records']) == all_record_count + + inactive_records = list(x for x in all_records if x.active_ind == False) + inactive_record_count = len(inactive_records) + + get_inactive_resp = test_client.get( + f'/mines/reports/definitions?{request_inactive_data}', + headers=auth_headers['full_auth_header'], + ) + get_inactive_data = json.loads(get_inactive_resp.data.decode()) + + assert get_inactive_resp.status_code == 200 + assert len(get_inactive_data['records']) == inactive_record_count + +def test_mine_report_definition_section_filter(test_client, db_session, auth_headers): + section_search = "2.3.1" + request_data = {"section": section_search} + + get_resp = test_client.get( + f'/mines/reports/definitions?{parse.urlencode(request_data)}', + headers=auth_headers['full_auth_header'], + ) + get_data = json.loads(get_resp.data.decode()) + + assert get_resp.status_code == 200 + + for record in get_data['records']: + compliance_article = record['compliance_articles'][0] + assert compliance_article['section'] == '2' + assert compliance_article['sub_section'] == '3' + assert compliance_article['paragraph'] == '1' + +def test_mine_report_definition_section_alpha_filter(test_client, db_session, auth_headers): + active_records = db_session.query(MineReportDefinition).filter_by(active_ind=True).all() + # find a record with a non numeric, lowercase sub_paragraph to search + alpha_records = list(x for x in active_records + if x.compliance_articles[0].sub_paragraph is not None and not x.compliance_articles[0].sub_paragraph.isnumeric() + and x.compliance_articles[0].sub_paragraph.lower() == x.compliance_articles[0].sub_paragraph + ) + article_search = alpha_records[0].compliance_articles[0] + # uppercase the sub_paragraph for searching + section_data = [article_search.section, article_search.sub_section, article_search.paragraph, article_search.sub_paragraph.upper()] + section_search_string = ".".join(section_data) + request_data = {"section": section_search_string} + + get_resp = test_client.get( + f'/mines/reports/definitions?{parse.urlencode(request_data)}', + headers=auth_headers['full_auth_header'], + ) + get_data = json.loads(get_resp.data.decode()) + + guids = (x['mine_report_definition_guid'] for x in get_data['records']) + + assert get_resp.status_code == 200 + assert str(alpha_records[0].mine_report_definition_guid) in guids + + for record in get_data['records']: + compliance_article = record['compliance_articles'][0] + assert compliance_article['section'] == article_search.section + assert compliance_article['sub_section'] == article_search.sub_section + assert compliance_article['paragraph'] == article_search.paragraph + assert compliance_article['sub_paragraph'].lower() == article_search.sub_paragraph.lower() + + +def test_mine_report_definition_report_type_filter(test_client, db_session, auth_headers): + crr_request_data = {"is_prr_only": "false"} + prr_request_data = {"is_prr_only": "true"} + + crr_get_resp = test_client.get( + f'/mines/reports/definitions?{parse.urlencode(crr_request_data)}', + headers=auth_headers['full_auth_header'], + ) + crr_get_data = json.loads(crr_get_resp.data.decode()) + + assert crr_get_resp.status_code == 200 + for record in crr_get_data['records']: + assert record['is_prr_only'] == False + + prr_get_resp = test_client.get( + f'/mines/reports/definitions?{parse.urlencode(prr_request_data)}', + headers=auth_headers['full_auth_header'], + ) + prr_get_data = json.loads(prr_get_resp.data.decode()) + + assert prr_get_resp.status_code == 200 + for record in prr_get_data['records']: + assert record['is_prr_only'] == True + + +def test_mine_report_definition_reg_auth_filter(test_client, db_session, auth_headers): + active_records = db_session.query(MineReportDefinition).filter_by(active_ind=True).all() + cpo_none_request_data = "regulatory_authority=CPO®ulatory_authority=NONE" + cpo_cim_request_data = "regulatory_authority=CPO®ulatory_authority=CIM" + + # include a "None" value + cn_get_resp = test_client.get( + f'/mines/reports/definitions?{cpo_none_request_data}', + headers=auth_headers['full_auth_header'], + ) + cn_get_data = json.loads(cn_get_resp.data.decode()) + + assert cn_get_resp.status_code == 200 + cn_expected_data = list(x for x in active_records if x.compliance_articles[0].cim_or_cpo == 'CPO' or x.compliance_articles[0].cim_or_cpo is None) + assert len(cn_get_data['records']) == len(cn_expected_data) + for record in cn_get_data['records']: + assert record['compliance_articles'][0]['cim_or_cpo'] in ['CPO', None] + + # don't include a "None" value + cc_get_resp = test_client.get( + f'/mines/reports/definitions?{cpo_cim_request_data}', + headers=auth_headers['full_auth_header'], + ) + cc_get_data = json.loads(cc_get_resp.data.decode()) + + assert cc_get_resp.status_code == 200 + cc_expected_data = list(x for x in active_records if x.compliance_articles[0].cim_or_cpo in ['CPO', 'CIM']) + assert len(cc_get_data['records']) == len(cc_expected_data) + for record in cc_get_data['records']: + assert record['compliance_articles'][0]['cim_or_cpo'] in ['CPO', 'CIM'] + +# test sort +def test_mine_report_definition_sort_by_report_name_desc(test_client, db_session, auth_headers): + request_data = {"sort_field": "report_name", "sort_dir": "desc"} + + get_resp = test_client.get( + f'/mines/reports/definitions?{parse.urlencode(request_data)}', + headers=auth_headers['full_auth_header'], + ) + get_data = json.loads(get_resp.data.decode()) + + assert get_resp.status_code == 200 + for index, report in enumerate(get_data['records']): + if index > 0: + prev_record = get_data['records'][index - 1] + assert report['report_name'].lower() < prev_record['report_name'].lower() + +def test_mine_report_definition_sort_by_section(test_client, db_session, auth_headers): + request_data = {"sort_field": "section", "sort_dir": "asc"} + + get_resp = test_client.get( + f'/mines/reports/definitions?{parse.urlencode(request_data)}', + headers=auth_headers['full_auth_header'], + ) + get_data = json.loads(get_resp.data.decode()) + + assert get_resp.status_code == 200 + for index, report in enumerate(get_data['records']): + if index > 0: + prev_article = get_data['records'][index - 1]['compliance_articles'][0] + current_article = report['compliance_articles'][0] + + assert int(current_article['section']) >= int(prev_article['section']) + + same_section = current_article['section'] == prev_article['section'] + if same_section: + assert int(current_article['sub_section'] or 0) >= int(prev_article['sub_section'] or 0) + + same_sub_section = current_article['sub_section'] == prev_article['sub_section'] + if same_sub_section: + assert int(current_article['paragraph'] or 0) >= int(prev_article['paragraph'] or 0) + + same_paragraph = current_article['paragraph'] == prev_article['paragraph'] + if same_paragraph: + current_sp = current_article['sub_paragraph'] or '' + prev_sp = prev_article['sub_paragraph'] or '' + assert current_sp.lower() >= prev_sp.lower() + +def test_mine_report_definition_sort_by_regulatory_authority(test_client, db_session, auth_headers): + + request_data = {'sort_field': 'regulatory_authority', 'sort_dir': 'asc'} + + get_resp = test_client.get( + f'/mines/reports/definitions?{parse.urlencode(request_data)}', + headers=auth_headers['full_auth_header'], + ) + get_data = json.loads(get_resp.data.decode()) + + assert get_resp.status_code == 200 + reg_auth_values = list(report['compliance_articles'][0]['cim_or_cpo'] or "None" for report in get_data['records']) + + for index, reg_auth_val in enumerate(reg_auth_values): + if index > 0: + prev_val = reg_auth_values[index - 1] + assert reg_auth_val >= prev_val From 5d9d845fdef1f215298930fd177f55e57e134b04 Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Tue, 4 Feb 2025 17:28:31 +0000 Subject: [PATCH 16/22] add api param documentation, take out unecessary param --- .../src/redux/slices/complianceReportsSlice.ts | 1 - .../resources/mine_report_definition_resource.py | 12 +++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/services/common/src/redux/slices/complianceReportsSlice.ts b/services/common/src/redux/slices/complianceReportsSlice.ts index d095084d13..63badea80e 100644 --- a/services/common/src/redux/slices/complianceReportsSlice.ts +++ b/services/common/src/redux/slices/complianceReportsSlice.ts @@ -20,7 +20,6 @@ export interface ComplianceReportParams extends ISearchParams { }; export const reportParamsGetAll: ComplianceReportParams = { - per_page: 0, active_ind: [true, false] }; diff --git a/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py b/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py index b0d5dd2231..4af504ec3d 100644 --- a/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py +++ b/services/core-api/app/api/mines/reports/resources/mine_report_definition_resource.py @@ -16,7 +16,7 @@ class MineReportDefinitionListResource(Resource, UserMixin): 'page', type=int, help='page for pagination', location='args', store_missing=False ) parser.add_argument( - 'per_page', type=int, help='records per page- 0 to retrieve all records', location='args', store_missing=False + 'per_page', type=int, help='records per page to retrieve all records', location='args', store_missing=False ) parser.add_argument( 'sort_field', type=str, help='field to sort by', location='args', store_missing=False @@ -38,8 +38,14 @@ class MineReportDefinitionListResource(Resource, UserMixin): ) @api.doc( params={ - 'page': 'The page number of paginated records to return. Default', - 'per_page': 'The number of records to return per page', + 'page': 'The page number of paginated records to return', + 'per_page': 'The number of records to return per page. None for all records', + 'sort_field': 'field to sort by', + 'sort_dir': 'direction to sort by: asc or desc', + 'regulatory_authority': 'CIM, CPO, Both, NONE', + 'is_prr_only': '[true] for only prr, [false] to exclude, [true, false] for both', + 'active_ind': '[true] for only active (default), [false] for only inactive, [true, false] for both', + 'section': 'article # of compliance report' }, description='returns the report definitions for possible reports.') @api.marshal_with(PAGINATED_MINE_REPORT_DEFINITION_MODEL, code=200) From af4d5991356cfc514ea068c50fbee6e88a9744d1 Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Tue, 4 Feb 2025 17:38:47 +0000 Subject: [PATCH 17/22] fix bug with searching section first, put search params in url --- .../complianceCodes/ComplianceReportManagement.tsx | 11 +++++++++-- services/core-web/src/constants/routes.ts | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx index c0aef94513..04d0805739 100644 --- a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx +++ b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.tsx @@ -1,5 +1,6 @@ import React, { FC, useEffect, useState } from "react"; import { Button, Input, Row, Typography } from "antd"; +import queryString from "query-string"; import PlusOutlined from "@ant-design/icons/PlusOutlined"; import CoreTable from "@mds/common/components/common/CoreTable"; import { useAppDispatch as useDispatch, useAppSelector as useSelector } from "@mds/common/redux/rootState"; @@ -11,6 +12,8 @@ import { formatComplianceCodeArticleNumber } from "@mds/common/redux/utils/helpe import { EMPTY_FIELD, REPORT_REGULATORY_AUTHORITY_CODES_HASH } from "@mds/common/constants/strings"; import { removeNullValues } from "@mds/common/constants/utils"; import SearchOutlined from "@ant-design/icons/SearchOutlined"; +import { useHistory, useLocation } from "react-router-dom"; +import { ADMIN_HSRC_COMPLIANCE_CODE_MANAGEMENT } from "@/constants/routes"; const defaultParams = { @@ -20,10 +23,13 @@ const defaultParams = { const ComplianceReportManagement: FC = () => { const dispatch = useDispatch(); + const { search } = useLocation(); + const history = useHistory(); + const initialParams = queryString.parse(search); const reportPageData = useSelector(getComplianceReportPageData); const reportDefinitions = reportPageData?.records ?? []; const [sectionSearchText, setSectionSearchText] = useState(null); - const [queryParams, setQueryParams] = useState(defaultParams); + const [queryParams, setQueryParams] = useState({ ...defaultParams, ...initialParams }); const isLoaded = useSelector(getReportDefinitionsLoaded(queryParams)); const fetchData = () => { @@ -31,6 +37,7 @@ const ComplianceReportManagement: FC = () => { }; useEffect(() => { + history.replace(ADMIN_HSRC_COMPLIANCE_CODE_MANAGEMENT.dynamicRoute("reports", queryParams)) if (!isLoaded) { fetchData(); } @@ -39,7 +46,7 @@ const ComplianceReportManagement: FC = () => { const onTableChange = (pagination, filters, sorter) => { const { current: page, pageSize: per_page } = pagination; const { order, field: sort_field } = sorter; - const activeFilters = removeNullValues(filters); + const activeFilters = removeNullValues({ ...filters, section: sectionSearchText }); const sortParams = order ? { sort_dir: order.replace("end", ""), diff --git a/services/core-web/src/constants/routes.ts b/services/core-web/src/constants/routes.ts index a4b9c24e9c..76c871fa29 100644 --- a/services/core-web/src/constants/routes.ts +++ b/services/core-web/src/constants/routes.ts @@ -502,7 +502,7 @@ export const ADMIN_CONTACT_MANAGEMENT = { export const ADMIN_HSRC_COMPLIANCE_CODE_MANAGEMENT = { route: "/admin/hsrc-management/:tab", component: ComplianceManagement, - dynamicRoute: (tab: string) => `/admin/hsrc-management/${tab}`, + dynamicRoute: (tab: string, params = {}) => `/admin/hsrc-management/${tab}?${queryString.stringify(params)}`, helpKey: "HSRC-Code-Management", }; From d37567a6cb32ce5e4d57532293b6e567b227c428 Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Tue, 4 Feb 2025 18:55:47 +0000 Subject: [PATCH 18/22] FE snapshot test, make params comparison better --- .../redux/slices/complianceReportsSlice.ts | 3 +- .../ComplianceReportManagement.spec.tsx | 64 + .../ComplianceReportManagement.spec.tsx.snap | 1799 +++++++++++++++++ 3 files changed, 1865 insertions(+), 1 deletion(-) create mode 100644 services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.spec.tsx create mode 100644 services/core-web/src/components/admin/complianceCodes/__snapshots__/ComplianceReportManagement.spec.tsx.snap diff --git a/services/common/src/redux/slices/complianceReportsSlice.ts b/services/common/src/redux/slices/complianceReportsSlice.ts index 63badea80e..4f41bdbd05 100644 --- a/services/common/src/redux/slices/complianceReportsSlice.ts +++ b/services/common/src/redux/slices/complianceReportsSlice.ts @@ -8,6 +8,7 @@ import { createDropDownList, createItemMap, formatComplianceCodeReportName } fro import { createSelectorWrapper } from "../selectors/staticContentSelectors"; import { createSelector } from "@reduxjs/toolkit"; import { ISearchParams } from "@mds/common/interfaces/common/searchParams.interface"; +import { isEqual } from "lodash"; export const complianceReportReducerType = 'complianceReports' @@ -118,7 +119,7 @@ export const getMineReportDefinitionByGuid = (mineReportDefinitionGuid: string) export const getReportDefinitionsLoaded = (params: ComplianceReportParams) => createSelector([getReportSearchParams], (currentParams) => { - return params === currentParams; + return isEqual(params, currentParams); }) export const { diff --git a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.spec.tsx b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.spec.tsx new file mode 100644 index 0000000000..b7fbe2d1e7 --- /dev/null +++ b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.spec.tsx @@ -0,0 +1,64 @@ +import React from "react"; +import { render } from "@testing-library/react"; +import * as MOCK from "@mds/common/tests/mocks/dataMocks"; +import { ReduxWrapper } from "@/tests/utils/ReduxWrapper"; +import { + complianceReportReducerType, +} from "@mds/common/redux/slices/complianceReportsSlice"; +import { REPORTS } from "@mds/common/constants/reducerTypes"; +import ComplianceManagement from "./ComplianceManagement"; +import queryString from "query-string"; + +const reportParams = { + page: '1', + per_page: '50', + is_prr_only: 'false', + regulatory_authority: ['CIM', 'CPO'], + section: '1.1', + sort_dir: 'asc', + sort_field: 'report_name' +}; +const reportParamsString = queryString.stringify(reportParams); +const initialState = { + [REPORTS]: { mineReports: MOCK.MINE_REPORTS, reportsPageData: MOCK.PAGE_DATA }, + [complianceReportReducerType]: { + reportPageData: { + records: MOCK.MINE_REPORT_DEFINITION_OPTIONS, + current_page: 1, + items_per_page: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, + total: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, + total_pages: 1, + }, + params: reportParams, + }, +}; + + +function mockFunction() { + const original = jest.requireActual("react-router-dom"); + return { + ...original, + useParams: jest.fn().mockReturnValue({ + tab: "reports" + }), + // mockImplementation (vs mockReturnValue) necessary to avoid a ReferenceError + useLocation: jest.fn().mockImplementation(() => ({ search: reportParamsString })), + useHistory: jest.fn().mockReturnValue({ + replace: jest.fn() + }) + } +}; + +jest.mock("react-router-dom", () => mockFunction()); + +describe("ComplianceReportManagement", () => { + it("renders properly", () => { + const { container } = render( + + + + ); + + expect(container).toMatchSnapshot(); + }); +}) \ No newline at end of file diff --git a/services/core-web/src/components/admin/complianceCodes/__snapshots__/ComplianceReportManagement.spec.tsx.snap b/services/core-web/src/components/admin/complianceCodes/__snapshots__/ComplianceReportManagement.spec.tsx.snap new file mode 100644 index 0000000000..b3dd4efa58 --- /dev/null +++ b/services/core-web/src/components/admin/complianceCodes/__snapshots__/ComplianceReportManagement.spec.tsx.snap @@ -0,0 +1,1799 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ComplianceReportManagement renders properly 1`] = ` +
+
+
+

+ Permit Condition Management +

+
+ + +
+`; From f4517b948f3b7eff4e7c3a798d7f706d60a75fd6 Mon Sep 17 00:00:00 2001 From: Tara Epp <102187683+taraepp@users.noreply.github.com> Date: Tue, 4 Feb 2025 20:46:42 +0000 Subject: [PATCH 19/22] make test fancier --- services/common/src/tests/handlers.ts | 21 +- .../ComplianceReportManagement.spec.tsx | 29 +- .../ComplianceReportManagement.spec.tsx.snap | 427 +----------------- 3 files changed, 50 insertions(+), 427 deletions(-) diff --git a/services/common/src/tests/handlers.ts b/services/common/src/tests/handlers.ts index 9288614105..189e449bce 100644 --- a/services/common/src/tests/handlers.ts +++ b/services/common/src/tests/handlers.ts @@ -4,10 +4,12 @@ import { HELP_GUIDE_CORE, HELP_GUIDE_MS, MINE_REPORT_CATEGORY_OPTIONS, + MINE_REPORT_DEFINITION_OPTIONS, PERMIT_CONDITION_EXTRACTION, PROJECT, PROJECT_SUMMARY_MINISTRY_COMMENTS, } from "@mds/common/tests/mocks/dataMocks"; +import queryString from "query-string"; import { SystemFlagEnum } from "../constants/enums"; const geoSpatialHandlers = [ @@ -52,6 +54,23 @@ const helpHandler = http.get("/%3CAPI_URL%3E/help/:helpKey", async ({ request, p return HttpResponse.json(response); }); -const commonHandlers = [...geoSpatialHandlers, ...projectHandlers, helpHandler, ...permitHandlers]; +const complianceReportHandler = http.get("/%3CAPI_URL%3E/mines/reports/definitions", async ({ request }) => { + const url = new URL(request.url); + const paramString = queryString.parse(url.searchParams.toString()) + const { page = 1, per_page = MINE_REPORT_DEFINITION_OPTIONS.length } = paramString; + + const complianceReportData = MINE_REPORT_DEFINITION_OPTIONS.slice(0, per_page); + + const response = { + records: complianceReportData, + current_page: page, + items_per_Page: per_page, + total: MINE_REPORT_DEFINITION_OPTIONS.length, + total_pages: Math.ceil(per_page / page) + }; + return HttpResponse.json(response); +}); + +const commonHandlers = [...geoSpatialHandlers, ...projectHandlers, helpHandler, ...permitHandlers, complianceReportHandler]; export default commonHandlers; diff --git a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.spec.tsx b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.spec.tsx index b7fbe2d1e7..fcbd0d985e 100644 --- a/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.spec.tsx +++ b/services/core-web/src/components/admin/complianceCodes/ComplianceReportManagement.spec.tsx @@ -1,17 +1,16 @@ import React from "react"; -import { render } from "@testing-library/react"; +import { render, waitFor } from "@testing-library/react"; import * as MOCK from "@mds/common/tests/mocks/dataMocks"; import { ReduxWrapper } from "@/tests/utils/ReduxWrapper"; import { complianceReportReducerType, } from "@mds/common/redux/slices/complianceReportsSlice"; -import { REPORTS } from "@mds/common/constants/reducerTypes"; import ComplianceManagement from "./ComplianceManagement"; import queryString from "query-string"; const reportParams = { - page: '1', - per_page: '50', + page: 1, + per_page: 10, is_prr_only: 'false', regulatory_authority: ['CIM', 'CPO'], section: '1.1', @@ -19,21 +18,20 @@ const reportParams = { sort_field: 'report_name' }; const reportParamsString = queryString.stringify(reportParams); + const initialState = { - [REPORTS]: { mineReports: MOCK.MINE_REPORTS, reportsPageData: MOCK.PAGE_DATA }, [complianceReportReducerType]: { reportPageData: { - records: MOCK.MINE_REPORT_DEFINITION_OPTIONS, - current_page: 1, - items_per_page: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, - total: MOCK.MINE_REPORT_DEFINITION_OPTIONS.length, - total_pages: 1, + records: [], + current_page: 0, + items_per_page: 0, + total: 0, + total_pages: 0 }, - params: reportParams, + params: {} }, }; - function mockFunction() { const original = jest.requireActual("react-router-dom"); return { @@ -52,13 +50,18 @@ function mockFunction() { jest.mock("react-router-dom", () => mockFunction()); describe("ComplianceReportManagement", () => { - it("renders properly", () => { + it("renders properly", async () => { const { container } = render( ); + const spinner = container.querySelector(".ant-spin-spinning"); + await waitFor(() => { + expect(spinner).not.toBeInTheDocument(); + }) + expect(container).toMatchSnapshot(); }); }) \ No newline at end of file diff --git a/services/core-web/src/components/admin/complianceCodes/__snapshots__/ComplianceReportManagement.spec.tsx.snap b/services/core-web/src/components/admin/complianceCodes/__snapshots__/ComplianceReportManagement.spec.tsx.snap index b3dd4efa58..32ef75ea18 100644 --- a/services/core-web/src/components/admin/complianceCodes/__snapshots__/ComplianceReportManagement.spec.tsx.snap +++ b/services/core-web/src/components/admin/complianceCodes/__snapshots__/ComplianceReportManagement.spec.tsx.snap @@ -1295,416 +1295,6 @@ exports[`ComplianceReportManagement renders properly 1`] = `
- - -
- Careless Acts Report -
- - -
- 8.3.9 -
- - -
- Code Required Report -
- - -
- Chief Permitting Officer -
- - -
- -
- - - - -
- Drilling Precaution Procedures Report -
- - -
- 8.7.2 -
- - -
- Code Required Report -
- - -
- Both -
- - -
- -
- - - - -
- Annual Summary of Exploration Activities -
- - -
- 9.2.1 -
- - -
- Code Required Report -
- - -
- Not specified -
- - -
- -
- - - - -
- Management Plan for Riparian Area -
- - -
- 9.5.1 -
- - -
- Code Required Report -
- - -
- Chief Inspector of Mines -
- - -
- -
- - - - -
- Terrain Stability Remediation Plan -
- - -
- 9.7.1 -
- - -
- Code Required Report -
- - -
- Chief Permitting Officer -
- - -
- -
- -
@@ -1757,13 +1347,24 @@ exports[`ComplianceReportManagement renders properly 1`] = `
  • + + 2 + +
  • +