diff --git a/.eslintrc.js b/.eslintrc.js index c1d2698120..ef2744dbbb 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,6 +9,7 @@ module.exports = { "services/minespace-web/cypress.config.ts", "services/minespace-web/cypress/**", "services/core-web/webpack.config.ts", + "services/core-web/webpack.parts.js", "services/core-web/cypress.config.ts", "services/core-web/cypress/**", ], diff --git a/services/common/src/@Types/declarations.d.ts b/services/common/src/@Types/declarations.d.ts new file mode 100644 index 0000000000..1a3dd3c2a2 --- /dev/null +++ b/services/common/src/@Types/declarations.d.ts @@ -0,0 +1,4 @@ +declare module "*.svg" { + const content: any; + export default content; +} diff --git a/services/common/src/@Types/global.d.ts b/services/common/src/@Types/global.d.ts index 34dbb92ef9..1869ec2b25 100644 --- a/services/common/src/@Types/global.d.ts +++ b/services/common/src/@Types/global.d.ts @@ -1,13 +1,16 @@ declare global { const REQUEST_HEADER: { - createRequestHeader: (customHeaders?: any) => { + createRequestHeader: ( + customHeaders?: any + ) => { headers: { - "Access-Control-Allow-Origin": string, - Authorization: string, - [prop: string]: any, - }, - }, + "Access-Control-Allow-Origin": string; + Authorization: string; + [prop: string]: any; + }; + }; }; + const GLOBAL_ROUTES: any; } -export {}; \ No newline at end of file +export {}; diff --git a/services/common/src/assets/images/small-pin-selected.svg b/services/common/src/assets/images/small-pin-selected.svg new file mode 100644 index 0000000000..4a8a37c804 --- /dev/null +++ b/services/common/src/assets/images/small-pin-selected.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/services/core-web/src/components/common/ActionMenu.tsx b/services/common/src/components/common/ActionMenu.tsx similarity index 94% rename from services/core-web/src/components/common/ActionMenu.tsx rename to services/common/src/components/common/ActionMenu.tsx index 262134ed71..0a957e0d9e 100644 --- a/services/core-web/src/components/common/ActionMenu.tsx +++ b/services/common/src/components/common/ActionMenu.tsx @@ -1,7 +1,7 @@ import { Button, Dropdown, Modal } from "antd"; import { CaretDownOutlined } from "@ant-design/icons"; import React, { FC } from "react"; -import { ITableAction } from "@/components/common/CoreTableCommonColumns"; +import { ITableAction } from "@mds/common/components/common/CoreTableCommonColumns"; interface ActionMenuProps { record: any; diff --git a/services/core-web/src/components/common/CoreTableCommonColumns.tsx b/services/common/src/components/common/CoreTableCommonColumns.tsx similarity index 94% rename from services/core-web/src/components/common/CoreTableCommonColumns.tsx rename to services/common/src/components/common/CoreTableCommonColumns.tsx index bab7d916b5..720e329168 100644 --- a/services/core-web/src/components/common/CoreTableCommonColumns.tsx +++ b/services/common/src/components/common/CoreTableCommonColumns.tsx @@ -1,6 +1,6 @@ import React, { ReactNode } from "react"; import Highlight from "react-highlighter"; -import { dateSorter, formatDate, nullableStringSorter } from "@common/utils/helpers"; +import { dateSorter, formatDate, nullableStringSorter } from "@mds/common/redux/utils/helpers"; import { EMPTY_FIELD } from "@mds/common/constants/strings"; import { ColumnType } from "antd/lib/table"; import { Button, Dropdown } from "antd"; @@ -50,11 +50,13 @@ export const renderCategoryColumn = ( title: string, categoryMap: any, sortable = false, - placeHolder = EMPTY_FIELD + placeHolder = EMPTY_FIELD, + className?: string ) => { return { title, dataIndex, + className, key: dataIndex, render: (text: string) =>
{categoryMap[text] ?? placeHolder}
, ...(sortable ? { sorter: nullableStringSorter(dataIndex) } : null), diff --git a/services/common/src/components/explosivespermits/ExplosivesPermitDiffModal.tsx b/services/common/src/components/explosivespermits/ExplosivesPermitDiffModal.tsx new file mode 100644 index 0000000000..788292683b --- /dev/null +++ b/services/common/src/components/explosivespermits/ExplosivesPermitDiffModal.tsx @@ -0,0 +1,256 @@ +import { Button, Modal, Table, Typography } from "antd"; +import React, { FC, useEffect, useState } from "react"; +import { isEqual } from "lodash"; +import { IExplosivesPermit } from "@mds/common/interfaces/permits/explosivesPermit.interface"; + +interface ExplosivesPermitDiffModalProps { + explosivesPermit: IExplosivesPermit; + open: boolean; + onCancel: () => void; +} + +interface IPermitDifference { + fieldName: string; + previousValue: any; + currentValue: any; +} + +interface IPermitDifferencesByAmendment { + [amendmentId: string]: IPermitDifference[]; +} + +const ExplosivesPermitDiffModal: FC = ({ + explosivesPermit, + open = false, + onCancel, +}) => { + const [currentDiff, setCurrentDiff] = useState({}); + + const getPermitDifferences = (permit: IExplosivesPermit): IPermitDifferencesByAmendment => { + const comparablePermit = { + explosives_permit_amendment_id: undefined, + ...permit, + }; + + const permitVersions = [comparablePermit, ...permit.explosives_permit_amendments].sort( + (a, b) => a.explosives_permit_amendment_id - b.explosives_permit_amendment_id + ); + + const ignoredFields = [ + "explosives_permit_amendment_id", + "explosives_permit_amendment_guid", + "issuing_inspector_party_guid", + "isAmendment", + "amendment_no", + ]; + + return permitVersions.reduce((acc, currAmendment, i) => { + if (i === 0) { + acc["0"] = []; + return acc; + } + + const previousAmendment = permitVersions[i - 1]; + + Object.entries(currAmendment).forEach(([key, newValue]) => { + const oldValue = previousAmendment[key]; + + if ( + (key === "detonator_magazines" || key === "explosive_magazines") && + Array.isArray(newValue) + ) { + for (const [idx, newVal] of newValue.entries()) { + const oldVal = oldValue[idx]; + + if (!isEqual(newVal, oldVal)) { + if (!acc[currAmendment.explosives_permit_amendment_id]) { + acc[currAmendment.explosives_permit_amendment_id] = []; + } + + for (const [magazineKey, magazineValue] of Object.entries(newVal)) { + const oldMagazineValue = oldVal?.[magazineKey]; + const ignoredMagazineFields = [ + "explosives_permit_amendment_magazine_id", + "explosives_permit_amendment_magazine_type_code", + "explosives_permit_magazine_id", + "explosives_permit_magazine_type_code", + ]; + + if ( + magazineValue !== oldMagazineValue && + !ignoredMagazineFields.includes(magazineKey) + ) { + const fieldPrefix = + key === "detonator_magazines" ? "Detonator Magazine" : "Explosive Magazine"; + const diff: IPermitDifference = { + fieldName: `${fieldPrefix} ${idx} - ${magazineKey}`, + previousValue: oldMagazineValue, + currentValue: magazineValue, + }; + acc[currAmendment.explosives_permit_amendment_id].push(diff); + } + } + } + } + + return; + } + + if (typeof newValue === "object" && newValue !== null) { + return; + } + + if (!acc[currAmendment.explosives_permit_amendment_id]) { + acc[currAmendment.explosives_permit_amendment_id] = []; + } + + if (newValue !== oldValue && !ignoredFields.includes(key)) { + const diff: IPermitDifference = { + fieldName: key, + previousValue: oldValue, + currentValue: newValue, + }; + + acc[currAmendment.explosives_permit_amendment_id].push(diff); + } + }); + + const amendmentDocuments = currAmendment.documents.map((doc) => doc.document_name); + if (amendmentDocuments && amendmentDocuments.length > 0) { + acc[currAmendment.explosives_permit_amendment_id].push({ + fieldName: "Documents", + previousValue: [], + currentValue: amendmentDocuments, + }); + } + return acc; + }, {}); + }; + + useEffect(() => { + if (explosivesPermit) { + const differencesList = getPermitDifferences(explosivesPermit); + setCurrentDiff(differencesList); + } + }, [explosivesPermit]); + + const valueOrNoData = (value: any) => { + if (typeof value === "boolean") { + return value ? "True" : "False"; + } + + return value ? value : "No Data"; + }; + + const columns = [ + { + title: "Notice of Work #", + dataIndex: "now_number", + key: "now_number", + }, + { + title: "Status", + key: "is_closed", + render: (record: any) => { + return record.is_closed ? "Closed" : "Open"; + }, + }, + { + title: "Amendment", + key: "amendment_no", + dataIndex: "amendment_no", + }, + { + title: "Changes", + dataIndex: "differences", + key: "differences", + render: (differences: IPermitDifference[]) => { + return ( +
+ {differences.map((diff) => ( +
+ {diff.fieldName === "Documents" ? ( +
+ + Files Added: + + {diff.currentValue.map((file: any, index) => ( + + {file} + + ))} +
+ ) : ( +
+ + {diff.fieldName}: + + {diff.fieldName !== "None" && ( + + + {valueOrNoData(diff.previousValue)} + + {` => `} + + {valueOrNoData(diff.currentValue)} + + + )} +
+ )} +
+ ))} +
+ ); + }, + }, + ]; + + const data = Object.keys(currentDiff) + .map((key: any, index: number) => { + const amendment = explosivesPermit.explosives_permit_amendments.find( + (amendment) => amendment.explosives_permit_amendment_id == key + ); + + const permit = key === "0" ? explosivesPermit : amendment; + + return { + ...permit, + differences: currentDiff[key].length > 0 ? currentDiff[key] : [{ fieldName: "None" }], + }; + }) + .reverse(); + + return ( + + Close + , + ]} + width={1000} + > + View History + + You are viewing the past history of explosive storage and use permits for this permit ( + Permit # {explosivesPermit.permit_number}) + + + + ); +}; + +export default ExplosivesPermitDiffModal; diff --git a/services/common/src/components/explosivespermits/ExplosivesPermitMap.tsx b/services/common/src/components/explosivespermits/ExplosivesPermitMap.tsx new file mode 100644 index 0000000000..c7ac9b3785 --- /dev/null +++ b/services/common/src/components/explosivespermits/ExplosivesPermitMap.tsx @@ -0,0 +1,124 @@ +import React, { Component, FC, useEffect, useRef, useState } from "react"; +import L, { Map } from "leaflet"; +import LeafletWms from "leaflet.wms"; + +import "leaflet.markercluster"; +import "leaflet/dist/leaflet.css"; +import "leaflet.markercluster/dist/MarkerCluster.css"; +import "leaflet.markercluster/dist/MarkerCluster.Default.css"; + +import * as Strings from "@mds/common/constants/strings"; +import { Validate } from "@mds/common/redux/utils/Validate"; +import { SMALL_PIN_SELECTED } from "@mds/common/constants/assets"; + +/** + * @class ExplosivesPermitMap.js is a Leaflet Map component. + */ + +const leafletWMSTiledOptions = { + transparent: true, + tiled: true, + uppercase: true, + format: "image/png", + identify: false, +}; + +const checkValidityOfCoordinateInput = (coordinates) => + coordinates.length === 2 && + Validate.checkLat(coordinates[0]) && + Validate.checkLon(coordinates[1]); + +const getMajorMinePermittedAreas = () => { + const majorMinesSource = LeafletWms.source( + "https://openmaps.gov.bc.ca/geo/pub/WHSE_MINERAL_TENURE.HSP_MJR_MINES_PERMTTD_AREAS_SP/ows", + { ...leafletWMSTiledOptions, identify: false } + ); + return majorMinesSource.getLayer("pub:WHSE_MINERAL_TENURE.HSP_MJR_MINES_PERMTTD_AREAS_SP"); +}; + +interface ExplosivesPermitMapProps { + pin: [number, number]; +} + +const ExplosivesPermitMap: FC = ({ pin = [] }) => { + const [containsPin, setContainsPin] = useState(false); + const mapRef = useRef(null); + const markerClusterGroupRef = useRef(null); + const pinRef = useRef(null); + + const latLong = checkValidityOfCoordinateInput(pin) + ? // only add mine Pin if location exists + pin + : [Number(Strings.DEFAULT_LAT), Number(Strings.DEFAULT_LONG)]; + + const createPin = () => { + const customIcon = L.icon({ + iconUrl: SMALL_PIN_SELECTED, + iconSize: [60, 60], + }); + pin = L.marker(pin, { icon: customIcon }); + markerClusterGroupRef.current?.addLayer(pin); + mapRef.current?.fitBounds(markerClusterGroupRef.current?.getBounds()); + markerClusterGroupRef.current?.zoomToShowLayer(pin); + setContainsPin(true); + }; + + const createMap = () => { + mapRef.current = L.map("leaflet-map", { attributionControl: false }) + .setView(latLong, Strings.DEFAULT_ZOOM) + .setMaxZoom(12); + const majorMinePermittedAreas = getMajorMinePermittedAreas(); + mapRef.current?.addLayer(majorMinePermittedAreas); + + markerClusterGroupRef.current = L.markerClusterGroup({ + animate: false, + spiderfyOnMaxZoom: true, + showCoverageOnHover: false, + }); + mapRef.current?.addLayer(markerClusterGroupRef.current); + }; + + const getBaseMaps = () => { + const topographicBasemap = L.tileLayer( + "https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}" + ); + // Add default basemap to the map + topographicBasemap.addTo(mapRef.current); + + const worldImageryLayer = L.tileLayer( + "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}" + ); + + return { + Topographic: topographicBasemap, + "World Imagery": worldImageryLayer, + }; + }; + + useEffect(() => { + // Create the base map with layers + createMap(); + L.control.layers(getBaseMaps(), {}, { position: "topright" }).addTo(mapRef?.current); + }, []); + + useEffect(() => { + if (checkValidityOfCoordinateInput(pin)) { + if (containsPin) { + pinRef.current?.setLatLng(pin); + mapRef.current?.fitBounds(markerClusterGroupRef.current?.getBounds()); + } else { + setContainsPin(false); + createPin(); + } + } + }, [pin, containsPin]); + + return ( +
+ ); +}; + +export default ExplosivesPermitMap; diff --git a/services/core-web/src/components/modalContent/ExplosivesPermitViewModal.tsx b/services/common/src/components/explosivespermits/ExplosivesPermitViewModal.tsx similarity index 78% rename from services/core-web/src/components/modalContent/ExplosivesPermitViewModal.tsx rename to services/common/src/components/explosivespermits/ExplosivesPermitViewModal.tsx index 8146bdeacb..e1626c4beb 100644 --- a/services/core-web/src/components/modalContent/ExplosivesPermitViewModal.tsx +++ b/services/common/src/components/explosivespermits/ExplosivesPermitViewModal.tsx @@ -7,14 +7,16 @@ import { IExplosivesPermitAmendment, } from "@mds/common"; import React, { FC, useEffect, useState } from "react"; -import { connect } from "react-redux"; -import ExplosivesPermitMap from "@/components/maps/ExplosivesPermitMap"; -import { formatDate } from "@common/utils/helpers"; -import Magazine from "@/components/mine/ExplosivesPermit/Magazine"; +import { useParams } from "react-router-dom"; + import { bindActionCreators } from "redux"; -import { openDocument } from "@/components/syncfusion/DocumentViewer"; -import { downloadFileFromDocumentManager } from "@common/utils/actionlessNetworkCalls"; -import ExplosivesPermitDiffModal from "@common/components/explosivesPermits/ExplosivesPermitDiffModal"; + +import ExplosivesPermitDiffModal from "@mds/common/components/explosivespermits/ExplosivesPermitDiffModal"; +import { downloadFileFromDocumentManager } from "@mds/common/redux/utils/actionlessNetworkCalls"; +import { formatDate } from "@mds/common/redux/utils/helpers"; +import ExplosivesPermitMap from "@mds/common/components/explosivespermits/ExplosivesPermitMap"; +import Magazine from "@mds/common/components/explosivespermits/Magazine"; +import { openDocument } from "@mds/common/components/syncfusion/DocumentViewer"; import { renderCategoryColumn, renderTextColumn } from "../common/CoreTableCommonColumns"; export const generatedDocColumns = [ @@ -23,13 +25,21 @@ export const generatedDocColumns = [ "Category", ESUP_DOCUMENT_GENERATED_TYPES, true, - "" + "", + "break-word" ), { title: "File Name", dataIndex: "document_name", key: "document_name", - render: (text, record) => downloadFileFromDocumentManager(record)}>{text}, + render: (text, record) => ( + downloadFileFromDocumentManager(record)} + > + {text} + + ), }, { title: "Created", @@ -44,7 +54,14 @@ export const supportingDocColumns = [ title: "File Name", dataIndex: "document_name", key: "document_name", - render: (text, record) => downloadFileFromDocumentManager(record)}>{text}, + render: (text, record) => ( + downloadFileFromDocumentManager(record)} + > + {text} + + ), }, { title: "Created By", @@ -65,7 +82,6 @@ interface ExplosivesPermitViewModalProps { parentPermit: IExplosivesPermit; closeModal: () => void; openAmendModal?: (event, record: IExplosivesPermit) => void; - openDocument: (document_manager_guid: string, mine_document_guid: string) => void; handleOpenExplosivesPermitCloseModal: (event, record: IExplosivesPermit) => void; } @@ -78,6 +94,11 @@ const permitAmendmentLike = (permit: IExplosivesPermit): IExplosivesPermitAmendm export const ExplosivesPermitViewModal: FC = (props) => { const { explosivesPermit, parentPermit } = props; const amendmentsCount = parentPermit?.amendment_count || 0; + const { explosivesPermitGuid } = useParams<{ + explosivesPermitGuid: string; + }>(); + + const isCore = !explosivesPermitGuid; const [generatedDocs, setGeneratedDocs] = useState([]); const [supportingDocs, setSupportingDocs] = useState([]); @@ -139,12 +160,12 @@ export const ExplosivesPermitViewModal: FC = (pr useEffect(() => { if (currentPermit) { const generatedTypes = Object.keys(ESUP_DOCUMENT_GENERATED_TYPES); - const allDocs = [ - ...parentPermit?.documents, - ...parentPermit?.explosives_permit_amendments - .map((amendment) => amendment.documents) - .flat(), - ]; + const permitDocuments = parentPermit?.documents ?? []; + const amendmentDocuments = + parentPermit?.explosives_permit_amendments?.map((amendment) => amendment.documents) ?? []; + // Wrapping in another array to flatten only if it's not undefined + const allDocs = [...permitDocuments, ...[].concat(...amendmentDocuments)]; + setGeneratedDocs( allDocs.filter((doc) => generatedTypes.includes(doc.explosives_permit_document_type_code)) ); @@ -178,12 +199,14 @@ export const ExplosivesPermitViewModal: FC = (pr
)} - - Explosive Storage and Use Permit - + {isCore && ( + + Explosive Storage and Use Permit + + )}
- + Explosives Permit Details <> @@ -234,7 +257,7 @@ export const ExplosivesPermitViewModal: FC = (pr {currentPermit.application_date} Other Information {currentPermit.description} - + Storage Details @@ -251,7 +274,7 @@ export const ExplosivesPermitViewModal: FC = (pr
{(supportingDocs.length > 0 || generatedDocs.length > 0) && ( - + Supporting Documents {generatedDocs.length > 0 && ( @@ -267,6 +290,7 @@ export const ExplosivesPermitViewModal: FC = (pr
= (pr <> - + Permit Status @@ -322,23 +346,25 @@ export const ExplosivesPermitViewModal: FC = (pr {currentPermit.is_closed ? "Closed" : "Open"} - - {currentPermit.is_closed ? ( - - {formatDate(currentPermit.closed_timestamp)} - - ) : ( - - )} - + {isCore && ( + + {currentPermit.is_closed ? ( + + {formatDate(currentPermit.closed_timestamp)} + + ) : ( + + )} + + )} {currentPermit.is_closed && ( @@ -350,7 +376,7 @@ export const ExplosivesPermitViewModal: FC = (pr )} - + Explosives Magazines {currentPermit?.explosive_magazines?.length > 0 && @@ -361,7 +387,7 @@ export const ExplosivesPermitViewModal: FC = (pr magazine={magazine} /> ))} - + Detonator Magazines {currentPermit?.detonator_magazines?.length > 0 && @@ -397,25 +423,29 @@ export const ExplosivesPermitViewModal: FC = (pr - - {explosivesPermit.application_status === "APP" && ( - - )} - - + {isCore && ( +
+ + {explosivesPermit.application_status === "APP" && ( + + )} + + +
+ )} setOpenDiffModal(false)} @@ -425,12 +455,4 @@ export const ExplosivesPermitViewModal: FC = (pr ); }; -const mapDispatchToProps = (dispatch) => - bindActionCreators( - { - openDocument, - }, - dispatch - ); - -export default connect(null, mapDispatchToProps)(ExplosivesPermitViewModal); +export default ExplosivesPermitViewModal; diff --git a/services/core-web/src/components/mine/ExplosivesPermit/Magazine.tsx b/services/common/src/components/explosivespermits/Magazine.tsx similarity index 85% rename from services/core-web/src/components/mine/ExplosivesPermit/Magazine.tsx rename to services/common/src/components/explosivespermits/Magazine.tsx index 0529efa086..35e6e6506b 100644 --- a/services/core-web/src/components/mine/ExplosivesPermit/Magazine.tsx +++ b/services/common/src/components/explosivespermits/Magazine.tsx @@ -1,6 +1,8 @@ import React, { FC } from "react"; import { Col, Collapse, Row, Typography } from "antd"; import { IExplosivesPermitMagazine } from "@mds/common/interfaces/permits/explosivesPermit.interface"; +import { useParams } from "react-router-dom"; +import PlusOutlined from "@ant-design/icons/PlusOutlined"; interface IMagazineProps { label: string; @@ -9,12 +11,22 @@ interface IMagazineProps { const Magazine: FC = (props) => { const { label, magazine } = props; + const { explosivesPermitGuid } = useParams<{ + explosivesPermitGuid: string; + }>(); + const isCore = !explosivesPermitGuid; + return ( - + + } + className="magazine-collapse margin-large--bottom" + > + {label} } diff --git a/services/common/src/components/syncfusion/AmazonS3Provider.js b/services/common/src/components/syncfusion/AmazonS3Provider.js new file mode 100644 index 0000000000..b27dc92fdc --- /dev/null +++ b/services/common/src/components/syncfusion/AmazonS3Provider.js @@ -0,0 +1,205 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { bindActionCreators } from "redux"; +import { connect } from "react-redux"; +import { SampleBase } from "@/components/syncfusion/SampleBase"; +import { ENVIRONMENT } from "@mds/common"; +import { + FileManagerComponent, + Inject, + NavigationPane, + DetailsView, + Toolbar, + ContextMenu, +} from "@syncfusion/ej2-react-filemanager"; +import { createRequestHeader } from "@common/utils/RequestHeaders"; +import { openDocumentViewer } from "@mds/common/redux/actions/documentViewerActions"; +import { isDocumentOpenable } from "@/components/syncfusion/DocumentViewer"; +import keycloak from "@/keycloak"; + +const propTypes = { + path: PropTypes.string.isRequired, +}; + +export class AmazonS3Provider extends SampleBase { + constructor() { + super(...arguments); + this.hostUrl = ENVIRONMENT.filesystemProviderUrl; + this.pathPrefix = `mms-archive/${this.props.mineNumber}`; + } + + toolbarClick = (args) => { + // Prevent default download using toolbar + if (args.item.id === `${this.filemanager.element.id}_tb_download`) { + args.cancel = true; + this.customDownload([]); + } + }; + + menuClick = (args) => { + // Prevent default download using context menu + if (args.item.id === `${this.filemanager.element.id}_cm_download`) { + args.cancel = true; + this.customDownload(args.fileDetails); + } + }; + + fileOpen = (args) => { + if (args.fileDetails.isFile && args.fileDetails._fm_iconClass !== "e-fe-image") { + this.customDownload([]); + } + }; + + // Workaround method for providing authorization headers in download request: https://www.syncfusion.com/forums/144270/how-to-implement-jwt-token-send-with-every-filemanager-request + customDownload(files) { + const flag = this.filemanager.selectedItems.length !== 0; + if (files.length !== 0 || flag) { + // Create data for the controller + const data = { + action: "download", + path: `${this.pathPrefix}${this.filemanager.path}`, + names: flag ? this.filemanager.selectedItems : [""], + data: files.length === 0 ? this.filemanager.getSelectedFiles() : files, + }; + + // If the user has selected a PDF, display it in the Document Viewer instead of downloading. + if ( + this.filemanager.selectedItems.length === 1 && + isDocumentOpenable(this.filemanager.selectedItems[0]) + ) { + const documentName = this.filemanager.selectedItems[0]; + const documentPath = data.path + documentName; + this.props.openDocumentViewer({ + documentPath, + props: { title: documentName }, + }); + return; + } + + // Initiate an XHR request + const xhr = new XMLHttpRequest(); + xhr.open("POST", this.filemanager.ajaxSettings.downloadUrl, true); + xhr.responseType = "blob"; + xhr.onload = function () { + if (this.status === 200) { + let name = ""; + + // Get the file name from content-disposition header + const header = xhr.getResponseHeader("Content-Disposition"); + if (header && header.indexOf("attachment") !== -1) { + const regex = /name[^;=\n]*=((['"]).*?\2|[^;\n]*)/; + const matches = regex.exec(header); + if (matches != null && matches[1]) { + name = matches[1].replace(/['"]/g, ""); + } + } + + // Save the file locally using anchor tag + const blob = new Blob([this.response], { type: xhr.getResponseHeader("Content-Type") }); + const anchorUrl = window.URL.createObjectURL(blob); + if (name) { + const anchor = document.createElement("a"); + anchor.href = anchorUrl; + anchor.download = name; + anchor.click(); + } else { + window.location = anchorUrl; + } + setTimeout(function () { + URL.revokeObjectURL(anchorUrl); + }, 100); + } + }; + + const fdata = new FormData(); + fdata.append("downloadInput", JSON.stringify(data)); + xhr.setRequestHeader("Authorization", createRequestHeader().headers.Authorization); + xhr.setRequestHeader("Access-Control-Allow-Origin", "*"); + xhr.send(fdata); + } + } + + render() { + return ( +
+
+ {/* NOTE: See here for documentation: https://ej2.syncfusion.com/react/documentation/api/file-manager/ */} + { + this.filemanager = node; + }} + ajaxSettings={{ + url: `${this.hostUrl}AmazonS3FileOperations`, + getImageUrl: `${this.hostUrl}AmazonS3GetImage`, + downloadUrl: `${this.hostUrl}AmazonS3Download`, + }} + allowDragAndDrop={false} + allowMultiSelection + enablePersistence={false} + showFileExtension + showHiddenItems={false} + showThumbnail + rootAliasName="Files" + view="Details" + searchSettings={{ + allowSearchOnTyping: false, + filterType: "contains", + ignoreCase: true, + }} + toolbarSettings={{ + items: ["Download", "SortBy", "Refresh", "Selection", "View", "Details"], + visible: true, + }} + contextMenuSettings={{ + file: ["Open", "|", "Details"], + folder: ["Open", "|", "Details"], + layout: ["SortBy", "View", "Refresh", "|", "Details", "|", "SelectAll"], + visible: true, + }} + beforeSend={(args) => { + const data = JSON.parse(args.ajaxSettings.data); + data.path = this.pathPrefix + data.path; + args.ajaxSettings.data = JSON.stringify(data); + args.ajaxSettings.beforeSend = function (args) { + args.httpRequest.setRequestHeader( + "Authorization", + createRequestHeader().headers.Authorization + ); + args.httpRequest.setRequestHeader("Access-Control-Allow-Origin", "*"); + }; + }} + beforeDownload={(args) => { + args.data.path = this.pathPrefix + args.data.path; + }} + beforeImageLoad={(args) => { + args.imageUrl = args.imageUrl.replace( + "/AmazonS3GetImage?path=", + `/AmazonS3GetImage?path=${this.pathPrefix}` + ); + + args.imageUrl += `&token=${keycloak.token}`; + }} + menuClick={this.menuClick} + toolbarClick={this.toolbarClick} + fileOpen={this.fileOpen} + > + + +
+
+ ); + } +} + +AmazonS3Provider.propTypes = propTypes; + +const mapDispatchToProps = (dispatch) => + bindActionCreators( + { + openDocumentViewer, + }, + dispatch + ); + +export default connect(null, mapDispatchToProps)(AmazonS3Provider); diff --git a/services/common/src/components/syncfusion/DocumentViewer.js b/services/common/src/components/syncfusion/DocumentViewer.js new file mode 100644 index 0000000000..6cc3ab793a --- /dev/null +++ b/services/common/src/components/syncfusion/DocumentViewer.js @@ -0,0 +1,187 @@ +import React, { Component } from "react"; +import { bindActionCreators } from "redux"; +import { connect } from "react-redux"; +import PropTypes from "prop-types"; +import { ENVIRONMENT } from "@mds/common"; +import { + PdfViewerComponent, + Toolbar, + Magnification, + Navigation, + LinkAnnotation, + BookmarkView, + ThumbnailView, + Print, + TextSelection, + Annotation, + TextSearch, + FormFields, + FormDesigner, + Inject, +} from "@syncfusion/ej2-react-pdfviewer"; +import { Modal } from "antd"; +import { + closeDocumentViewer, + openDocumentViewer, +} from "@mds/common/redux/actions/documentViewerActions"; +import { + getDocumentPath, + getDocumentName, + getIsDocumentViewerOpen, + getProps, +} from "@mds/common/redux/selectors/documentViewerSelectors"; +import { + downloadFileFromDocumentManager, + getDocument, +} from "@mds/common/redux/utils/actionlessNetworkCalls"; + +// eslint-disable-next-line no-undef +const createRequestHeader = REQUEST_HEADER.createRequestHeader; + +const propTypes = { + closeDocumentViewer: PropTypes.func.isRequired, + isDocumentViewerOpen: PropTypes.bool.isRequired, + documentPath: PropTypes.string.isRequired, + props: PropTypes.objectOf(PropTypes.any).isRequired, +}; + +const getAjaxRequestSettingsHeaders = (obj) => { + const ajaxRequestSettingsHeaders = []; + for (const key in obj) { + ajaxRequestSettingsHeaders.push({ headerName: key, headerValue: obj[key] }); + } + return ajaxRequestSettingsHeaders; +}; + +/** + * All file types that can currently be opened by the Document Viewer. + */ +export const OPENABLE_DOCUMENT_TYPES = ["PDF"]; + +/** + * Whether or not the document can be opened by the Document Viewer (determined by the file extension in the document name). + */ +export const isDocumentOpenable = (documentName) => + OPENABLE_DOCUMENT_TYPES.some((type) => documentName.toUpperCase().includes(`.${type}`)); + +/** + * If possible, open the document in the Document Viewer, otherwise, download the document. + */ +export const openDocument = (documentManagerGuid, documentName) => async (dispatch) => { + const document = { + document_manager_guid: documentManagerGuid, + document_name: documentName, + }; + + if (!isDocumentOpenable(documentName)) { + return downloadFileFromDocumentManager(document); + } + + const documentRecord = await getDocument(documentManagerGuid); + const documentPath = documentRecord.object_store_path; + if (!documentPath) { + return downloadFileFromDocumentManager(document); + } + + return dispatch( + openDocumentViewer({ + documentPath, + documentName, + props: { title: documentName }, + }) + ); +}; + +/** + * The Document Viewer allows documents to be opened and viewed within the application. + */ +export class DocumentViewer extends Component { + constructor(props) { + super(props); + this.containerRef = React.createRef(); + this.pdfViewerServiceUrl = ENVIRONMENT.filesystemProviderUrl.replace( + "AmazonS3Provider/", + "PdfViewer" + ); + } + + handleOk = () => this.props.closeDocumentViewer(); + + handleCancel = () => this.props.closeDocumentViewer(); + + render() { + const ajaxRequestSettings = { + ajaxHeaders: getAjaxRequestSettingsHeaders(createRequestHeader().headers), + withCredentials: false, + }; + + return ( + <> + {/* + Create a container for the pdf viewer modal to be put in. + Without it, syncfusion will crash :dragons: + */} +
+ + this.containerRef?.current} + footer={null} + width="98%" + destroyOnClose={true} + > + {/* // NOTE: See here for documentation: + https://ej2.syncfusion.com/react/documentation/pdfviewer/getting-started/ */} + + {/* NOTE: Some toolbar items are hidden using CSS. */} + + + + + ); + } +} + +DocumentViewer.propTypes = propTypes; + +const mapStateToProps = (state) => ({ + documentPath: getDocumentPath(state), + documentName: getDocumentName(state), + isDocumentViewerOpen: getIsDocumentViewerOpen(state), + props: getProps(state), +}); + +const mapDispatchToProps = (dispatch) => + bindActionCreators( + { + closeDocumentViewer, + }, + dispatch + ); + +export default connect(mapStateToProps, mapDispatchToProps)(DocumentViewer); diff --git a/services/common/src/components/syncfusion/SampleBase.js b/services/common/src/components/syncfusion/SampleBase.js new file mode 100644 index 0000000000..35469b7a26 --- /dev/null +++ b/services/common/src/components/syncfusion/SampleBase.js @@ -0,0 +1,16 @@ +import { PureComponent } from "react"; +import { enableRipple } from "@syncfusion/ej2-base"; + +enableRipple(true); + +export class SampleBase extends PureComponent { + renderComplete() {} + + componentDidMount() { + setTimeout(() => { + this.renderComplete(); + }); + } +} + +export default SampleBase; diff --git a/services/common/src/constants/actionTypes.js b/services/common/src/constants/actionTypes.js index eb3ad9e731..0f7ac34cd4 100755 --- a/services/common/src/constants/actionTypes.js +++ b/services/common/src/constants/actionTypes.js @@ -18,6 +18,7 @@ export const UPDATE_DOCUMENT_VIEWER_TITLE = "UPDATE_DOCUMENT_VIEWER_TITLE"; export const AUTHENTICATE_USER = "AUTHENTICATE_USER"; export const STORE_USER_ACCESS_DATA = "STORE_USER_ACCESS_DATA"; export const LOGOUT = "LOGOUT"; +export const STORE_IS_PROPONENT = "STORE_IS_PROPONENT"; export const STORE_CURRENT_USER_MINE_VERIFIED_STATUS = "STORE_CURRENT_USER_MINE_VERIFIED_STATUS"; export const STORE_MINE_LIST = "STORE_MINE_LIST"; diff --git a/services/common/src/constants/assets.ts b/services/common/src/constants/assets.ts new file mode 100644 index 0000000000..54544251ec --- /dev/null +++ b/services/common/src/constants/assets.ts @@ -0,0 +1 @@ +export { default as SMALL_PIN_SELECTED } from "../assets/images/small-pin-selected.svg"; diff --git a/services/common/src/redux/reducers/authenticationReducer.ts b/services/common/src/redux/reducers/authenticationReducer.ts index 168a5777a3..ed6c2dc245 100644 --- a/services/common/src/redux/reducers/authenticationReducer.ts +++ b/services/common/src/redux/reducers/authenticationReducer.ts @@ -3,11 +3,14 @@ import { AUTHENTICATION } from "@mds/common/constants/reducerTypes"; import { IUserInfo } from "@mds/common/interfaces"; import { USER_ROLES } from "@mds/common/constants"; import { RootState } from "@mds/common/redux/rootState"; +import * as ReducerTypes from "@mds/minespace-web/src/constants/reducerTypes"; interface IAuthenticationReducerState { isAuthenticated: boolean; - userAccessData: string[]; userInfo: IUserInfo; + redirect: boolean; + userAccessData: string[]; + isProponent: boolean; } /** @@ -18,6 +21,8 @@ const initialState: IAuthenticationReducerState = { isAuthenticated: false, userAccessData: [], userInfo: {} as IUserInfo, + redirect: false, + isProponent: undefined, }; const getUserName = (tokenParsed) => { @@ -43,17 +48,24 @@ export const authenticationReducer = (state = initialState, action) => { ...action.payload.userInfo, preferred_username, }, + redirect: GLOBAL_ROUTES?.MINES?.route, }; case ActionTypes.STORE_USER_ACCESS_DATA: return { ...state, userAccessData: action.payload.roles, }; + case ActionTypes.STORE_IS_PROPONENT: + return { + ...state, + isProponent: action.payload.data, + }; case ActionTypes.LOGOUT: return { ...state, isAuthenticated: false, userInfo: {}, + redirect: GLOBAL_ROUTES?.HOME?.route, }; default: return state; @@ -69,5 +81,7 @@ export const getUserAccessData = (state: RootState) => state[AUTHENTICATION].use export const getUserInfo = (state: RootState) => state[AUTHENTICATION].userInfo; export const userHasRole = (state: RootState, role: string) => state[AUTHENTICATION].userAccessData.includes(USER_ROLES[role]); +export const getRedirect = (state) => state[ReducerTypes.AUTHENTICATION].redirect; +export const isProponent = (state) => state[ReducerTypes.AUTHENTICATION].isProponent; export default authenticationReducerObject; diff --git a/services/common/src/redux/selectors/authenticationSelectors.js b/services/common/src/redux/selectors/authenticationSelectors.js index cf16035528..99391c633c 100644 --- a/services/common/src/redux/selectors/authenticationSelectors.js +++ b/services/common/src/redux/selectors/authenticationSelectors.js @@ -4,4 +4,6 @@ export const { isAuthenticated, getUserAccessData, getUserInfo, + getRedirect, + isProponent, } = authenticationReducer; diff --git a/services/core-web/common/components/explosivesPermits/ExplosivesPermitDiffModal.tsx b/services/core-web/common/components/explosivesPermits/ExplosivesPermitDiffModal.tsx deleted file mode 100644 index 925719bec4..0000000000 --- a/services/core-web/common/components/explosivesPermits/ExplosivesPermitDiffModal.tsx +++ /dev/null @@ -1,252 +0,0 @@ -import { Button, Modal, Table, Typography } from "antd"; -import React, { FC, useEffect, useState } from "react"; -import { isEqual } from "lodash"; -import { IExplosivesPermit } from "@mds/common/interfaces/permits/explosivesPermit.interface"; - -interface ExplosivesPermitDiffModalProps { - explosivesPermit: IExplosivesPermit; - open: boolean; - onCancel: () => void; -} - -interface IPermitDifference { - fieldName: string; - previousValue: any; - currentValue: any; -} - -interface IPermitDifferencesByAmendment { - [amendmentId: string]: IPermitDifference[]; -} - -const ExplosivesPermitDiffModal: FC = ({ - explosivesPermit, - open = false, - onCancel, -}) => { - const [differences, setDifferences] = useState({}); - - const getPermitDifferences = (permit: IExplosivesPermit): IPermitDifferencesByAmendment => { - const comparablePermit = { - explosives_permit_amendment_id: undefined, - ...permit, - }; - - const permitVersions = [comparablePermit, ...permit.explosives_permit_amendments].sort( - (a, b) => a.explosives_permit_amendment_id - b.explosives_permit_amendment_id - ); - - const ignoredFields = [ - "explosives_permit_amendment_id", - "explosives_permit_amendment_guid", - "issuing_inspector_party_guid", - "isAmendment", - "amendment_no", - ]; - - const differences: IPermitDifferencesByAmendment = permitVersions.reduce( - (acc, currAmendment, i) => { - if (i === 0) { - acc["0"] = []; - return acc; - } - - const previousAmendment = permitVersions[i - 1]; - - Object.entries(currAmendment).forEach(([key, newValue]) => { - const oldValue = previousAmendment[key]; - - if ( - (key === "detonator_magazines" || key === "explosive_magazines") && - Array.isArray(newValue) - ) { - for (const [idx, newVal] of newValue.entries()) { - const oldVal = oldValue[idx]; - - if (!isEqual(newVal, oldVal)) { - if (!acc[currAmendment.explosives_permit_amendment_id]) { - acc[currAmendment.explosives_permit_amendment_id] = []; - } - - for (const [magazineKey, magazineValue] of Object.entries(newVal)) { - const oldMagazineValue = oldVal?.[magazineKey]; - const ignoredMagazineFields = [ - "explosives_permit_amendment_magazine_id", - "explosives_permit_amendment_magazine_type_code", - "explosives_permit_magazine_id", - "explosives_permit_magazine_type_code", - ]; - - if ( - magazineValue !== oldMagazineValue && - !ignoredMagazineFields.includes(magazineKey) - ) { - const fieldPrefix = - key === "detonator_magazines" ? "Detonator Magazine" : "Explosive Magazine"; - const diff: IPermitDifference = { - fieldName: `${fieldPrefix} ${idx} - ${magazineKey}`, - previousValue: oldMagazineValue, - currentValue: magazineValue, - }; - acc[currAmendment.explosives_permit_amendment_id].push(diff); - } - } - } - } - - return; - } - - if (typeof newValue === "object" && newValue !== null) { - return; - } - - if (!acc[currAmendment.explosives_permit_amendment_id]) { - acc[currAmendment.explosives_permit_amendment_id] = []; - } - - if (newValue !== oldValue && !ignoredFields.includes(key)) { - const diff: IPermitDifference = { - fieldName: key, - previousValue: oldValue, - currentValue: newValue, - }; - - acc[currAmendment.explosives_permit_amendment_id].push(diff); - } - }); - - const amendmentDocuments = currAmendment.documents.map((doc) => doc.document_name); - if (amendmentDocuments && amendmentDocuments.length > 0) { - acc[currAmendment.explosives_permit_amendment_id].push({ - fieldName: "Documents", - previousValue: [], - currentValue: amendmentDocuments, - }); - } - return acc; - }, - {} - ); - - return differences; - }; - - useEffect(() => { - if (explosivesPermit) { - const differencesList = getPermitDifferences(explosivesPermit); - setDifferences(differencesList); - } - }, [explosivesPermit]); - - const valueOrNoData = (value: any) => { - if (typeof value === "boolean") { - return value ? "True" : "False"; - } - - return value ? value : "No Data"; - }; - - const columns = [ - { - title: "Notice of Work #", - dataIndex: "now_number", - key: "now_number", - }, - { - title: "Status", - key: "is_closed", - render: (record: any) => { - return record.is_closed ? "Closed" : "Open"; - }, - }, - { - title: "Amendment", - key: "order_no", - dataIndex: "order_no", - }, - { - title: "Changes", - dataIndex: "differences", - key: "differences", - render: (differences: IPermitDifference[]) => - differences.map((diff) => ( -
- {diff.fieldName === "Documents" ? ( -
- - Files Added: - - {diff.currentValue.map((file: any, index) => ( - - {file} - - ))} -
- ) : ( -
- - {diff.fieldName} - - {diff.fieldName !== "None" && ( - - - {valueOrNoData(diff.previousValue)} - - {` => `} - - {valueOrNoData(diff.currentValue)} - - - )} -
- )} -
- )), - }, - ]; - - const data = Object.keys(differences) - .map((key: any, index: number) => { - const amendment = explosivesPermit.explosives_permit_amendments.find( - (amendment) => amendment.explosives_permit_amendment_id == key - ); - - const permit = key === "0" ? explosivesPermit : amendment; - - return { - ...permit, - differences: differences[key].length > 0 ? differences[key] : [{ fieldName: "None" }], - order_no: index, - }; - }) - .reverse(); - - return ( - - Close - , - ]} - width={1000} - > - View History - - You are viewing the past history of explosive storage and use permits for this permit ( - Permit # {explosivesPermit.permit_number}) - -
- - ); -}; - -export default ExplosivesPermitDiffModal; diff --git a/services/core-web/src/components/Forms/ExplosivesPermit/ExplosivesPermitForm.tsx b/services/core-web/src/components/Forms/ExplosivesPermit/ExplosivesPermitForm.tsx index 525221e683..21d525c47a 100644 --- a/services/core-web/src/components/Forms/ExplosivesPermit/ExplosivesPermitForm.tsx +++ b/services/core-web/src/components/Forms/ExplosivesPermit/ExplosivesPermitForm.tsx @@ -40,12 +40,12 @@ import { import { getPermits } from "@mds/common/redux/selectors/permitSelectors"; import { renderConfig } from "@/components/common/config"; import * as FORM from "@/constants/forms"; -import ExplosivesPermitMap from "@/components/maps/ExplosivesPermitMap"; import DocumentCategoryForm from "@/components/Forms/DocumentCategoryForm"; import MagazineForm from "@/components/Forms/ExplosivesPermit/MagazineForm"; import { Feature } from "@mds/common"; import { useFeatureFlag } from "@mds/common/providers/featureFlags/useFeatureFlag"; +import ExplosivesPermitMap from "@mds/common/components/explosivespermits/ExplosivesPermitMap"; interface StateProps { permits: IPermit[]; diff --git a/services/core-web/src/components/Forms/ExplosivesPermit/ExplosivesPermitFormNew.tsx b/services/core-web/src/components/Forms/ExplosivesPermit/ExplosivesPermitFormNew.tsx index 93f95eec16..aede255a26 100644 --- a/services/core-web/src/components/Forms/ExplosivesPermit/ExplosivesPermitFormNew.tsx +++ b/services/core-web/src/components/Forms/ExplosivesPermit/ExplosivesPermitFormNew.tsx @@ -40,13 +40,13 @@ import { import { getPermits } from "@mds/common/redux/selectors/permitSelectors"; import { renderConfig } from "@/components/common/config"; import * as FORM from "@/constants/forms"; -import ExplosivesPermitMap from "@/components/maps/ExplosivesPermitMap"; import DocumentCategoryForm from "@/components/Forms/DocumentCategoryForm"; import MagazineFormNew from "@/components/Forms/ExplosivesPermit/MagazineFormNew"; import { generatedDocColumns, supportingDocColumns, -} from "@/components/modalContent/ExplosivesPermitViewModal"; +} from "@mds/common/components/explosivespermits/ExplosivesPermitViewModal"; +import ExplosivesPermitMap from "@mds/common/components/explosivespermits/ExplosivesPermitMap"; export enum EsupFormMode { select_type_modal, diff --git a/services/core-web/src/components/Forms/Securities/BondForm.js b/services/core-web/src/components/Forms/Securities/BondForm.js index 6385caa59a..cbe4e5545a 100644 --- a/services/core-web/src/components/Forms/Securities/BondForm.js +++ b/services/core-web/src/components/Forms/Securities/BondForm.js @@ -34,7 +34,7 @@ import { removeFunctionColumn, uploadDateColumn, } from "@/components/common/DocumentColumns"; -import { renderTextColumn } from "@/components/common/CoreTableCommonColumns"; +import { renderTextColumn } from "@mds/common/components/common/CoreTableCommonColumns"; const propTypes = { onSubmit: PropTypes.func.isRequired, diff --git a/services/core-web/src/components/Forms/Securities/ReclamationInvoiceForm.js b/services/core-web/src/components/Forms/Securities/ReclamationInvoiceForm.js index bebef058c1..dba38b8cf4 100644 --- a/services/core-web/src/components/Forms/Securities/ReclamationInvoiceForm.js +++ b/services/core-web/src/components/Forms/Securities/ReclamationInvoiceForm.js @@ -20,7 +20,7 @@ import { removeFunctionColumn, uploadDateColumn, } from "@/components/common/DocumentColumns"; -import { renderTextColumn } from "@/components/common/CoreTableCommonColumns"; +import { renderTextColumn } from "@mds/common/components/common/CoreTableCommonColumns"; const propTypes = { onSubmit: PropTypes.func.isRequired, diff --git a/services/core-web/src/components/admin/AdminVerifiedMinesList.js b/services/core-web/src/components/admin/AdminVerifiedMinesList.js index a03e3e7e28..69f395ec63 100644 --- a/services/core-web/src/components/admin/AdminVerifiedMinesList.js +++ b/services/core-web/src/components/admin/AdminVerifiedMinesList.js @@ -10,7 +10,10 @@ import * as router from "@/constants/routes"; import { fetchMineVerifiedStatuses } from "@mds/common/redux/actionCreators/mineActionCreator"; import { AuthorizationGuard } from "@/HOC/AuthorizationGuard"; import * as Permission from "@/constants/permissions"; -import { renderDateColumn, renderTextColumn } from "../common/CoreTableCommonColumns"; +import { + renderDateColumn, + renderTextColumn, +} from "@mds/common/components/common/CoreTableCommonColumns"; /** * @class AdminVerifiedMinesList displays list of mineVerifiedStatuses for the admin page. diff --git a/services/core-web/src/components/admin/MinespaceUserList.js b/services/core-web/src/components/admin/MinespaceUserList.js index b3d33549f9..a520c6da8e 100644 --- a/services/core-web/src/components/admin/MinespaceUserList.js +++ b/services/core-web/src/components/admin/MinespaceUserList.js @@ -5,7 +5,7 @@ import * as Strings from "@mds/common/constants/strings"; import { TRASHCAN, EDIT_OUTLINE_VIOLET } from "@/constants/assets"; import CustomPropTypes from "@/customPropTypes"; import CoreTable from "../common/CoreTable"; -import { renderTextColumn } from "../common/CoreTableCommonColumns"; +import { renderTextColumn } from "@mds/common/components/common/CoreTableCommonColumns"; const propTypes = { minespaceUsers: PropTypes.arrayOf(CustomPropTypes.minespaceUser), diff --git a/services/core-web/src/components/admin/contacts/EMLIContacts/EMLIContactsTable.js b/services/core-web/src/components/admin/contacts/EMLIContacts/EMLIContactsTable.js index e7ce84c45d..bab8084f4d 100644 --- a/services/core-web/src/components/admin/contacts/EMLIContacts/EMLIContactsTable.js +++ b/services/core-web/src/components/admin/contacts/EMLIContacts/EMLIContactsTable.js @@ -8,7 +8,7 @@ import CoreTable from "@/components/common/CoreTable"; import AuthorizationWrapper from "@/components/common/wrappers/AuthorizationWrapper"; import * as Permission from "@/constants/permissions"; import { CoreTooltip } from "@/components/common/CoreTooltip"; -import { renderTextColumn } from "@/components/common/CoreTableCommonColumns"; +import { renderTextColumn } from "@mds/common/components/common/CoreTableCommonColumns"; const propTypes = { isLoaded: PropTypes.bool.isRequired, diff --git a/services/core-web/src/components/common/DocumentColumns.tsx b/services/core-web/src/components/common/DocumentColumns.tsx index bcf96011fe..d5dd94dbbe 100644 --- a/services/core-web/src/components/common/DocumentColumns.tsx +++ b/services/core-web/src/components/common/DocumentColumns.tsx @@ -3,7 +3,10 @@ import * as Strings from "@mds/common/constants/strings"; import { Button, Popconfirm, Tag, Tooltip } from "antd"; import { ColumnType } from "antd/lib/table"; import { TRASHCAN } from "@/constants/assets"; -import { renderDateColumn, renderTextColumn } from "./CoreTableCommonColumns"; +import { + renderDateColumn, + renderTextColumn, +} from "@mds/common/components/common/CoreTableCommonColumns"; import { nullableStringSorter } from "@common/utils/helpers"; import { ClockCircleOutlined } from "@ant-design/icons"; import DocumentLink from "./DocumentLink"; diff --git a/services/core-web/src/components/common/DocumentTable.tsx b/services/core-web/src/components/common/DocumentTable.tsx index 09afe20e1b..3a9bfe567c 100644 --- a/services/core-web/src/components/common/DocumentTable.tsx +++ b/services/core-web/src/components/common/DocumentTable.tsx @@ -6,7 +6,11 @@ import { uploadDateColumn, uploadedByColumn, } from "./DocumentColumns"; -import { renderTextColumn, renderActionsColumn, ITableAction } from "./CoreTableCommonColumns"; +import { + renderTextColumn, + renderActionsColumn, + ITableAction, +} from "@mds/common/components/common/CoreTableCommonColumns"; import { some } from "lodash"; import { closeModal, openModal } from "@mds/common/redux/actions/modalActions"; import DocumentCompression from "./DocumentCompression"; diff --git a/services/core-web/src/components/dashboard/contactsHomePage/ContactList.js b/services/core-web/src/components/dashboard/contactsHomePage/ContactList.js index 05b72437bc..bc010830ba 100644 --- a/services/core-web/src/components/dashboard/contactsHomePage/ContactList.js +++ b/services/core-web/src/components/dashboard/contactsHomePage/ContactList.js @@ -8,7 +8,7 @@ import * as router from "@/constants/routes"; import CustomPropTypes from "@/customPropTypes"; import { SUCCESS_CHECKMARK } from "@/constants/assets"; import CoreTable from "@/components/common/CoreTable"; -import { renderTextColumn } from "@/components/common/CoreTableCommonColumns"; +import { renderTextColumn } from "@mds/common/components/common/CoreTableCommonColumns"; /** * @class ContactList - paginated list of contacts diff --git a/services/core-web/src/components/dashboard/majorProjectHomePage/MajorProjectTable.js b/services/core-web/src/components/dashboard/majorProjectHomePage/MajorProjectTable.js index 0dda288de6..b37c16fddb 100644 --- a/services/core-web/src/components/dashboard/majorProjectHomePage/MajorProjectTable.js +++ b/services/core-web/src/components/dashboard/majorProjectHomePage/MajorProjectTable.js @@ -8,7 +8,10 @@ import { formatDate } from "@common/utils/helpers"; import CoreTable from "@/components/common/CoreTable"; import CustomPropTypes from "@/customPropTypes"; import * as router from "@/constants/routes"; -import { renderCategoryColumn, renderTextColumn } from "@/components/common/CoreTableCommonColumns"; +import { + renderCategoryColumn, + renderTextColumn, +} from "@mds/common/components/common/CoreTableCommonColumns"; const propTypes = { projects: PropTypes.arrayOf(CustomPropTypes.project).isRequired, diff --git a/services/core-web/src/components/maps/ExplosivesPermitMap.js b/services/core-web/src/components/maps/ExplosivesPermitMap.js deleted file mode 100644 index 7aa15e9240..0000000000 --- a/services/core-web/src/components/maps/ExplosivesPermitMap.js +++ /dev/null @@ -1,135 +0,0 @@ -import React, { Component } from "react"; -import L from "leaflet"; -import LeafletWms from "leaflet.wms"; -import PropTypes from "prop-types"; - -import "leaflet.markercluster"; -import "leaflet/dist/leaflet.css"; -import "leaflet.markercluster/dist/MarkerCluster.css"; -import "leaflet.markercluster/dist/MarkerCluster.Default.css"; - -import * as Strings from "@mds/common/constants/strings"; -import { Validate } from "@common/utils/Validate"; -import { SMALL_PIN_SELECTED } from "@/constants/assets"; - -/** - * @class ExplosivesPermitMap.js is a Leaflet Map component. - */ - -const propTypes = { - pin: PropTypes.arrayOf(PropTypes.string), -}; - -const defaultProps = { - pin: [], -}; - -const leafletWMSTiledOptions = { - transparent: true, - tiled: true, - uppercase: true, - format: "image/png", - identify: false, -}; - -const checkValidityOfCoordinateInput = (coordinates) => - coordinates.length === 2 && - Validate.checkLat(coordinates[0]) && - Validate.checkLon(coordinates[1]); - -const getMajorMinePermittedAreas = () => { - const majorMinesSource = LeafletWms.source( - "https://openmaps.gov.bc.ca/geo/pub/WHSE_MINERAL_TENURE.HSP_MJR_MINES_PERMTTD_AREAS_SP/ows", - { ...leafletWMSTiledOptions, identify: false } - ); - return majorMinesSource.getLayer("pub:WHSE_MINERAL_TENURE.HSP_MJR_MINES_PERMTTD_AREAS_SP"); -}; - -export class ExplosivesPermitMap extends Component { - state = { containsPin: false }; - - // if mine does not have a location, set a default to center the map - latLong = checkValidityOfCoordinateInput(this.props.pin) - ? // only add mine Pin if location exists - this.props.pin - : [Number(Strings.DEFAULT_LAT), Number(Strings.DEFAULT_LONG)]; - - componentDidMount() { - // Create the base map with layers - this.createMap(); - - // Add MinePins to the top of LayerList and add the LayerList widget - L.control.layers(this.getBaseMaps(), {}, { position: "topright" }).addTo(this.map); - } - - componentWillReceiveProps(nextProps) { - const locationChanged = nextProps.pin !== this.props.pin; - if (locationChanged && checkValidityOfCoordinateInput(nextProps.pin)) { - if (this.state.containsPin) { - this.pin.setLatLng(nextProps.pin); - this.map.fitBounds(this.markerClusterGroup.getBounds()); - } else { - this.setState({ containsPin: false }); - this.createPin(nextProps.pin); - } - } - } - - getBaseMaps() { - const topographicBasemap = L.tileLayer( - "https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}" - ); - // Add default basemap to the map - topographicBasemap.addTo(this.map); - - const worldImageryLayer = L.tileLayer( - "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}" - ); - - return { - Topographic: topographicBasemap, - "World Imagery": worldImageryLayer, - }; - } - - createPin = (pin) => { - const customIcon = L.icon({ - iconUrl: SMALL_PIN_SELECTED, - iconSize: [60, 60], - }); - this.pin = L.marker(pin, { icon: customIcon }); - this.markerClusterGroup.addLayer(this.pin); - this.map.fitBounds(this.markerClusterGroup.getBounds()); - this.markerClusterGroup.zoomToShowLayer(this.pin); - this.setState({ containsPin: true }); - }; - - createMap() { - this.map = L.map("leaflet-map", { attributionControl: false }) - .setView(this.latLong, Strings.DEFAULT_ZOOM) - .setMaxZoom(12); - const majorMinePermittedAreas = getMajorMinePermittedAreas(); - this.map.addLayer(majorMinePermittedAreas); - - this.markerClusterGroup = L.markerClusterGroup({ - animate: false, - spiderfyOnMaxZoom: true, - showCoverageOnHover: false, - }); - this.map.addLayer(this.markerClusterGroup); - } - - render() { - return ( -
- ); - } -} - -ExplosivesPermitMap.propTypes = propTypes; -ExplosivesPermitMap.defaultProps = defaultProps; - -export default ExplosivesPermitMap; diff --git a/services/core-web/src/components/mine/Compliance/ComplianceOrdersTable.js b/services/core-web/src/components/mine/Compliance/ComplianceOrdersTable.js index 2abc4191c0..8172b06ebc 100644 --- a/services/core-web/src/components/mine/Compliance/ComplianceOrdersTable.js +++ b/services/core-web/src/components/mine/Compliance/ComplianceOrdersTable.js @@ -6,7 +6,10 @@ import { RED_CLOCK } from "@/constants/assets"; import CustomPropTypes from "@/customPropTypes"; import DocumentLink from "@/components/common/DocumentLink"; import CoreTable from "@/components/common/CoreTable"; -import { renderDateColumn, renderTextColumn } from "@/components/common/CoreTableCommonColumns"; +import { + renderDateColumn, + renderTextColumn, +} from "@mds/common/components/common/CoreTableCommonColumns"; const propTypes = { filteredOrders: CustomPropTypes.complianceOrders, diff --git a/services/core-web/src/components/mine/ExplosivesPermit/MineExplosivesPermitTable.tsx b/services/core-web/src/components/mine/ExplosivesPermit/MineExplosivesPermitTable.tsx index 9ac4938588..078f0b75f7 100644 --- a/services/core-web/src/components/mine/ExplosivesPermit/MineExplosivesPermitTable.tsx +++ b/services/core-web/src/components/mine/ExplosivesPermit/MineExplosivesPermitTable.tsx @@ -29,12 +29,12 @@ import { ITableAction, renderDateColumn, renderTextColumn, -} from "@/components/common/CoreTableCommonColumns"; +} from "@mds/common/components/common/CoreTableCommonColumns"; import VioletEditIcon from "@/assets/icons/violet-edit"; import ActionMenu, { deleteConfirmWrapper, generateActionMenuItems, -} from "@/components/common/ActionMenu"; +} from "@mds/common/components/common/ActionMenu"; import { userHasRole } from "@mds/common/redux/reducers/authenticationReducer"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faFiles } from "@fortawesome/pro-light-svg-icons"; diff --git a/services/core-web/src/components/mine/NoticeOfWork/MineNoticeOfWorkTable.js b/services/core-web/src/components/mine/NoticeOfWork/MineNoticeOfWorkTable.js index f0b196e81e..16b654d905 100644 --- a/services/core-web/src/components/mine/NoticeOfWork/MineNoticeOfWorkTable.js +++ b/services/core-web/src/components/mine/NoticeOfWork/MineNoticeOfWorkTable.js @@ -11,7 +11,10 @@ import DocumentLink from "@/components/common/DocumentLink"; import { isEmpty } from "lodash"; import { downloadNowDocument } from "@common/utils/actionlessNetworkCalls"; import CoreTable from "@/components/common/CoreTable"; -import { renderDateColumn, renderTextColumn } from "@/components/common/CoreTableCommonColumns"; +import { + renderDateColumn, + renderTextColumn, +} from "@mds/common/components/common/CoreTableCommonColumns"; /** * @class MineNoticeOfWorkTable - list of mine notice of work applications diff --git a/services/core-web/src/components/mine/Projects/DecisionPackageTab.js b/services/core-web/src/components/mine/Projects/DecisionPackageTab.js index 5f1888c389..35adba5d76 100644 --- a/services/core-web/src/components/mine/Projects/DecisionPackageTab.js +++ b/services/core-web/src/components/mine/Projects/DecisionPackageTab.js @@ -27,7 +27,7 @@ import { fetchMineDocuments } from "@mds/common/redux/actionCreators/mineActionC import { getMineDocuments } from "@mds/common/redux/selectors/mineSelectors"; import ArchivedDocumentsSection from "@common/components/documents/ArchivedDocumentsSection"; import { Feature } from "@mds/common"; -import { renderCategoryColumn } from "@/components/common/CoreTableCommonColumns"; +import { renderCategoryColumn } from "@mds/common/components/common/CoreTableCommonColumns"; import * as Strings from "@mds/common/constants/strings"; import { MajorMineApplicationDocument } from "@mds/common/models/documents/document"; import withFeatureFlag from "@mds/common/providers/featureFlags/withFeatureFlag"; diff --git a/services/core-web/src/components/mine/Projects/MajorMineApplicationTab.js b/services/core-web/src/components/mine/Projects/MajorMineApplicationTab.js index d582822604..1f48b21241 100644 --- a/services/core-web/src/components/mine/Projects/MajorMineApplicationTab.js +++ b/services/core-web/src/components/mine/Projects/MajorMineApplicationTab.js @@ -24,7 +24,7 @@ import ArchivedDocumentsSection from "@common/components/documents/ArchivedDocum import DocumentCompression from "@/components/common/DocumentCompression"; import { Feature } from "@mds/common"; import { MajorMineApplicationDocument } from "@mds/common/models/documents/document"; -import { renderCategoryColumn } from "@/components/common/CoreTableCommonColumns"; +import { renderCategoryColumn } from "@mds/common/components/common/CoreTableCommonColumns"; import { DownloadOutlined } from "@ant-design/icons"; import { Button } from "antd"; diff --git a/services/core-web/src/components/mine/Projects/ProjectDocumentsTab.js b/services/core-web/src/components/mine/Projects/ProjectDocumentsTab.js index 302bdacbcc..060d779a97 100644 --- a/services/core-web/src/components/mine/Projects/ProjectDocumentsTab.js +++ b/services/core-web/src/components/mine/Projects/ProjectDocumentsTab.js @@ -20,7 +20,7 @@ import { getMineDocuments } from "@mds/common/redux/selectors/mineSelectors"; import ArchivedDocumentsSection from "@common/components/documents/ArchivedDocumentsSection"; import { Feature } from "@mds/common"; import { MajorMineApplicationDocument } from "@mds/common/models/documents/document"; -import { renderCategoryColumn } from "@/components/common/CoreTableCommonColumns"; +import { renderCategoryColumn } from "@mds/common/components/common/CoreTableCommonColumns"; import * as Strings from "@mds/common/constants/strings"; import withFeatureFlag from "@mds/common/providers/featureFlags/withFeatureFlag"; diff --git a/services/core-web/src/components/mine/Tailings/MineTailingsTable.tsx b/services/core-web/src/components/mine/Tailings/MineTailingsTable.tsx index f900400631..70d4d65321 100644 --- a/services/core-web/src/components/mine/Tailings/MineTailingsTable.tsx +++ b/services/core-web/src/components/mine/Tailings/MineTailingsTable.tsx @@ -24,7 +24,10 @@ import { EDIT_DAM, MINE_TAILINGS_DETAILS } from "@/constants/routes"; import { IDam, ITailingsStorageFacility } from "@mds/common"; import { ColumnsType } from "antd/lib/table"; import { FixedType } from "rc-table/lib/interface"; -import { renderCategoryColumn, renderTextColumn } from "@/components/common/CoreTableCommonColumns"; +import { + renderCategoryColumn, + renderTextColumn, +} from "@mds/common/components/common/CoreTableCommonColumns"; import { Feature } from "@mds/common"; import { useFeatureFlag } from "@mds/common/providers/featureFlags/useFeatureFlag"; diff --git a/services/core-web/src/components/modalContent/NoticeOfDepartureModal.tsx b/services/core-web/src/components/modalContent/NoticeOfDepartureModal.tsx index 29173b0668..92f130fbae 100644 --- a/services/core-web/src/components/modalContent/NoticeOfDepartureModal.tsx +++ b/services/core-web/src/components/modalContent/NoticeOfDepartureModal.tsx @@ -36,7 +36,10 @@ import { } from "@mds/common"; import { getUserAccessData } from "@mds/common/redux/selectors/authenticationSelectors"; import CoreTable from "@/components/common/CoreTable"; -import { renderDateColumn, renderTextColumn } from "../common/CoreTableCommonColumns"; +import { + renderDateColumn, + renderTextColumn, +} from "@mds/common/components/common/CoreTableCommonColumns"; import * as FORM from "@/constants/forms"; import { TRASHCAN } from "@/constants/assets"; import { NOTICE_OF_DEPARTURE_DOCUMENTS } from "@/constants/API"; diff --git a/services/core-web/src/components/modalContent/ViewMagazineModal.js b/services/core-web/src/components/modalContent/ViewMagazineModal.js index ca450f693b..1cda27a097 100644 --- a/services/core-web/src/components/modalContent/ViewMagazineModal.js +++ b/services/core-web/src/components/modalContent/ViewMagazineModal.js @@ -1,8 +1,9 @@ import React from "react"; import PropTypes from "prop-types"; import { Button, Descriptions, Row, Col, Divider } from "antd"; -import ExplosivesPermitMap from "@/components/maps/ExplosivesPermitMap"; + import CustomPropTypes from "@/customPropTypes"; +import ExplosivesPermitMap from "@mds/common/components/explosivespermits/ExplosivesPermitMap"; const propTypes = { closeModal: PropTypes.func.isRequired, diff --git a/services/core-web/src/components/modalContent/config.js b/services/core-web/src/components/modalContent/config.js index 78805dda78..a4e16e39c2 100644 --- a/services/core-web/src/components/modalContent/config.js +++ b/services/core-web/src/components/modalContent/config.js @@ -45,7 +45,7 @@ import ExplosivesPermitApplicationDecisionModal from "./ExplosivesPermitApplicat import ViewMagazineModal from "./ViewMagazineModal"; import ExplosivesPermitStatusModal from "./ExplosivesPermitStatusModal"; import ExplosivesPermitCloseModal from "./ExplosivesPermitCloseModal"; -import ExplosivesPermitViewModal from "./ExplosivesPermitViewModal"; +import ExplosivesPermitViewModal from "@mds/common/components/explosivespermits/ExplosivesPermitViewModal"; import MergePartyConfirmationModal from "./MergePartyConfirmationModal"; import ViewAllConditionsModal from "./ViewAllConditionsModal"; import UpdateNoWDateModal from "./UpdateNoWDateModal"; diff --git a/services/core-web/src/components/search/ContactResultsTable.js b/services/core-web/src/components/search/ContactResultsTable.js index e23dc9c8b1..6e8af92df2 100644 --- a/services/core-web/src/components/search/ContactResultsTable.js +++ b/services/core-web/src/components/search/ContactResultsTable.js @@ -7,7 +7,7 @@ import { Validate } from "@common/utils/Validate"; import * as Strings from "@mds/common/constants/strings"; import * as router from "@/constants/routes"; import CoreTable from "@/components/common/CoreTable"; -import { renderHighlightedTextColumn } from "../common/CoreTableCommonColumns"; +import { renderHighlightedTextColumn } from "@mds/common/components/common/CoreTableCommonColumns"; import { nullableStringSorter } from "@common/utils/helpers"; /** diff --git a/services/core-web/src/components/search/DocumentResultsTable.js b/services/core-web/src/components/search/DocumentResultsTable.js index b763e902c1..9e0f55222c 100644 --- a/services/core-web/src/components/search/DocumentResultsTable.js +++ b/services/core-web/src/components/search/DocumentResultsTable.js @@ -4,7 +4,10 @@ import PropTypes from "prop-types"; import Highlight from "react-highlighter"; import DocumentLink from "@/components/common/DocumentLink"; import CoreTable from "@/components/common/CoreTable"; -import { renderDateColumn, renderTextColumn } from "../common/CoreTableCommonColumns"; +import { + renderDateColumn, + renderTextColumn, +} from "@mds/common/components/common/CoreTableCommonColumns"; import { nullableStringSorter } from "@common/utils/helpers"; /** diff --git a/services/core-web/src/components/search/MineResultsTable.js b/services/core-web/src/components/search/MineResultsTable.js index 109f3d6f3d..5128b0bc95 100644 --- a/services/core-web/src/components/search/MineResultsTable.js +++ b/services/core-web/src/components/search/MineResultsTable.js @@ -6,7 +6,10 @@ import { Link } from "react-router-dom"; import * as Strings from "@mds/common/constants/strings"; import * as router from "@/constants/routes"; import { nullableStringSorter } from "@common/utils/helpers"; -import { renderHighlightedTextColumn, renderTextColumn } from "../common/CoreTableCommonColumns"; +import { + renderHighlightedTextColumn, + renderTextColumn, +} from "@mds/common/components/common/CoreTableCommonColumns"; import CoreTable from "@/components/common/CoreTable"; /** diff --git a/services/core-web/src/components/search/PermitResultsTable.js b/services/core-web/src/components/search/PermitResultsTable.js index 130fec5812..12e56d4790 100644 --- a/services/core-web/src/components/search/PermitResultsTable.js +++ b/services/core-web/src/components/search/PermitResultsTable.js @@ -5,7 +5,10 @@ import Highlight from "react-highlighter"; import { Link } from "react-router-dom"; import * as router from "@/constants/routes"; import CoreTable from "@/components/common/CoreTable"; -import { renderHighlightedTextColumn, renderTextColumn } from "../common/CoreTableCommonColumns"; +import { + renderHighlightedTextColumn, + renderTextColumn, +} from "@mds/common/components/common/CoreTableCommonColumns"; /** * @class PermitResultsTable - displays a table of mine search results diff --git a/services/core-web/src/setupTests.js b/services/core-web/src/setupTests.js index 3e72a2f7f8..e3c29960fd 100755 --- a/services/core-web/src/setupTests.js +++ b/services/core-web/src/setupTests.js @@ -4,14 +4,20 @@ import path from "path"; require("jest-localstorage-mock"); -global.REQUEST_HEADER = require(path.resolve(__dirname, "../common/utils/RequestHeaders.js")); - Enzyme.configure({ adapter: new Adapter() }); +// eslint-disable-next-line @typescript-eslint/no-var-requires +global.REQUEST_HEADER = require(path.resolve(__dirname, "../common/utils/RequestHeaders.js")); + global.requestAnimationFrame = (callback) => { setTimeout(callback, 0); }; +jest.mock("react-lottie", () => ({ + __esModule: true, + default: "lottie-mock", +})); + const location = JSON.stringify(window.location); delete window.location; diff --git a/services/core-web/src/styles/components/Tables.scss b/services/core-web/src/styles/components/Tables.scss index 24c6a55974..0bb6d3dae1 100644 --- a/services/core-web/src/styles/components/Tables.scss +++ b/services/core-web/src/styles/components/Tables.scss @@ -106,6 +106,12 @@ tr.ant-table-expanded-row:hover { white-space: normal; } +.break-word { + word-break: break-word !important; + overflow: initial !important; + white-space: initial !important; +} + // Centered Pagination $pagination-width-1: 112px; $pagination-width-2: 152px; @@ -165,4 +171,11 @@ td.ant-table-cell { margin-top: 0; margin-bottom: 0; } +} + +// This style is used in a common component, so it needs to be here, but doesn't require styles in core-web +.diff-table {} + +.diff-table-row > td{ + vertical-align: top; } \ No newline at end of file diff --git a/services/core-web/src/styles/elements/elements.scss b/services/core-web/src/styles/elements/elements.scss index a06705211a..7135fa6302 100644 --- a/services/core-web/src/styles/elements/elements.scss +++ b/services/core-web/src/styles/elements/elements.scss @@ -89,6 +89,10 @@ p, color: $violet !important; } +.primary-colour { + color: $violet !important; +} + .dark-grey { color: $darkest-grey !important; } diff --git a/services/core-web/src/styles/generic/layout.scss b/services/core-web/src/styles/generic/layout.scss index 6a47280f2a..c1e1a9498f 100644 --- a/services/core-web/src/styles/generic/layout.scss +++ b/services/core-web/src/styles/generic/layout.scss @@ -14,6 +14,10 @@ html { height: $nav-height + $loading-bar-height; } +.line-height-none { + line-height: 1; +} + .content { overflow: initial; min-height: 100vh; diff --git a/services/core-web/src/tests/components/common/__snapshots__/Loading.spec.js.snap b/services/core-web/src/tests/components/common/__snapshots__/Loading.spec.js.snap index 895e0b367c..f96434d439 100644 --- a/services/core-web/src/tests/components/common/__snapshots__/Loading.spec.js.snap +++ b/services/core-web/src/tests/components/common/__snapshots__/Loading.spec.js.snap @@ -7,13 +7,7 @@ exports[`Loading renders properly 1`] = `
-
diff --git a/services/core-web/src/tests/components/dashboard/majorProjectHomePage/__snapshots__/MajorProjectTable.spec.js.snap b/services/core-web/src/tests/components/dashboard/majorProjectHomePage/__snapshots__/MajorProjectTable.spec.js.snap index 125a461644..42ddbabd11 100644 --- a/services/core-web/src/tests/components/dashboard/majorProjectHomePage/__snapshots__/MajorProjectTable.spec.js.snap +++ b/services/core-web/src/tests/components/dashboard/majorProjectHomePage/__snapshots__/MajorProjectTable.spec.js.snap @@ -45,6 +45,7 @@ exports[`MajorProjectTable renders properly 1`] = ` "title": "Stage", }, Object { + "className": undefined, "dataIndex": "status_code", "key": "status_code", "render": [Function], diff --git a/services/core-web/src/tests/reducers/authenticationReducer.spec.js b/services/core-web/src/tests/reducers/authenticationReducer.spec.js index ce91911181..e9d6fa27f6 100644 --- a/services/core-web/src/tests/reducers/authenticationReducer.spec.js +++ b/services/core-web/src/tests/reducers/authenticationReducer.spec.js @@ -4,24 +4,39 @@ import { logoutUser, storeUserAccessData, } from "@mds/common/redux/actions/authenticationActions"; +import * as ROUTES from "../../constants/routes"; const baseExpectedValue = { isAuthenticated: false, userAccessData: [], userInfo: {}, + isProponent: undefined, + redirect: false, +}; + +const baseAuthenticatedExpectedValue = { + isAuthenticated: true, + userAccessData: [], + userInfo: {}, }; // Creates deep copy of javascript object instead of setting a reference const getBaseExpectedValue = () => JSON.parse(JSON.stringify(baseExpectedValue)); +const getBaseAuthenticatedExpectedValue = () => + JSON.parse(JSON.stringify(baseAuthenticatedExpectedValue)); describe("authReducer", () => { + beforeEach(() => { + global.GLOBAL_ROUTES = ROUTES; + }); + it("receives undefined", () => { const expectedValue = getBaseExpectedValue(); expect(authenticationReducer(undefined, {})).toEqual(expectedValue); }); it("receives AUTHENTICATE_USER", () => { - const expectedValue = getBaseExpectedValue(); + const expectedValue = getBaseAuthenticatedExpectedValue(); expectedValue.isAuthenticated = true; const result = authenticationReducer(undefined, authenticateUser({})); expect(result).toEqual(expectedValue); @@ -35,7 +50,8 @@ describe("authReducer", () => { }); it("receives LOGOUT", () => { - const expectedValue = getBaseExpectedValue(); + const expectedValue = getBaseAuthenticatedExpectedValue(); + expectedValue.isAuthenticated = false; const result = authenticationReducer(undefined, logoutUser()); expect(result).toEqual(expectedValue); }); diff --git a/services/core-web/src/tests/reducers/rootReducer.spec.js b/services/core-web/src/tests/reducers/rootReducer.spec.js index 94e21be36a..a077a8a644 100644 --- a/services/core-web/src/tests/reducers/rootReducer.spec.js +++ b/services/core-web/src/tests/reducers/rootReducer.spec.js @@ -2,8 +2,12 @@ import { createStore } from "redux"; import { authenticateUser } from "@mds/common/redux/actions/authenticationActions"; import * as reducerTypes from "@mds/common/constants/reducerTypes"; import { rootReducer, reducerObject } from "@/reducers/rootReducer"; +import * as ROUTES from "../../constants/routes"; describe("Store", () => { + beforeEach(() => { + global.GLOBAL_ROUTES = ROUTES; + }); it("should handle reducer creation", () => { const store = createStore(rootReducer, reducerObject); diff --git a/services/core-web/src/tests/selectors/authenticationSelectors.spec.js b/services/core-web/src/tests/selectors/authenticationSelectors.spec.js index 8adf91c790..42d6bb23ab 100644 --- a/services/core-web/src/tests/selectors/authenticationSelectors.spec.js +++ b/services/core-web/src/tests/selectors/authenticationSelectors.spec.js @@ -10,6 +10,7 @@ import { storeUserAccessData, } from "@mds/common/redux/actions/authenticationActions"; import { AUTHENTICATION } from "@mds/common/constants/reducerTypes"; +import * as ROUTES from "../../constants/routes"; const mockData = { userAccessData: ["role1"], @@ -17,6 +18,9 @@ const mockData = { }; describe("authSelectors", () => { + beforeEach(() => { + global.GLOBAL_ROUTES = ROUTES; + }); it("`isAuthenticated` calls `authReducer.isAuthenticated`", () => { const authAction = authenticateUser(mockData.userInfo); const authState = authenticationReducer({}, authAction); diff --git a/services/core-web/webpack.config.ts b/services/core-web/webpack.config.ts index 28023403ce..f2edf32cb8 100755 --- a/services/core-web/webpack.config.ts +++ b/services/core-web/webpack.config.ts @@ -74,7 +74,8 @@ const commonConfig = merge([ template: PATHS.template, }), new webpack.ProvidePlugin({ - REQUEST_HEADER: path.resolve(__dirname, 'common/utils/RequestHeaders.js'), + REQUEST_HEADER: path.resolve(__dirname, "common/utils/RequestHeaders.js"), + GLOBAL_ROUTES: path.resolve(__dirname, "src/constants/routes.ts"), }), ], resolve: { diff --git a/services/core-web/webpack.parts.js b/services/core-web/webpack.parts.js index 39d5f6cb64..56d392f2fe 100755 --- a/services/core-web/webpack.parts.js +++ b/services/core-web/webpack.parts.js @@ -18,23 +18,23 @@ const postCSSLoader = { options: { postcssOptions: { plugins: () => [autoprefixer], - } + }, }, }; const threadLoader = { - loader: 'thread-loader', + loader: "thread-loader", options: { workers: 1, workerParallelJobs: 50, workerNodeArgs: ["--max-old-space-size=3072"], - } -} + }, +}; exports.devServer = ({ host, port } = {}) => ({ cache: { // Persist cache to filesystem to speed up consecutive builds. - type: 'filesystem' + type: "filesystem", }, stats: { warningsFilter: [/Serializing big strings/], @@ -59,28 +59,27 @@ exports.devServer = ({ host, port } = {}) => ({ exports.loadJS = ({ include, exclude } = {}) => ({ module: { rules: [ - { test: /\.[[t]sx?$/, include, exclude, - loader: 'esbuild-loader', + loader: "esbuild-loader", options: { - target: 'es2015' - } + target: "es2015", + }, }, { test: /\.[[j]sx?$/, include, exclude, - loader: 'esbuild-loader', + loader: "esbuild-loader", options: { /// Treat .js files as `.jsx` files - loader: 'jsx', - target: 'es2015' - } + loader: "jsx", + target: "es2015", + }, }, ], }, @@ -94,11 +93,11 @@ exports.loadTS = ({ include, exclude } = {}) => ({ include, exclude, - loader: 'esbuild-loader', + loader: "esbuild-loader", options: { - target: 'es2015' - } - } + target: "es2015", + }, + }, ], }, }); @@ -292,7 +291,7 @@ exports.loadFonts = ({ include, exclude, options } = {}) => ({ module: { rules: [ { - test: /\.(woff(2)?|ttf|eot|svg)(\?v=\d+\.\d+\.\d+)?$/, + test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/, include, exclude, use: { @@ -320,12 +319,9 @@ exports.bundleOptimization = ({ options, cssOptions } = {}) => ({ }), new CssMinimizerPlugin({ minimizerOptions: { - preset: [ - 'default', - cssOptions - ] - } - }) + preset: ["default", cssOptions], + }, + }), ], }, }); @@ -349,9 +345,11 @@ exports.clean = () => ({ }); exports.copy = (from, to) => ({ - plugins: [new CopyWebpackPlugin({ - patterns: [{ from, to, globOptions: { ignore: ["*.html"] } }] - })], + plugins: [ + new CopyWebpackPlugin({ + patterns: [{ from, to, globOptions: { ignore: ["*.html"] } }], + }), + ], }); exports.extractManifest = () => ({ diff --git a/services/minespace-web/common/components/explosivesPermits/ExplosivesPermitDiffModal.tsx b/services/minespace-web/common/components/explosivesPermits/ExplosivesPermitDiffModal.tsx deleted file mode 100644 index 925719bec4..0000000000 --- a/services/minespace-web/common/components/explosivesPermits/ExplosivesPermitDiffModal.tsx +++ /dev/null @@ -1,252 +0,0 @@ -import { Button, Modal, Table, Typography } from "antd"; -import React, { FC, useEffect, useState } from "react"; -import { isEqual } from "lodash"; -import { IExplosivesPermit } from "@mds/common/interfaces/permits/explosivesPermit.interface"; - -interface ExplosivesPermitDiffModalProps { - explosivesPermit: IExplosivesPermit; - open: boolean; - onCancel: () => void; -} - -interface IPermitDifference { - fieldName: string; - previousValue: any; - currentValue: any; -} - -interface IPermitDifferencesByAmendment { - [amendmentId: string]: IPermitDifference[]; -} - -const ExplosivesPermitDiffModal: FC = ({ - explosivesPermit, - open = false, - onCancel, -}) => { - const [differences, setDifferences] = useState({}); - - const getPermitDifferences = (permit: IExplosivesPermit): IPermitDifferencesByAmendment => { - const comparablePermit = { - explosives_permit_amendment_id: undefined, - ...permit, - }; - - const permitVersions = [comparablePermit, ...permit.explosives_permit_amendments].sort( - (a, b) => a.explosives_permit_amendment_id - b.explosives_permit_amendment_id - ); - - const ignoredFields = [ - "explosives_permit_amendment_id", - "explosives_permit_amendment_guid", - "issuing_inspector_party_guid", - "isAmendment", - "amendment_no", - ]; - - const differences: IPermitDifferencesByAmendment = permitVersions.reduce( - (acc, currAmendment, i) => { - if (i === 0) { - acc["0"] = []; - return acc; - } - - const previousAmendment = permitVersions[i - 1]; - - Object.entries(currAmendment).forEach(([key, newValue]) => { - const oldValue = previousAmendment[key]; - - if ( - (key === "detonator_magazines" || key === "explosive_magazines") && - Array.isArray(newValue) - ) { - for (const [idx, newVal] of newValue.entries()) { - const oldVal = oldValue[idx]; - - if (!isEqual(newVal, oldVal)) { - if (!acc[currAmendment.explosives_permit_amendment_id]) { - acc[currAmendment.explosives_permit_amendment_id] = []; - } - - for (const [magazineKey, magazineValue] of Object.entries(newVal)) { - const oldMagazineValue = oldVal?.[magazineKey]; - const ignoredMagazineFields = [ - "explosives_permit_amendment_magazine_id", - "explosives_permit_amendment_magazine_type_code", - "explosives_permit_magazine_id", - "explosives_permit_magazine_type_code", - ]; - - if ( - magazineValue !== oldMagazineValue && - !ignoredMagazineFields.includes(magazineKey) - ) { - const fieldPrefix = - key === "detonator_magazines" ? "Detonator Magazine" : "Explosive Magazine"; - const diff: IPermitDifference = { - fieldName: `${fieldPrefix} ${idx} - ${magazineKey}`, - previousValue: oldMagazineValue, - currentValue: magazineValue, - }; - acc[currAmendment.explosives_permit_amendment_id].push(diff); - } - } - } - } - - return; - } - - if (typeof newValue === "object" && newValue !== null) { - return; - } - - if (!acc[currAmendment.explosives_permit_amendment_id]) { - acc[currAmendment.explosives_permit_amendment_id] = []; - } - - if (newValue !== oldValue && !ignoredFields.includes(key)) { - const diff: IPermitDifference = { - fieldName: key, - previousValue: oldValue, - currentValue: newValue, - }; - - acc[currAmendment.explosives_permit_amendment_id].push(diff); - } - }); - - const amendmentDocuments = currAmendment.documents.map((doc) => doc.document_name); - if (amendmentDocuments && amendmentDocuments.length > 0) { - acc[currAmendment.explosives_permit_amendment_id].push({ - fieldName: "Documents", - previousValue: [], - currentValue: amendmentDocuments, - }); - } - return acc; - }, - {} - ); - - return differences; - }; - - useEffect(() => { - if (explosivesPermit) { - const differencesList = getPermitDifferences(explosivesPermit); - setDifferences(differencesList); - } - }, [explosivesPermit]); - - const valueOrNoData = (value: any) => { - if (typeof value === "boolean") { - return value ? "True" : "False"; - } - - return value ? value : "No Data"; - }; - - const columns = [ - { - title: "Notice of Work #", - dataIndex: "now_number", - key: "now_number", - }, - { - title: "Status", - key: "is_closed", - render: (record: any) => { - return record.is_closed ? "Closed" : "Open"; - }, - }, - { - title: "Amendment", - key: "order_no", - dataIndex: "order_no", - }, - { - title: "Changes", - dataIndex: "differences", - key: "differences", - render: (differences: IPermitDifference[]) => - differences.map((diff) => ( -
- {diff.fieldName === "Documents" ? ( -
- - Files Added: - - {diff.currentValue.map((file: any, index) => ( - - {file} - - ))} -
- ) : ( -
- - {diff.fieldName} - - {diff.fieldName !== "None" && ( - - - {valueOrNoData(diff.previousValue)} - - {` => `} - - {valueOrNoData(diff.currentValue)} - - - )} -
- )} -
- )), - }, - ]; - - const data = Object.keys(differences) - .map((key: any, index: number) => { - const amendment = explosivesPermit.explosives_permit_amendments.find( - (amendment) => amendment.explosives_permit_amendment_id == key - ); - - const permit = key === "0" ? explosivesPermit : amendment; - - return { - ...permit, - differences: differences[key].length > 0 ? differences[key] : [{ fieldName: "None" }], - order_no: index, - }; - }) - .reverse(); - - return ( - - Close - , - ]} - width={1000} - > - View History - - You are viewing the past history of explosive storage and use permits for this permit ( - Permit # {explosivesPermit.permit_number}) - -
- - ); -}; - -export default ExplosivesPermitDiffModal; diff --git a/services/minespace-web/src/App.tsx b/services/minespace-web/src/App.tsx index aeb625a6f4..a1e8d03a2c 100755 --- a/services/minespace-web/src/App.tsx +++ b/services/minespace-web/src/App.tsx @@ -10,7 +10,7 @@ import { loadBulkStaticContent } from "@mds/common/redux/actionCreators/staticCo import { getStaticContentLoadingIsComplete } from "@mds/common/redux/selectors/staticContentSelectors"; import MediaQuery from "react-responsive"; import * as PropTypes from "prop-types"; -import { isAuthenticated } from "@/selectors/authenticationSelectors"; +import { isAuthenticated } from "@mds/common/redux/selectors/authenticationSelectors"; import { Header } from "@/components/layout/Header"; import { Footer } from "@/components/layout/Footer"; import ModalWrapper from "@/components/common/wrappers/ModalWrapper"; diff --git a/services/minespace-web/src/HOC/AuthenticationGuard.js b/services/minespace-web/src/HOC/AuthenticationGuard.js index 5ed347e8b2..139d6522e4 100644 --- a/services/minespace-web/src/HOC/AuthenticationGuard.js +++ b/services/minespace-web/src/HOC/AuthenticationGuard.js @@ -7,7 +7,7 @@ import queryString from "query-string"; import hoistNonReactStatics from "hoist-non-react-statics"; import { useKeycloak } from "@react-keycloak/web"; import { KEYCLOAK } from "@mds/common"; -import { isAuthenticated } from "@/selectors/authenticationSelectors"; +import { isAuthenticated } from "@mds/common/redux/selectors/authenticationSelectors"; import { authenticateUser } from "@/actionCreators/authenticationActionCreator"; import { storeUserAccessData } from "@mds/common/redux/actions/authenticationActions"; import UnauthenticatedNotice from "@/components/common/UnauthenticatedNotice"; diff --git a/services/minespace-web/src/components/Forms/projects/informationRequirementsTable/IRTFileImport.js b/services/minespace-web/src/components/Forms/projects/informationRequirementsTable/IRTFileImport.js index 28725f1774..6f41764810 100644 --- a/services/minespace-web/src/components/Forms/projects/informationRequirementsTable/IRTFileImport.js +++ b/services/minespace-web/src/components/Forms/projects/informationRequirementsTable/IRTFileImport.js @@ -24,7 +24,7 @@ import { renderCategoryColumn, renderDateColumn, renderTextColumn, -} from "@/components/common/CoreTableCommonColumns"; +} from "@mds/common/components/common/CoreTableCommonColumns"; import { formatDateTime } from "@common/utils/helpers"; import { documentNameColumn } from "@/components/common/DocumentColumns"; import { MineDocument } from "@mds/common/models/documents/document"; diff --git a/services/minespace-web/src/components/Forms/projects/majorMineApplication/MajorMineApplicationForm.js b/services/minespace-web/src/components/Forms/projects/majorMineApplication/MajorMineApplicationForm.js index e2b024cdfc..54c37b3601 100644 --- a/services/minespace-web/src/components/Forms/projects/majorMineApplication/MajorMineApplicationForm.js +++ b/services/minespace-web/src/components/Forms/projects/majorMineApplication/MajorMineApplicationForm.js @@ -26,7 +26,7 @@ import { fetchMineDocuments } from "@mds/common/redux/actionCreators/mineActionC import { getMineDocuments } from "@mds/common/redux/selectors/mineSelectors"; import ArchivedDocumentsSection from "@common/components/documents/ArchivedDocumentsSection"; import { MajorMineApplicationDocument } from "@mds/common/models/documents/document"; -import { renderCategoryColumn } from "@/components/common/CoreTableCommonColumns"; +import { renderCategoryColumn } from "@mds/common/components/common/CoreTableCommonColumns"; import * as Strings from "@mds/common/constants/strings"; const propTypes = { diff --git a/services/minespace-web/src/components/Forms/projects/majorMineApplication/MajorMineApplicationReviewSubmit.js b/services/minespace-web/src/components/Forms/projects/majorMineApplication/MajorMineApplicationReviewSubmit.js index 881d70a96c..84f3f0db1a 100644 --- a/services/minespace-web/src/components/Forms/projects/majorMineApplication/MajorMineApplicationReviewSubmit.js +++ b/services/minespace-web/src/components/Forms/projects/majorMineApplication/MajorMineApplicationReviewSubmit.js @@ -17,7 +17,7 @@ import { MAJOR_MINE_APPLICATION_SUBMISSION_STATUSES } from "@/components/pages/P import ArchivedDocumentsSection from "@common/components/documents/ArchivedDocumentsSection"; import { getMineDocuments } from "@mds/common/redux/selectors/mineSelectors"; import { MajorMineApplicationDocument } from "@mds/common/models/documents/document"; -import { renderCategoryColumn } from "@/components/common/CoreTableCommonColumns"; +import { renderCategoryColumn } from "@mds/common/components/common/CoreTableCommonColumns"; const propTypes = { project: CustomPropTypes.project.isRequired, diff --git a/services/minespace-web/src/components/Forms/projects/projectSummary/DocumentUpload.tsx b/services/minespace-web/src/components/Forms/projects/projectSummary/DocumentUpload.tsx index c5d2b80eeb..eddf613021 100644 --- a/services/minespace-web/src/components/Forms/projects/projectSummary/DocumentUpload.tsx +++ b/services/minespace-web/src/components/Forms/projects/projectSummary/DocumentUpload.tsx @@ -12,7 +12,7 @@ import DocumentTable from "@/components/common/DocumentTable"; import { documentNameColumn, uploadDateColumn } from "@/components/common/DocumentColumns"; import ProjectSummaryFileUpload from "@/components/Forms/projects/projectSummary/ProjectSummaryFileUpload"; import * as FORM from "@/constants/forms"; -import { renderCategoryColumn } from "@/components/common/CoreTableCommonColumns"; +import { renderCategoryColumn } from "@mds/common/components/common/CoreTableCommonColumns"; import { MineDocument } from "@mds/common/models/documents/document"; import { IMineDocument } from "@mds/common"; import { RootState } from "@/App"; diff --git a/services/minespace-web/src/components/Forms/projects/projectSummary/ProjectSummaryFileUpload.tsx b/services/minespace-web/src/components/Forms/projects/projectSummary/ProjectSummaryFileUpload.tsx index 11640d6044..e1b7f757db 100644 --- a/services/minespace-web/src/components/Forms/projects/projectSummary/ProjectSummaryFileUpload.tsx +++ b/services/minespace-web/src/components/Forms/projects/projectSummary/ProjectSummaryFileUpload.tsx @@ -5,7 +5,7 @@ import { useSelector } from "react-redux"; import { NEW_VERSION_DOCUMENTS, PROJECT_SUMMARY_DOCUMENTS } from "@mds/common/constants/API"; import FileUpload from "@/components/common/FileUpload"; import { Alert, Divider, Modal, Popconfirm, Table, Typography } from "antd"; -import { getUserInfo } from "@/selectors/authenticationSelectors"; +import { getUserInfo } from "@mds/common/redux/selectors/authenticationSelectors"; import { FilePondFile } from "filepond"; import { IDocument } from "@mds/common"; import { HttpRequest, HttpResponse } from "tus-js-client"; diff --git a/services/minespace-web/src/components/common/ActionMenu.tsx b/services/minespace-web/src/components/common/ActionMenu.tsx deleted file mode 100644 index c857f3126e..0000000000 --- a/services/minespace-web/src/components/common/ActionMenu.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { Button, Dropdown, Modal } from "antd"; -import CaretDownOutlined from "@ant-design/icons/CaretDownOutlined"; -import React, { FC } from "react"; -import { ITableAction } from "@/components/common/CoreTableCommonColumns"; - -interface ActionMenuProps { - record: any; - actionItems: ITableAction[]; - category: string; -} - -export const deleteConfirmWrapper = (recordDescription: string, onOk: () => void) => { - const title = `Confirm Deletion`; - const content = `Are you sure you want to delete this ${recordDescription}?`; - const modalContent = { - title, - content, - onOk, - okText: "Delete", - }; - return Modal.confirm(modalContent); -}; - -export const generateActionMenuItems = (actionItems: ITableAction[], record) => { - return actionItems.map((action) => { - return { - key: action.key, - icon: action.icon, - label: ( - - ), - }; - }); -}; - -const ActionMenu: FC = ({ record, actionItems, category }) => { - const items = generateActionMenuItems(actionItems, record); - return ( - - - - ); -}; - -export default ActionMenu; diff --git a/services/minespace-web/src/components/common/CoreTableCommonColumns.tsx b/services/minespace-web/src/components/common/CoreTableCommonColumns.tsx deleted file mode 100644 index 5f1a3d7926..0000000000 --- a/services/minespace-web/src/components/common/CoreTableCommonColumns.tsx +++ /dev/null @@ -1,116 +0,0 @@ -import React, { ReactNode } from "react"; -import Highlight from "react-highlighter"; -import { dateSorter, formatDate, nullableStringSorter } from "@common/utils/helpers"; -import { EMPTY_FIELD } from "@mds/common/constants/strings"; -import { ColumnType } from "antd/lib/table"; -import { Button, Dropdown } from "antd"; -import CaretDownOutlined from "@ant-design/icons/CaretDownOutlined"; -import { generateActionMenuItems } from "./ActionMenu"; - -export const renderTextColumn = ( - dataIndex: string, - title: string, - sortable = false, - placeHolder = EMPTY_FIELD, - width?: number -): ColumnType => { - return { - title, - dataIndex, - key: dataIndex, - render: (text: string) => ( -
- {text ?? placeHolder} -
- ), - ...(width !== undefined ? { width } : null), - ...(sortable ? { sorter: nullableStringSorter(dataIndex) } : null), - }; -}; - -export const renderDateColumn = ( - dataIndex: string, - title = "Date", - sortable = false, - format: (date: any) => string | null = null, - placeHolder = EMPTY_FIELD -) => { - const formatFunction = format ?? formatDate; - return { - title, - dataIndex, - key: dataIndex, - render: (text) =>
{formatFunction(text) || placeHolder}
, - ...(sortable ? { sorter: dateSorter(dataIndex) } : null), - }; -}; - -export const renderCategoryColumn = ( - dataIndex: string, - title: string, - categoryMap: any, - sortable = false, - placeHolder = EMPTY_FIELD -) => { - return { - title, - dataIndex, - key: dataIndex, - render: (text: string) =>
{categoryMap[text] ?? placeHolder}
, - ...(sortable ? { sorter: nullableStringSorter(dataIndex) } : null), - }; -}; - -export const renderHighlightedTextColumn = ( - dataIndex: string, - title: string, - regex: string, - sortable = true -): ColumnType => { - return { - title, - dataIndex, - key: dataIndex, - render: (text: string) => { - return {text}; - }, - ...(sortable ? { sorter: nullableStringSorter(dataIndex) } : null), - }; -}; - -export interface ITableAction { - key: string; - label: string; - clickFunction: (event, record) => any; - icon?: ReactNode; -} - -export const renderActionsColumn = ( - actions: ITableAction[], - recordActionsFilter?: (record, actions) => ITableAction[], - isRowSelected = false, - text = "Actions", - dropdownAltText = "Menu" -) => { - return { - key: "actions", - render: (record) => { - const filteredActions = recordActionsFilter ? recordActionsFilter(record, actions) : actions; - const items = generateActionMenuItems(filteredActions, record); - - return ( -
- {items.length > 0 && ( - - {/* // TODO: change button classname to something generic */} - - - )} -
- ); - }, - }; -}; diff --git a/services/minespace-web/src/components/common/DocumentColumns.tsx b/services/minespace-web/src/components/common/DocumentColumns.tsx index 85a3f9a810..b8f8ef0279 100644 --- a/services/minespace-web/src/components/common/DocumentColumns.tsx +++ b/services/minespace-web/src/components/common/DocumentColumns.tsx @@ -2,7 +2,10 @@ import React, { ReactNode } from "react"; import * as Strings from "@mds/common/constants/strings"; import { Button, Popconfirm, Tag, Tooltip } from "antd"; import { ColumnType } from "antd/lib/table"; -import { renderDateColumn, renderTextColumn } from "./CoreTableCommonColumns"; +import { + renderDateColumn, + renderTextColumn, +} from "@mds/common/components/common/CoreTableCommonColumns"; import { MineDocument } from "@mds/common/models/documents/document"; import { nullableStringSorter } from "@common/utils/helpers"; import ClockCircleOutlined from "@ant-design/icons/ClockCircleOutlined"; diff --git a/services/minespace-web/src/components/common/DocumentTable.tsx b/services/minespace-web/src/components/common/DocumentTable.tsx index 850e2ef981..e91fcc0bd6 100644 --- a/services/minespace-web/src/components/common/DocumentTable.tsx +++ b/services/minespace-web/src/components/common/DocumentTable.tsx @@ -6,7 +6,11 @@ import { uploadDateColumn, uploadedByColumn, } from "./DocumentColumns"; -import { renderTextColumn, renderActionsColumn, ITableAction } from "./CoreTableCommonColumns"; +import { + renderTextColumn, + renderActionsColumn, + ITableAction, +} from "@mds/common/components/common/CoreTableCommonColumns"; import { some } from "lodash"; import { closeModal, openModal } from "@mds/common/redux/actions/modalActions"; import { archiveMineDocuments } from "@mds/common/redux/actionCreators/mineActionCreator"; diff --git a/services/minespace-web/src/components/common/wrappers/AuthorizationWrapper.js b/services/minespace-web/src/components/common/wrappers/AuthorizationWrapper.js index ea9b44aef9..cf7833373a 100644 --- a/services/minespace-web/src/components/common/wrappers/AuthorizationWrapper.js +++ b/services/minespace-web/src/components/common/wrappers/AuthorizationWrapper.js @@ -2,7 +2,7 @@ import React from "react"; import { PropTypes } from "prop-types"; import { connect } from "react-redux"; import { detectDevelopmentEnvironment, detectProdEnvironment } from "@mds/common"; -import { isProponent, isAuthenticated } from "@/selectors/authenticationSelectors"; +import { isProponent, isAuthenticated } from "@mds/common/redux/selectors/authenticationSelectors"; /** * @constant AuthorizationWrapper conditionally renders react children depending diff --git a/services/minespace-web/src/components/dashboard/mine/overview/Overview.js b/services/minespace-web/src/components/dashboard/mine/overview/Overview.js index 505bd03d88..22f89a2051 100644 --- a/services/minespace-web/src/components/dashboard/mine/overview/Overview.js +++ b/services/minespace-web/src/components/dashboard/mine/overview/Overview.js @@ -13,7 +13,7 @@ import { import { getTransformedMineTypes } from "@mds/common/redux/selectors/mineSelectors"; import { getEMLIContactsByRegion } from "@mds/common/redux/selectors/minespaceSelector"; import WorkerInfoEmployee from "@/components/dashboard/mine/overview/WorkerInfoEmployee"; -import { getUserInfo } from "@/selectors/authenticationSelectors"; +import { getUserInfo } from "@mds/common/redux/selectors/authenticationSelectors"; import CustomPropTypes from "@/customPropTypes"; import ContactCard from "@/components/common/ContactCard"; import MinistryContactItem from "@/components/dashboard/mine/overview/MinistryContactItem"; diff --git a/services/minespace-web/src/components/dashboard/mine/permits/ExplosivesPermit.tsx b/services/minespace-web/src/components/dashboard/mine/permits/ExplosivesPermit.tsx new file mode 100644 index 0000000000..e4ba546e7a --- /dev/null +++ b/services/minespace-web/src/components/dashboard/mine/permits/ExplosivesPermit.tsx @@ -0,0 +1,106 @@ +import ExplosivesPermitViewModal from "@mds/common/components/explosivespermits/ExplosivesPermitViewModal"; +import { IExplosivesPermit, IExplosivesPermitAmendment, IMine } from "@mds/common"; +import { closeModal, openModal } from "@mds/common/redux/actions/modalActions"; +import { connect } from "react-redux"; +import React, { FC, useEffect, useState } from "react"; +import { getExplosivesPermits } from "@mds/common/redux/selectors/explosivesPermitSelectors"; +import { Link, useParams } from "react-router-dom"; +import { fetchExplosivesPermits } from "@mds/common/redux/actionCreators/explosivesPermitActionCreator"; +import { ActionCreator } from "@mds/common/interfaces/actionCreator"; +import { Col, Row, Typography } from "antd"; +import * as router from "@/constants/routes"; +import ArrowLeftOutlined from "@ant-design/icons/ArrowLeftOutlined"; +import { getMines } from "@mds/common/redux/selectors/mineSelectors"; + +interface ExplosivesPermitProps { + handleOpenExplosivesPermitCloseModal: () => void; + openModal?: () => void; + closeModal?: () => void; + explosivesPermits?: IExplosivesPermit[]; + fetchExplosivesPermits?: ActionCreator; + mines?: IMine[]; +} + +const ExplosivesPermit: FC = ({ + handleOpenExplosivesPermitCloseModal, + explosivesPermits, + mines, + ...props +}) => { + const [explosivesPermit, setExplosivesPermit] = useState< + IExplosivesPermit | IExplosivesPermitAmendment + >(null); + const [parentPermit, setParentPermit] = useState(null); + + const { mineGuid, explosivesPermitGuid, amendmentId } = useParams<{ + mineGuid: string; + explosivesPermitGuid: string; + amendmentId: string; + }>(); + + const mineName = mines[mineGuid]?.mine_name || ""; + + useEffect(() => { + if (explosivesPermits.length >= 0) { + props.fetchExplosivesPermits(mineGuid); + } + }, []); + + useEffect(() => { + if (explosivesPermits?.length > 0) { + const currentPermit = explosivesPermits.find( + ({ explosives_permit_guid }) => explosives_permit_guid === explosivesPermitGuid + ); + const currentAmendment = amendmentId + ? currentPermit?.explosives_permit_amendments.find( + ({ explosives_permit_amendment_id }) => + explosives_permit_amendment_id === Number(amendmentId) + ) + : currentPermit; + + setExplosivesPermit(currentAmendment); + setParentPermit(currentPermit); + } + }, [explosivesPermits]); + + return ( +
+
+ +
+ Explosives Storage and Use Permit + + + + + + + Back to: {mineName} Permits + + + + + {explosivesPermit && parentPermit && ( + + )} + + ); +}; + +const mapStateToProps = (state) => ({ + explosivesPermits: getExplosivesPermits(state), + mines: getMines(state), +}); + +const mapDispatchToProps = { + openModal, + closeModal, + fetchExplosivesPermits, +}; + +export default connect(mapStateToProps, mapDispatchToProps)(ExplosivesPermit); diff --git a/services/minespace-web/src/components/dashboard/mine/permits/Permits.tsx b/services/minespace-web/src/components/dashboard/mine/permits/Permits.tsx index e22e4626e8..68dd2cd59f 100644 --- a/services/minespace-web/src/components/dashboard/mine/permits/Permits.tsx +++ b/services/minespace-web/src/components/dashboard/mine/permits/Permits.tsx @@ -7,7 +7,14 @@ import { openModal } from "@mds/common/redux/actions/modalActions"; import { getPermits } from "@mds/common/redux/selectors/permitSelectors"; import { getExplosivesPermits } from "@mds/common/redux/selectors/explosivesPermitSelectors"; import PermitsTable from "@/components/dashboard/mine/permits/PermitsTable"; -import { Feature, IExplosivesPermit, IMine, IPermit, VC_CONNECTION_STATES, isFeatureEnabled } from "@mds/common"; +import { + Feature, + IExplosivesPermit, + IMine, + IPermit, + VC_CONNECTION_STATES, + isFeatureEnabled, +} from "@mds/common"; import { ActionCreator } from "@mds/common/interfaces/actionCreator"; import modalConfig from "@/components/modalContent/config"; diff --git a/services/minespace-web/src/components/dashboard/mine/permits/PermitsTable.tsx b/services/minespace-web/src/components/dashboard/mine/permits/PermitsTable.tsx index ee53173451..102a484f31 100644 --- a/services/minespace-web/src/components/dashboard/mine/permits/PermitsTable.tsx +++ b/services/minespace-web/src/components/dashboard/mine/permits/PermitsTable.tsx @@ -9,7 +9,7 @@ import { VC_CONNECTION_STATES, VC_CRED_ISSUE_STATES, isFeatureEnabled, -} from "@mds/common/index"; +} from "@mds/common"; import { openModal, closeModal } from "@mds/common/redux/actions/modalActions"; import { truncateFilename } from "@common/utils/helpers"; import { getDropdownPermitStatusOptions } from "@mds/common/redux/selectors/staticContentSelectors"; @@ -23,9 +23,12 @@ import { renderCategoryColumn, renderDateColumn, renderTextColumn, -} from "@/components/common/CoreTableCommonColumns"; +} from "@mds/common/components/common/CoreTableCommonColumns"; import IssuePermitDigitalCredential from "@/components/modalContent/verifiableCredentials/IssuePermitDigitalCredential"; import { SortOrder } from "antd/lib/table/interface"; +import { VIEW_ESUP } from "@/constants/routes"; +import { useHistory } from "react-router-dom"; +import { useParams } from "react-router-dom"; const draftAmendment = "DFT"; @@ -40,6 +43,7 @@ interface PermitsTableProps { explosivesPermits: IExplosivesPermit[]; majorMineInd: boolean; openModal: (value: any) => void; + closeModal: (value: any) => void; openVCWalletInvitationModal: ( event, partyGuid: string, @@ -49,6 +53,9 @@ interface PermitsTableProps { } export const PermitsTable: FC = (props) => { + const history = useHistory(); + const { id } = useParams<{ id: string }>(); + const columns = [ renderTextColumn("permit_no", "Permit No.", true), renderTextColumn("current_permittee", "Permittee"), @@ -62,15 +69,22 @@ export const PermitsTable: FC = (props) => { }, ]; - if ( + const showVCColumn = isFeatureEnabled(Feature.VERIFIABLE_CREDENTIALS) && props.majorMineInd && props.permits.some((p) => { // look for *any* active wallet connections to show the issuance column/action const walletStatus = p.current_permittee_digital_wallet_connection_state; return VC_CONNECTION_STATES[walletStatus] === VC_CONNECTION_STATES.active; - }) - ) { + }); + + const handleOpenViewEsup = (event, record: any) => { + event.preventDefault(); + event.stopPropagation(); + history.push(VIEW_ESUP.dynamicRoute(id, record.key)); + }; + + if (showVCColumn || isFeatureEnabled(Feature.MINESPACE_ESUPS)) { const colourMap = { "Not Active": "#D8292F", Pending: "#F1C21B", @@ -108,14 +122,31 @@ export const PermitsTable: FC = (props) => { label: "Issue as digital credential", clickFunction: openIssuanceModal, }, + { + key: "view_esup", + label: "View", + clickFunction: (event, esup: IExplosivesPermit) => { + handleOpenViewEsup(event, esup); + }, + }, ]; const filterActions = (record, actionList) => { - if (record.permit_type !== permitTypes.Permit) { - return actionList.filter((a) => a.key !== "vc_issue"); + let filteredActionList = actionList; + + // filter for permit type and vc_issue key + if (record.permit_type !== permitTypes.Permit || !showVCColumn) { + filteredActionList = filteredActionList.filter((a) => a.key !== "vc_issue"); + } + + // filter for feature flag and view_esup key + if (!isFeatureEnabled(Feature.MINESPACE_ESUPS) || record.permit_type !== permitTypes.ESUP) { + filteredActionList = filteredActionList.filter((a) => a.key !== "view_esup"); } - return actionList; + + return filteredActionList; }; + const actionColumn = renderActionsColumn(actions, filterActions); columns.splice(3, 0, issuanceStateColumn); columns.push(actionColumn); diff --git a/services/minespace-web/src/components/dashboard/mine/variances/VarianceDetails.js b/services/minespace-web/src/components/dashboard/mine/variances/VarianceDetails.js index 86136dd4d3..e542823419 100644 --- a/services/minespace-web/src/components/dashboard/mine/variances/VarianceDetails.js +++ b/services/minespace-web/src/components/dashboard/mine/variances/VarianceDetails.js @@ -6,7 +6,7 @@ import CustomPropTypes from "@/customPropTypes"; import DocumentTable from "@/components/common/DocumentTable"; import { documentNameColumn, uploadDateColumn } from "@/components/common/DocumentColumns"; import * as Strings from "@/constants/strings"; -import { renderCategoryColumn } from "@/components/common/CoreTableCommonColumns"; +import { renderCategoryColumn } from "@mds/common/components/common/CoreTableCommonColumns"; import { MineDocument } from "@mds/common/models/documents/document"; const propTypes = { diff --git a/services/minespace-web/src/components/layout/HeaderDropdown.js b/services/minespace-web/src/components/layout/HeaderDropdown.js index 507f0b07f0..f4d89d0552 100644 --- a/services/minespace-web/src/components/layout/HeaderDropdown.js +++ b/services/minespace-web/src/components/layout/HeaderDropdown.js @@ -9,7 +9,7 @@ import * as COMMON_ENV from "@mds/common"; import * as route from "@/constants/routes"; import * as MINESPACE_ENV from "@/constants/environment"; import { signOutFromSSO } from "@/utils/authenticationHelpers"; -import { isAuthenticated, getUserInfo } from "@/selectors/authenticationSelectors"; +import { isAuthenticated, getUserInfo } from "@mds/common/redux/selectors/authenticationSelectors"; import { MENU } from "@/constants/assets"; import AuthorizationWrapper from "../common/wrappers/AuthorizationWrapper"; import LoginButton from "../common/LoginButton"; diff --git a/services/minespace-web/src/components/modalContent/config.js b/services/minespace-web/src/components/modalContent/config.js index 3d1162e54c..fa08fc2c93 100644 --- a/services/minespace-web/src/components/modalContent/config.js +++ b/services/minespace-web/src/components/modalContent/config.js @@ -17,6 +17,7 @@ import ArchiveDocumentModal from "@common/components/documents/ArchiveDocumentMo import DeleteDocumentModal from "@mds/common/components/documents/DeleteDocumentModal"; import ReplaceDocumentModal from "@common/components/documents/ReplaceDocumentModal"; import CreateInvitationModal from "@/components/modalContent/verifiableCredentials/createInvitationModal"; +import ExplosivesPermitViewModal from "@mds/common/components/explosivespermits/ExplosivesPermitViewModal"; export const modalConfig = { ADD_REPORT: AddReportModal, @@ -38,6 +39,7 @@ export const modalConfig = { VIEW_FILE_HISTORY: ViewFileHistoryModal, UPLOAD_INCIDENT_DOCUMENT: UploadIncidentDocumentModal, VC_WALLET_INVITATION: CreateInvitationModal, + EXPLOSIVES_PERMIT_VIEW_MODAL: ExplosivesPermitViewModal, }; export default modalConfig; diff --git a/services/minespace-web/src/components/modalContent/informationRequirementsTable/ViewFileHistoryModal.js b/services/minespace-web/src/components/modalContent/informationRequirementsTable/ViewFileHistoryModal.js index a99d4b5971..d583c676ae 100644 --- a/services/minespace-web/src/components/modalContent/informationRequirementsTable/ViewFileHistoryModal.js +++ b/services/minespace-web/src/components/modalContent/informationRequirementsTable/ViewFileHistoryModal.js @@ -7,7 +7,7 @@ import { renderCategoryColumn, renderDateColumn, renderTextColumn, -} from "@/components/common/CoreTableCommonColumns"; +} from "@mds/common/components/common/CoreTableCommonColumns"; import { formatDateTime } from "@common/utils/helpers"; import { MineDocument } from "@mds/common/models/documents/document"; import { documentNameColumn } from "@/components/common/DocumentColumns"; diff --git a/services/minespace-web/src/components/pages/LandingPage.tsx b/services/minespace-web/src/components/pages/LandingPage.tsx index cfe5dad6bb..6f12e69076 100644 --- a/services/minespace-web/src/components/pages/LandingPage.tsx +++ b/services/minespace-web/src/components/pages/LandingPage.tsx @@ -7,7 +7,7 @@ import * as COMMON_ENV from "@mds/common"; // Uncomment when image is re-introduced // import { MAP_LOGO } from "@/constants/assets"; import * as MINESPACE_ENV from "@/constants/environment"; -import { isAuthenticated } from "@/selectors/authenticationSelectors"; +import { isAuthenticated } from "@mds/common/redux/selectors/authenticationSelectors"; import { AuthorizationWrapper } from "@/components/common/wrappers/AuthorizationWrapper"; import LoginButton from "../common/LoginButton"; diff --git a/services/minespace-web/src/components/pages/MinesPage.js b/services/minespace-web/src/components/pages/MinesPage.js index 70c4d4bd08..56ab41cbf8 100644 --- a/services/minespace-web/src/components/pages/MinesPage.js +++ b/services/minespace-web/src/components/pages/MinesPage.js @@ -5,7 +5,7 @@ import EnvironmentOutlined from "@ant-design/icons/EnvironmentOutlined"; import { Row, Col, Divider, Typography } from "antd"; import PropTypes from "prop-types"; import { Link } from "react-router-dom"; -import { getUserInfo, isProponent } from "@/selectors/authenticationSelectors"; +import { getUserInfo, isProponent } from "@mds/common/redux/selectors/authenticationSelectors"; import { getUserMineInfo } from "@/selectors/userMineSelectors"; import { fetchUserMineInfo } from "@/actionCreators/userDashboardActionCreator"; import CustomPropTypes from "@/customPropTypes"; diff --git a/services/minespace-web/src/components/pages/Project/DocumentsTab.js b/services/minespace-web/src/components/pages/Project/DocumentsTab.js index 18160ce2bc..18271c561a 100644 --- a/services/minespace-web/src/components/pages/Project/DocumentsTab.js +++ b/services/minespace-web/src/components/pages/Project/DocumentsTab.js @@ -14,7 +14,7 @@ import ArchivedDocumentsSection from "@common/components/documents/ArchivedDocum import { documentNameColumn, uploadDateColumn } from "@/components/common/DocumentColumns"; import { Feature } from "@mds/common"; import { MajorMineApplicationDocument } from "@mds/common/models/documents/document"; -import { renderCategoryColumn } from "@/components/common/CoreTableCommonColumns"; +import { renderCategoryColumn } from "@mds/common/components/common/CoreTableCommonColumns"; import * as Strings from "@mds/common/constants/strings"; import withFeatureFlag from "@mds/common/providers/featureFlags/withFeatureFlag"; diff --git a/services/minespace-web/src/components/pages/ReturnPage.js b/services/minespace-web/src/components/pages/ReturnPage.js index 0f8f202f63..d4e33dc19a 100644 --- a/services/minespace-web/src/components/pages/ReturnPage.js +++ b/services/minespace-web/src/components/pages/ReturnPage.js @@ -4,7 +4,7 @@ import { bindActionCreators } from "redux"; import { connect } from "react-redux"; import { Redirect } from "react-router-dom"; import queryString from "query-string"; -import { getRedirect, isAuthenticated } from "@/selectors/authenticationSelectors"; +import { getRedirect, isAuthenticated } from "@mds/common/redux/selectors/authenticationSelectors"; import { unAuthenticateUser } from "@/actionCreators/authenticationActionCreator"; import { signOutFromSSO } from "@/utils/authenticationHelpers"; import { RETURN_PAGE_TYPE } from "@/constants/strings"; diff --git a/services/minespace-web/src/components/pages/UsersPage.js b/services/minespace-web/src/components/pages/UsersPage.js index 883a618697..ccaf2d2ba5 100644 --- a/services/minespace-web/src/components/pages/UsersPage.js +++ b/services/minespace-web/src/components/pages/UsersPage.js @@ -3,7 +3,7 @@ import { connect } from "react-redux"; import { bindActionCreators } from "redux"; import { Row, Col, Divider, Typography } from "antd"; import PropTypes from "prop-types"; -import { getUserInfo } from "@/selectors/authenticationSelectors"; +import { getUserInfo } from "@mds/common/redux/selectors/authenticationSelectors"; import Loading from "@/components/common/Loading"; const propTypes = { diff --git a/services/minespace-web/src/constants/routes.js b/services/minespace-web/src/constants/routes.js index 329101f79e..b5c4ea2791 100755 --- a/services/minespace-web/src/constants/routes.js +++ b/services/minespace-web/src/constants/routes.js @@ -1,5 +1,6 @@ import React from "react"; import queryString from "query-string"; +import ExplosivesPermit from "@/components/dashboard/mine/permits/ExplosivesPermit"; const DamsPage = React.lazy(() => import("@common/components/tailings/dam/DamsPage")); const InformationRequirementsTablePage = React.lazy(() => import("@/components/pages/Project/InformationRequirementsTablePage") @@ -186,3 +187,10 @@ export const EDIT_DAM = { `/mine/${mineGuid}/tailings-storage-facility/${tailingsStorageFacilityGuid}/dam/${damGuid}`, component: DamsPage, }; + +export const VIEW_ESUP = { + route: "/mine/:mineGuid/explosives-permits/:explosivesPermitGuid", + dynamicRoute: (mineGuid, explosivesPermitGuid) => + `/mine/${mineGuid}/explosives-permits/${explosivesPermitGuid}`, + component: ExplosivesPermit, +}; diff --git a/services/minespace-web/src/reducers/authenticationReducer.js b/services/minespace-web/src/reducers/authenticationReducer.js deleted file mode 100644 index 80098f7b0d..0000000000 --- a/services/minespace-web/src/reducers/authenticationReducer.js +++ /dev/null @@ -1,62 +0,0 @@ -import * as ActionTypes from "@/constants/actionTypes"; -import * as ReducerTypes from "@/constants/reducerTypes"; -import * as route from "@/constants/routes"; -/** - * @file authenticationReducer.js - * all data associated with a users record is handled within this reducer. - */ -const initialState = { - isAuthenticated: undefined, - userInfo: {}, - redirect: false, - isProponent: undefined, -}; - -const getUserName = (tokenParsed) => { - const { bceid_username } = tokenParsed; - if (bceid_username && bceid_username.length > 0) { - return `${bceid_username}@bceid`; - } - if (tokenParsed.idir_username) { - return tokenParsed.idir_username; - } - return tokenParsed.preferred_username; -}; - -const authenticationReducer = (state = initialState, action) => { - switch (action.type) { - case ActionTypes.AUTHENTICATE_USER: - const tokenParsed = action.payload.userInfo; - const preferred_username = getUserName(tokenParsed); - return { - ...state, - isAuthenticated: true, - userInfo: { - ...action.payload.userInfo, - preferred_username, - }, - redirect: route.MINES.route, - }; - case ActionTypes.LOGOUT: - return { - ...state, - isAuthenticated: false, - userInfo: {}, - redirect: route.HOME.route, - }; - case ActionTypes.STORE_IS_PROPONENT: - return { - ...state, - isProponent: action.payload.data, - }; - default: - return state; - } -}; - -export const isAuthenticated = (state) => state[ReducerTypes.AUTHENTICATION].isAuthenticated; -export const getUserInfo = (state) => state[ReducerTypes.AUTHENTICATION].userInfo; -export const getRedirect = (state) => state[ReducerTypes.AUTHENTICATION].redirect; -export const isProponent = (state) => state[ReducerTypes.AUTHENTICATION].isProponent; - -export default authenticationReducer; diff --git a/services/minespace-web/src/reducers/rootReducer.ts b/services/minespace-web/src/reducers/rootReducer.ts index 1a7efe0ff9..bc366cd878 100644 --- a/services/minespace-web/src/reducers/rootReducer.ts +++ b/services/minespace-web/src/reducers/rootReducer.ts @@ -1,5 +1,4 @@ import { combineReducers } from "redux"; -import authenticationReducer from "@/reducers/authenticationReducer"; import userMineReducer from "@/reducers/userMineReducer"; import networkReducer from "./networkReducer"; import * as reducerTypes from "../constants/reducerTypes"; @@ -8,7 +7,6 @@ import { sharedReducer } from "@mds/common/redux/reducers/rootReducerShared"; const minespaceReducer = { ...sharedReducer, - [reducerTypes.AUTHENTICATION]: authenticationReducer, [reducerTypes.USER_MINE_INFO]: userMineReducer, [reducerTypes.GET_USER_MINE_INFO]: createReducer(networkReducer, reducerTypes.GET_USER_MINE_INFO), [reducerTypes.AUTHENTICATE_USER]: createReducer(networkReducer, reducerTypes.AUTHENTICATE_USER), diff --git a/services/minespace-web/src/routes/Routes.js b/services/minespace-web/src/routes/Routes.js index d5babd5020..e542909087 100644 --- a/services/minespace-web/src/routes/Routes.js +++ b/services/minespace-web/src/routes/Routes.js @@ -111,6 +111,10 @@ const Routes = () => ( path={routes.EDIT_MINE_INCIDENT.route} component={AuthenticationGuard()(routes.EDIT_MINE_INCIDENT.component)} /> + {/* 404 - PAGE NOT FOUND */} ( diff --git a/services/minespace-web/src/selectors/authenticationSelectors.js b/services/minespace-web/src/selectors/authenticationSelectors.js deleted file mode 100644 index e11e7bb7f9..0000000000 --- a/services/minespace-web/src/selectors/authenticationSelectors.js +++ /dev/null @@ -1,3 +0,0 @@ -import * as authenticationReducer from "@/reducers/authenticationReducer"; - -export const { isAuthenticated, getUserInfo, getRedirect, isProponent } = authenticationReducer; diff --git a/services/minespace-web/src/setupTests.js b/services/minespace-web/src/setupTests.js index 64fd7c391a..4585d76142 100755 --- a/services/minespace-web/src/setupTests.js +++ b/services/minespace-web/src/setupTests.js @@ -6,7 +6,10 @@ require("jest-localstorage-mock"); Enzyme.configure({ adapter: new Adapter() }); +// eslint-disable-next-line @typescript-eslint/no-var-requires global.REQUEST_HEADER = require(path.resolve(__dirname, "../common/utils/RequestHeaders.js")); +// eslint-disable-next-line @typescript-eslint/no-var-requires +global.GLOBAL_ROUTES = require(path.resolve(__dirname, "./constants/routes.js")); global.requestAnimationFrame = (callback) => { setTimeout(callback, 0); diff --git a/services/minespace-web/src/styles/components/Tables.scss b/services/minespace-web/src/styles/components/Tables.scss index b043f31ecb..1fe9484f0a 100644 --- a/services/minespace-web/src/styles/components/Tables.scss +++ b/services/minespace-web/src/styles/components/Tables.scss @@ -36,12 +36,12 @@ display: none; } - .ant-table-tbody>tr { + .ant-table-tbody > tr { display: block; border-top: 2px solid $background; } - .ant-table-tbody>tr>td { + .ant-table-tbody > tr > td { display: block !important; padding: 0.25rem 0.5rem; border-bottom: none; @@ -70,11 +70,17 @@ } } - .ant-table-tbody>tr>td:last-child { + .ant-table-tbody > tr > td:last-child { border-bottom: 0; } } +.break-word { + word-break: break-word !important; + overflow: initial !important; + white-space: initial !important; +} + // buttons within tables td.ant-table-cell { vertical-align: middle; @@ -83,4 +89,20 @@ td.ant-table-cell { margin-top: 0; margin-bottom: 0; } -} \ No newline at end of file +} + +.diff-table th { + background-color: #F4F0F0 !important; +} + +.diff-table td { + background-color: #fff !important; +} + +.diff-table-row, .diff-table-row:hover, .ant-table-cell-row-hover { + background: none; +} + +.diff-table-row > td{ + vertical-align: top; +} diff --git a/services/minespace-web/src/styles/elements/elements.scss b/services/minespace-web/src/styles/elements/elements.scss index eaa8a3694a..a5add15daa 100644 --- a/services/minespace-web/src/styles/elements/elements.scss +++ b/services/minespace-web/src/styles/elements/elements.scss @@ -1,3 +1,7 @@ +.primary-colour { + color: $gov-blue !important; +} + a { &[disabled] { pointer-events: all; diff --git a/services/minespace-web/src/styles/generic/layout.scss b/services/minespace-web/src/styles/generic/layout.scss index bc5e724f9d..874e02feb6 100644 --- a/services/minespace-web/src/styles/generic/layout.scss +++ b/services/minespace-web/src/styles/generic/layout.scss @@ -67,6 +67,10 @@ width: 100%; } +.line-height-none { + line-height: 1; +} + .padding-sm { &--left { padding-left: $default-padding-sm; @@ -156,6 +160,10 @@ padding: $default-padding-xxl; } +.margin-none { + margin: 0 !important; +} + .margin-small { margin: $default-margin-sm; &--top { @@ -244,6 +252,10 @@ color: $gov-red; } +.green { + color: $ok-green; +} + .light-grey-background { background-color: $light-grey; } diff --git a/services/minespace-web/src/styles/overrides/antd-overrides.scss b/services/minespace-web/src/styles/overrides/antd-overrides.scss index e736a76057..00a12c2eea 100644 --- a/services/minespace-web/src/styles/overrides/antd-overrides.scss +++ b/services/minespace-web/src/styles/overrides/antd-overrides.scss @@ -521,4 +521,45 @@ th.ant-descriptions-item.vertical-description { .ant-popover-message-title { max-width: 400px; +} + +.ant-alert-grey { + background-color: $lightest-grey; + border: 1px solid $medium-grey; + + span>svg { + fill: #6b6363; + background-color: #f4f0f0; + } + + .ant-alert-content .ant-alert-description div { + position: relative; + + button.ant-btn { + position: absolute; + right: 0; + top: -75%; + + border-width: 2px; + background-color: transparent; + + &:focus, + &:hover { + background-color: white; + ; + } + } + } +} + +.magazine-collapse, +.magazine-collapse > div { + background-color: #f2f2f2; + align-items: center !important; + border-bottom: none !important; +} + +.ant-modal-footer { + padding-right: 24px; + padding-bottom: 24px; } \ No newline at end of file diff --git a/services/minespace-web/src/tests/components/Forms/projects/majorMineApplication/__snapshots__/MajorMineApplicationForm.spec.js.snap b/services/minespace-web/src/tests/components/Forms/projects/majorMineApplication/__snapshots__/MajorMineApplicationForm.spec.js.snap index 567d4ba254..ec21f06114 100644 --- a/services/minespace-web/src/tests/components/Forms/projects/majorMineApplication/__snapshots__/MajorMineApplicationForm.spec.js.snap +++ b/services/minespace-web/src/tests/components/Forms/projects/majorMineApplication/__snapshots__/MajorMineApplicationForm.spec.js.snap @@ -269,6 +269,7 @@ exports[`MajorMineApplicationForm renders properly 1`] = ` additionalColumns={ Array [ Object { + "className": undefined, "dataIndex": "category_code", "key": "category_code", "render": [Function], diff --git a/services/minespace-web/src/tests/components/Forms/projects/majorMineApplication/__snapshots__/MajorMineApplicationReviewSubmit.spec.js.snap b/services/minespace-web/src/tests/components/Forms/projects/majorMineApplication/__snapshots__/MajorMineApplicationReviewSubmit.spec.js.snap index d0ccaa2802..874f7156a6 100644 --- a/services/minespace-web/src/tests/components/Forms/projects/majorMineApplication/__snapshots__/MajorMineApplicationReviewSubmit.spec.js.snap +++ b/services/minespace-web/src/tests/components/Forms/projects/majorMineApplication/__snapshots__/MajorMineApplicationReviewSubmit.spec.js.snap @@ -140,6 +140,7 @@ exports[`MajorMineApplicationReviewSubmit renders properly 1`] = ` additionalColumns={ Array [ Object { + "className": undefined, "dataIndex": "category_code", "key": "category_code", "render": [Function], diff --git a/services/minespace-web/src/tests/components/Forms/projects/projectSummary/__snapshots__/DocumentUpload.spec.js.snap b/services/minespace-web/src/tests/components/Forms/projects/projectSummary/__snapshots__/DocumentUpload.spec.js.snap index 3ccbb5a797..3d22592b05 100644 --- a/services/minespace-web/src/tests/components/Forms/projects/projectSummary/__snapshots__/DocumentUpload.spec.js.snap +++ b/services/minespace-web/src/tests/components/Forms/projects/projectSummary/__snapshots__/DocumentUpload.spec.js.snap @@ -43,6 +43,7 @@ exports[`DocumentUpload renders properly 1`] = ` "title": "File Name", }, Object { + "className": undefined, "dataIndex": "project_summary_document_type_code", "key": "project_summary_document_type_code", "render": [Function], diff --git a/services/minespace-web/src/tests/components/dashboard/mine/variances/__snapshots__/VarianceDetails.spec.js.snap b/services/minespace-web/src/tests/components/dashboard/mine/variances/__snapshots__/VarianceDetails.spec.js.snap index d3bd512355..b15f16c04f 100644 --- a/services/minespace-web/src/tests/components/dashboard/mine/variances/__snapshots__/VarianceDetails.spec.js.snap +++ b/services/minespace-web/src/tests/components/dashboard/mine/variances/__snapshots__/VarianceDetails.spec.js.snap @@ -39,6 +39,7 @@ exports[`VarianceDetails renders properly 1`] = ` "title": "File Name", }, Object { + "className": undefined, "dataIndex": "category", "key": "category", "render": [Function], diff --git a/services/minespace-web/src/tests/routes/__snapshots__/Routes.spec.js.snap b/services/minespace-web/src/tests/routes/__snapshots__/Routes.spec.js.snap index c90dd51657..e8dbd97e2f 100644 --- a/services/minespace-web/src/tests/routes/__snapshots__/Routes.spec.js.snap +++ b/services/minespace-web/src/tests/routes/__snapshots__/Routes.spec.js.snap @@ -368,6 +368,18 @@ exports[`PrivateRoutes renders properly 1`] = ` } path="/mines/:mineGuid/incidents/:mineIncidentGuid" /> + diff --git a/services/minespace-web/src/tests/selectors/authenticationSelectors.spec.js b/services/minespace-web/src/tests/selectors/authenticationSelectors.spec.js index 002c456589..f57110f892 100644 --- a/services/minespace-web/src/tests/selectors/authenticationSelectors.spec.js +++ b/services/minespace-web/src/tests/selectors/authenticationSelectors.spec.js @@ -1,8 +1,12 @@ -import { isAuthenticated, getUserInfo, getRedirect } from "@/selectors/authenticationSelectors"; -import authenticationReducer from "@/reducers/authenticationReducer"; +import { + isAuthenticated, + getUserInfo, + getRedirect, +} from "@mds/common/redux/selectors/authenticationSelectors"; import { authenticateUser, logoutUser } from "@/actions/authenticationActions"; import { AUTHENTICATION } from "@/constants/reducerTypes"; import * as route from "@/constants/routes"; +import { authenticationReducer } from "@mds/common/redux/reducers/authenticationReducer"; const mockData = { isAuthenticated: true, diff --git a/services/minespace-web/tsconfig.json b/services/minespace-web/tsconfig.json index 526c5e059f..1d342f5847 100644 --- a/services/minespace-web/tsconfig.json +++ b/services/minespace-web/tsconfig.json @@ -5,18 +5,10 @@ "jsx": "react", "outDir": "build" /* Redirect output structure to the directory. */, "paths": { - "@common/*": [ - "common/*" - ], - "@mds/common": [ - "../common/src" - ], - "@mds/common/*": [ - "../common/src/*" - ], - "@/*": [ - "src/*" - ] + "@common/*": ["common/*"], + "@mds/common": ["../common/src"], + "@mds/common/*": ["../common/src/*"], + "@/*": ["src/*"] }, "module": "ESNext", "moduleResolution": "node", @@ -41,9 +33,5 @@ "../../.eslintrc.js", "../common/src/@Types/**/*" ], - "exclude": [ - "node_modules", - "src/**/*.test.ts", - "src/**/*.test.tsx" - ] -} \ No newline at end of file + "exclude": ["node_modules", "src/**/*.test.ts", "src/**/*.test.tsx"] +} diff --git a/services/minespace-web/webpack.config.ts b/services/minespace-web/webpack.config.ts index 8459b11c3e..2720b39082 100755 --- a/services/minespace-web/webpack.config.ts +++ b/services/minespace-web/webpack.config.ts @@ -87,6 +87,7 @@ const commonConfig = merge([ }), new webpack.ProvidePlugin({ REQUEST_HEADER: path.resolve(__dirname, "common/utils/RequestHeaders.js"), + GLOBAL_ROUTES: path.resolve(__dirname, "src/constants/routes.js"), }), // Prevent moment locales to be bundled with the app // to reduce app size