From 9d8973e0c99b705714634e8d0d232e8f541ccca4 Mon Sep 17 00:00:00 2001 From: LGTM Migrator Date: Mon, 7 Nov 2022 16:19:27 +0000 Subject: [PATCH 1/2] Add CodeQL workflow for GitHub code scanning --- .github/workflows/codeql.yml | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..6a0e7bd --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,41 @@ +name: "CodeQL" + +on: + push: + branches: [ "main", "develop" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: "39 17 * * 0" + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ javascript ] + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + queries: +security-and-quality + + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + with: + category: "/language:${{ matrix.language }}" From 5f1e4355be4bf772ac009459fa0e875e5a1b711e Mon Sep 17 00:00:00 2001 From: Patrick Delcroix Date: Thu, 15 Dec 2022 10:43:36 +0100 Subject: [PATCH 2/2] fixes (#19) * OP-824: Fixed locations dry run * OP-898: Add confirmation popup to XML upload * OP-898: Remove unused variable --- .github/workflows/npmpublish.yml | 17 +- package.json | 14 +- src/components/ToolsMainMenu.js | 14 +- src/pages/ExtractsPage.js | 281 +++++++++++++++++++++++++++---- src/pages/RegistersPage.js | 126 +++++++++++--- src/translations/en.json | 23 +++ 6 files changed, 404 insertions(+), 71 deletions(-) diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml index 2336d08..435edf0 100644 --- a/.github/workflows/npmpublish.yml +++ b/.github/workflows/npmpublish.yml @@ -5,29 +5,38 @@ name: Node.js Package on: release: - types: [created] + types: [published] jobs: build: runs-on: ubuntu-latest steps: + - uses: olegtarasov/get-tag@v2.1 + id: tagName - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 12 + - name: update the version with TAG + run: echo $(jq --arg a "$GIT_TAG_NAME" '.version = ($a)' package.json) > package.json - run: yarn install - - run: yarn build + - run: yarn build + publish-npm: needs: build runs-on: ubuntu-latest steps: + - uses: olegtarasov/get-tag@v2.1 + id: tagName - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 12 registry-url: https://registry.npmjs.org/ scope: openimis + - name: update the version with TAG + run: echo $(jq --arg a "$GIT_TAG_NAME" '.version = ($a)' package.json) > package.json - run: yarn install - run: yarn build - run: npm publish --access public @@ -38,11 +47,15 @@ jobs: needs: build runs-on: ubuntu-latest steps: + - uses: olegtarasov/get-tag@v2.1 + id: tagName - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: node-version: 12 registry-url: https://npm.pkg.github.com/ + - name: update the version with TAG + run: echo $(jq --arg a "$GIT_TAG_NAME" '.version = ($a)' package.json) > package.json - run: yarn install - run: yarn build - run: npm publish diff --git a/package.json b/package.json index b12db57..128ac47 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@openimis/fe-tools", - "version": "1.5.0", + "version": "1.5.1", "license": "AGPL-3.0-only", "description": "openIMIS Frontend Tools reference module", "repository": "openimis/openimis-fe-tools_js", @@ -12,12 +12,12 @@ }, "scripts": { "build": "rollup -c", - "start": "rollup -c -w" + "start": "rollup -c -w", + "prepare":"npm run build" }, "peerDependency": { "react-intl": "^5.8.1", - "react": "^17.0.2", - "react-router-dom": "^5.2.0" + "react": "^17.0.2" }, "devDependencies": { "@babel/cli": "^7.8.4", @@ -35,12 +35,8 @@ "prop-types": "^15.7.2", "clsx": "^1.1.1", "rollup": "^2.10.0", - "@material-ui/core": "^4.9.14", - "@material-ui/icons": "^4.9.1", - "@openimis/fe-core": "^1.4.0-rc3", "moment": "^2.25.3", - "react-autosuggest": "^10.0.2", - "react-router-dom": "^5.2.0" + "react-autosuggest": "^10.0.2" }, "files": [ "dist" diff --git a/src/components/ToolsMainMenu.js b/src/components/ToolsMainMenu.js index be12bb5..9fedd64 100644 --- a/src/components/ToolsMainMenu.js +++ b/src/components/ToolsMainMenu.js @@ -1,9 +1,9 @@ -import { Ballot, ImportExport, Settings } from "@material-ui/icons"; +import {Ballot, ImportExport, SaveAlt, Settings} from "@material-ui/icons"; import { formatMessage, MainMenuContribution, withModulesManager } from "@openimis/fe-core"; import React, { Component } from "react"; import { injectIntl } from "react-intl"; import { connect } from "react-redux"; -import { RIGHT_REGISTERS, RIGHT_REPORTS } from "../constants"; +import { RIGHT_REGISTERS, RIGHT_REPORTS, RIGHT_EXTRACTS } from "../constants"; class ToolsMainMenu extends Component { enablers = (enablers) => { @@ -37,11 +37,11 @@ class ToolsMainMenu extends Component { // ) // } // Extracts page is not implemented in the modular version yet. As it's not used as of now. - // if (this.enablers(RIGHT_EXTRACTS)) { - // entries.push( - // { text: formatMessage(this.props.intl, "tools", "menu.extracts"), icon: , route: "/tools/extracts" }, - // ) - // } + if (this.enablers(RIGHT_EXTRACTS)) { + entries.push( + { text: formatMessage(this.props.intl, "tools", "menu.extracts"), icon: , route: "/tools/extracts" }, + ) + } if (this.enablers(RIGHT_REPORTS)) { entries.push({ text: formatMessage(this.props.intl, "tools", "menu.reports"), diff --git a/src/pages/ExtractsPage.js b/src/pages/ExtractsPage.js index 226abc5..63389ac 100644 --- a/src/pages/ExtractsPage.js +++ b/src/pages/ExtractsPage.js @@ -7,13 +7,16 @@ import { baseApiUrl, apiHeaders, ProgressOrError, - ProxyPage, + decodeId, } from "@openimis/fe-core"; import { useSelector } from "react-redux"; -import { Box, Grid, Paper, Button, Input, Dialog, DialogContent, DialogTitle, DialogActions } from "@material-ui/core"; +import { Box, Grid, Button, Input, Dialog, DialogContent, DialogTitle, DialogActions } from "@material-ui/core"; +import { People, Autorenew as RenewIcon, Keyboard } from "@material-ui/icons"; +import FeedbackIcon from "@material-ui/icons/SpeakerNotesOutlined"; import Block from "../components/Block"; import { RIGHT_EXTRACTS } from "../constants"; +import {string} from "prop-types"; const EXTRACTS_URL = `${baseApiUrl}/tools/extracts`; @@ -21,6 +24,12 @@ const OfficerDownloadBlock = (props) => { const modulesManager = useModulesManager(); const { formatMessage } = useTranslations("tools.ExtractsPage", modulesManager); const [officer, setOfficer] = useState(); + const onExtractDownload = (extract, params) => (e) => { + const stringParams = Object.keys(params).map((k)=>`${k}=${encodeURIComponent(params[k])}`)?.join("&") + return window.open(`${EXTRACTS_URL}/download_${extract}${stringParams ? `?${stringParams}` : ""}`); + } + const officer_id = officer ? decodeId(officer.id) : ""; + return ( @@ -28,17 +37,20 @@ const OfficerDownloadBlock = (props) => { - - @@ -56,7 +68,7 @@ const ResultDialog = ({ open, title, isLoading, children, onClose }) => { {title} {children} - @@ -121,8 +133,206 @@ const ClaimsUploadBlock = (props) => { /> - + + + + ); +}; + +const EnrollmentsUploadBlock = (props) => { + const modulesManager = useModulesManager(); + const { formatMessage } = useTranslations("tools.ExtractsPage", modulesManager); + const [files, setFiles] = useState(); + const [request, setRequest] = useState(); + const onSubmit = async () => { + setRequest({ isLoading: true }); + const formData = new FormData(); + for (let i = 0; i < files.length; i++) { + const f = files.item(i); + formData.append(f.name, f); + } + try { + const response = await fetch(`${EXTRACTS_URL}/upload_enrollments`, { + headers: apiHeaders, + body: formData, + method: "POST", + credentials: "same-origin", + }); + if (response.status >= 400) { + throw new Error("Unknown error"); + } + const payload = await response.json(); + setRequest({ isLoading: false, error: null, payload }); + } catch (exc) { + console.error(exc); + setRequest({ isLoading: false, error: exc.message || formatMessage("EnrollmentsUploadBlock.errorMessage") }); + } finally { + setFiles(null); + } + }; + + return ( + + {request && ( + setRequest(undefined)} + > + + {request?.payload?.success && formatMessage("EnrollmentsUploadBlock.ResultDialog.success")} + + )} + + + setFiles(event.target.files)} + required + multiple + inputProps={{ + accept: ".zip, .rar, .xml, application/zip, application/xml, text/xml", + }} + type="file" + /> + + + + + + + ); +}; + +const RenewalsUploadBlock = (props) => { + const modulesManager = useModulesManager(); + const { formatMessage } = useTranslations("tools.ExtractsPage", modulesManager); + const [files, setFiles] = useState(); + const [request, setRequest] = useState(); + const onSubmit = async () => { + setRequest({ isLoading: true }); + const formData = new FormData(); + for (let i = 0; i < files.length; i++) { + const f = files.item(i); + formData.append(f.name, f); + } + try { + const response = await fetch(`${EXTRACTS_URL}/upload_renewals`, { + headers: apiHeaders, + body: formData, + method: "POST", + credentials: "same-origin", + }); + if (response.status >= 400) { + throw new Error("Unknown error"); + } + const payload = await response.json(); + setRequest({ isLoading: false, error: null, payload }); + } catch (exc) { + console.error(exc); + setRequest({ isLoading: false, error: exc.message || formatMessage("RenewalsUploadBlock.errorMessage") }); + } finally { + setFiles(null); + } + }; + + return ( + + {request && ( + setRequest(undefined)} + > + + {request?.payload?.success && formatMessage("RenewalsUploadBlock.ResultDialog.success")} + + )} + + + setFiles(event.target.files)} + required + multiple + inputProps={{ + accept: ".zip, .rar, .xml, application/zip, application/xml, text/xml", + }} + type="file" + /> + + + + + + + ); +}; + +const FeedbacksUploadBlock = (props) => { + const modulesManager = useModulesManager(); + const { formatMessage } = useTranslations("tools.ExtractsPage", modulesManager); + const [files, setFiles] = useState(); + const [request, setRequest] = useState(); + const onSubmit = async () => { + setRequest({ isLoading: true }); + const formData = new FormData(); + for (let i = 0; i < files.length; i++) { + const f = files.item(i); + formData.append(f.name, f); + } + try { + const response = await fetch(`${EXTRACTS_URL}/upload_feedbacks`, { + headers: apiHeaders, + body: formData, + method: "POST", + credentials: "same-origin", + }); + if (response.status >= 400) { + throw new Error("Unknown error"); + } + const payload = await response.json(); + setRequest({ isLoading: false, error: null, payload }); + } catch (exc) { + console.error(exc); + setRequest({ isLoading: false, error: exc.message || formatMessage("FeedbacksUploadBlock.errorMessage") }); + } finally { + setFiles(null); + } + }; + + return ( + + {request && ( + setRequest(undefined)} + > + + {request?.payload?.success && formatMessage("FeedbacksUploadBlock.ResultDialog.success")} + + )} + + + setFiles(event.target.files)} + required + multiple + inputProps={{ + accept: ".zip, .rar, .xml, application/zip, application/xml, text/xml", + }} + type="file" + /> + + + @@ -138,30 +348,41 @@ const ExtractsPage = (props) => { if (!RIGHT_EXTRACTS.every((x) => rights.includes(x))) { return null; } + const EXTRACTS_URL = `${baseApiUrl}/tools/extracts`; - return ; - - // return ( - // <> - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // ); + const onExtractDownload = (extract) => (e) => window.open(`${EXTRACTS_URL}/download_${extract}`); + + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); }; export { ExtractsPage }; diff --git a/src/pages/RegistersPage.js b/src/pages/RegistersPage.js index d11b2b9..d90017f 100644 --- a/src/pages/RegistersPage.js +++ b/src/pages/RegistersPage.js @@ -14,7 +14,7 @@ import { Dialog, DialogContent, DialogTitle, - DialogActions, + DialogActions } from "@material-ui/core"; import { STRATEGY_INSERT, @@ -36,6 +36,7 @@ const RegistersPage = () => { const [forms, setForms] = useState({}); const rights = useSelector((state) => state.core?.user?.i_user?.rights ?? []); const [dialogState, setDialogState] = useState({}); + const [popupState, setPopupState] = useState({}); const hasRights = (rightsList) => rightsList.every((x) => rights.includes(x)); @@ -52,6 +53,29 @@ const RegistersPage = () => { const REGISTERS_URL = `${baseApiUrl}/tools/registers`; const onRegisterDownload = (register) => (e) => window.open(`${REGISTERS_URL}/download_${register}`); + + const openPopup = (e, uploadType) => { + setPopupState({ + open: true, + openLocations: uploadType === 'locations', + openDiagnosis: uploadType === 'diagnosis', + openHF: uploadType === 'hf', + anchorEl: e.currentTarget, + error: null, + }); + } + + const onPopupClose = (e) => { + setPopupState({ + open: false, + openLocations: false, + openDiagnosis: false, + openHF: false, + anchorEl: null, + error: null, + }); + } + const onDialogClose = (reason) => { if (reason === "escapeKeyDown" || reason === "backdropClick") { return; @@ -66,6 +90,11 @@ const RegistersPage = () => { data: null, error: null, }); + setPopupState({ + open: false, + anchorEl: null, + error: null, + }); const formData = new FormData(); formData.append("dry_run", Boolean(values.dryRun)); formData.append("file", values.file); @@ -100,7 +129,7 @@ const RegistersPage = () => { }); } }; - + console.log("TOOLS RENDERING") return ( <> {dialogState?.open && ( @@ -203,14 +232,31 @@ const RegistersPage = () => { /> - + + {popupState?.open && popupState?.openDiagnosis && ( + + {formatMessage("UploadDialog.confirmDiagnoses")} + + + + + )} @@ -264,20 +310,37 @@ const RegistersPage = () => { control={ handleFieldChange("diagnoses", "dryRun", e.target.checked)} + onChange={(e) => handleFieldChange("locations", "dryRun", e.target.checked)} /> } /> - + {formatMessage("uploadBtn")} + + {popupState?.open && popupState?.openLocations && ( + + {formatMessage("UploadDialog.confirmLocations")} + + + + + )} @@ -337,14 +400,31 @@ const RegistersPage = () => { /> - + + {popupState?.open && popupState?.openHF && ( + + {formatMessage("UploadDialog.confirmHF")} + + + + + )} diff --git a/src/translations/en.json b/src/translations/en.json index ed705cf..afd3a09 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -22,6 +22,10 @@ "tools.RegistersPage.diagnoses.uploadLabel": "Upload Diagnoses", "tools.RegistersPage.downloadBtn": "Download", "tools.RegistersPage.uploadBtn": "Upload", + "tools.RegistersPage.cancelBtn": "Cancel", + "tools.RegistersPage.UploadDialog.confirmLocations": "Are you sure you want to upload a new list of locations?", + "tools.RegistersPage.UploadDialog.confirmHF": "Are you sure you want to upload a new list of Health Facilities?", + "tools.RegistersPage.UploadDialog.confirmDiagnoses": "Are you sure you want to upload a new list of diagnoses?", "tools.RegistersPage.locationsBlockTitle": "Locations", "tools.RegistersPage.locations.downloadLabel": "Download Locations", "tools.RegistersPage.locations.uploadLabel": "Upload Locations", @@ -38,10 +42,29 @@ "tools.RegistersPage.UploadDialog.errors": "Errors:", "tools.RegistersPage.UploadDialog.deleted": "Deleted:", "tools.ExtractsPage.OfficerDownloadBlock.title": "Download Data for Officer", + "tools.ExtractsPage.OfficerDownloadBlock.officerLabel": "Select Officer", "tools.ExtractsPage.OfficerDownloadBlock.downloadFeedbacksBtn": "Download Feedbacks", "tools.ExtractsPage.OfficerDownloadBlock.downloadRenewalsBtn": "Download Renewals", + "tools.ExtractsPage.EnrollmentsUploadBlock.title": "Upload Enrollments", + "tools.ExtractsPage.EnrollmentsUploadBlock.uploadBtn": "Upload", + "tools.ExtractsPage.ClaimsUploadBlock.title": "Upload Claims", + "tools.ExtractsPage.ClaimsUploadBlock.uploadBtn": "Upload", + "tools.ExtractsPage.ClaimsUploadBlock.ResultDialog.okBtn": "OK", + "tools.ExtractsPage.ClaimsUploadBlock.ResultDialog.success": "Success", + "tools.ExtractsPage.ClaimsUploadBlock.ResultDialog.title": "Claims Upload Done", + "tools.ExtractsPage.FeedbacksUploadBlock.title": "Upload Feedbacks", + "tools.ExtractsPage.FeedbacksUploadBlock.uploadBtn": "Upload", + "tools.ExtractsPage.FeedbacksUploadBlock.ResultDialog.okBtn": "OK", + "tools.ExtractsPage.FeedbacksUploadBlock.ResultDialog.success": "Success", + "tools.ExtractsPage.FeedbacksUploadBlock.ResultDialog.title": "Feedbacks Upload Done", + "tools.ExtractsPage.RenewalsUploadBlock.title": "Upload Renewals", + "tools.ExtractsPage.RenewalsUploadBlock.uploadBtn": "Upload", + "tools.ExtractsPage.RenewalsUploadBlock.ResultDialog.okBtn": "OK", + "tools.ExtractsPage.RenewalsUploadBlock.ResultDialog.success": "Success", + "tools.ExtractsPage.RenewalsUploadBlock.ResultDialog.title": "Renewals Upload Done", "tools.ExtractsPage.DownloadMasterData.title": "Download Master Data", "tools.ExtractsPage.DownloadMasterData.downloadBtn": "Download", + "tools.ExtractsPage.ResultDialog.okBtn": "OK", "tools.ReportForm.title": "Editing report {name}", "tools.ReportForm.unknownReport": "Unknown report", "tools.ReportSearcher.tableTitle": "Reports",