diff --git a/.github/workflows/auto-testing-label.yml b/.github/workflows/auto-testing-label.yml
index 98cfd46dab3..6c6fc1002a0 100644
--- a/.github/workflows/auto-testing-label.yml
+++ b/.github/workflows/auto-testing-label.yml
@@ -38,7 +38,7 @@ jobs:
}
if (isChangesRequired) {
- await github.rest.issues.createComment({
+ await github.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml
index 1104d7f480d..0f588568bbc 100644
--- a/.github/workflows/cypress.yaml
+++ b/.github/workflows/cypress.yaml
@@ -1,6 +1,8 @@
name: Cypress Tests
on:
+ schedule:
+ - cron: "30 22 * * *"
pull_request:
branches:
- develop
@@ -15,7 +17,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- containers: [1, 2, 3, 4]
+ containers: [1, 2, 3, 4, 5, 6, 7, 8]
env:
REACT_CARE_API_URL: http://localhost:9000
steps:
@@ -134,4 +136,4 @@ jobs:
if: steps.pr_origin.outputs.is_forked == 'true'
with:
name: cypress-videos
- path: cypress/videos
\ No newline at end of file
+ path: cypress/videos
diff --git a/.github/workflows/generate-sbom.yml b/.github/workflows/generate-sbom.yml
deleted file mode 100644
index 4357fd735b1..00000000000
--- a/.github/workflows/generate-sbom.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-name: Generate SBOM using CycloneDX
-
-on:
- workflow_dispatch:
-
-jobs:
- generate-sbom:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout code
- uses: actions/checkout@v3
-
- - name: Set up Node.js
- uses: actions/setup-node@v3
- with:
- node-version: '20'
-
- - name: Install dependencies
- run: npm ci
-
- - name: Install CycloneDX NPM tool
- run: npm install -g @cyclonedx/cyclonedx-npm
-
- - name: Generate SBOM
- run: cyclonedx-npm --output-file sbom.json
-
- - name: Upload SBOM
- uses: actions/upload-artifact@v3
- with:
- name: sbom
- path: sbom.json
- if-no-files-found: error
diff --git a/src/components/Facility/ConsultationDetails/ConsultationNursingTab.tsx b/src/components/Facility/ConsultationDetails/ConsultationNursingTab.tsx
index 81078197a45..02604566869 100644
--- a/src/components/Facility/ConsultationDetails/ConsultationNursingTab.tsx
+++ b/src/components/Facility/ConsultationDetails/ConsultationNursingTab.tsx
@@ -4,43 +4,20 @@ import { useTranslation } from "react-i18next";
import Loading from "@/components/Common/Loading";
import PageTitle from "@/components/Common/PageTitle";
import Pagination from "@/components/Common/Pagination";
+import { ProcedureType } from "@/components/Common/prescription-builder/ProcedureBuilder";
import { ConsultationTabProps } from "@/components/Facility/ConsultationDetails/index";
-import { NursingPlot } from "@/components/Facility/Consultations/NursingPlot";
+import LogUpdateAnalayseTable from "@/components/Facility/Consultations/LogUpdateAnalayseTable";
import {
+ DailyRoundsRes,
+ NursingPlotFields,
RoutineAnalysisRes,
RoutineFields,
} from "@/components/Facility/models";
-import { PAGINATION_LIMIT } from "@/common/constants";
+import { NURSING_CARE_PROCEDURES, PAGINATION_LIMIT } from "@/common/constants";
import routes from "@/Utils/request/api";
import request from "@/Utils/request/request";
-import { classNames, formatDate, formatTime } from "@/Utils/utils";
-
-export default function ConsultationNursingTab(props: ConsultationTabProps) {
- const { t } = useTranslation();
- return (
-
-
-
-
{t("routine")}
-
-
-
-
{t("nursing_care")}
-
-
-
- );
-}
const REVERSE_CHOICES = {
appetite: {
@@ -114,6 +91,116 @@ const ROUTINE_ROWS = [
{ subField: true, field: "appetite" } as const,
];
+const NursingPlot = ({ consultationId }: ConsultationTabProps) => {
+ const { t } = useTranslation();
+ const [results, setResults] = useState({});
+ const [currentPage, setCurrentPage] = useState(1);
+ const [totalCount, setTotalCount] = useState(0);
+
+ useEffect(() => {
+ const fetchDailyRounds = async (
+ currentPage: number,
+ consultationId: string,
+ ) => {
+ const { res, data } = await request(routes.dailyRoundsAnalyse, {
+ body: { page: currentPage, fields: NursingPlotFields },
+ pathParams: { consultationId },
+ });
+ if (res && res.ok && data) {
+ setResults(data.results);
+ setTotalCount(data.count);
+ }
+ };
+
+ fetchDailyRounds(currentPage, consultationId);
+ }, [consultationId, currentPage]);
+
+ const handlePagination = (page: number) => setCurrentPage(page);
+
+ const data = Object.entries(results).map(([date, result]) => {
+ if ("nursing" in result) {
+ return {
+ date: date,
+ nursing: result.nursing,
+ };
+ } else {
+ return {
+ date: date,
+ nursing: null,
+ };
+ }
+ });
+
+ const dataToDisplay = data
+ .map((x) =>
+ x.nursing
+ ? x.nursing.map((f: any) => {
+ f["date"] = x.date;
+ return f;
+ })
+ : [],
+ )
+ .reduce((accumulator, value) => accumulator.concat(value), []);
+
+ const filterEmpty = (field: (typeof NURSING_CARE_PROCEDURES)[number]) => {
+ const filtered = dataToDisplay.filter(
+ (i: ProcedureType) => i.procedure === field,
+ );
+ return filtered.length > 0;
+ };
+
+ const areFieldsEmpty = () => {
+ let emptyFieldCount = 0;
+ for (const field of NURSING_CARE_PROCEDURES) {
+ if (!filterEmpty(field)) emptyFieldCount++;
+ }
+ return emptyFieldCount === NURSING_CARE_PROCEDURES.length;
+ };
+
+ const rows = NURSING_CARE_PROCEDURES.filter((f) => filterEmpty(f)).map(
+ (procedure) => ({
+ field: procedure,
+ title: t(`NURSING_CARE_PROCEDURE__${procedure}`),
+ }),
+ );
+
+ const mappedData = dataToDisplay.reduce(
+ (acc: Record, item: any) => {
+ if (!acc[item.date]) acc[item.date] = {};
+ acc[item.date][item.procedure] = item.description;
+ return acc;
+ },
+ {},
+ );
+
+ return (
+
+
+ {areFieldsEmpty() ? (
+
+
+ {t("no_data_found")}
+
+
+ ) : (
+
+ )}
+
+
+ {totalCount > PAGINATION_LIMIT && !areFieldsEmpty() && (
+
+ )}
+
+ );
+};
+
const RoutineSection = ({ consultationId }: ConsultationTabProps) => {
const { t } = useTranslation();
const [page, setPage] = useState(1);
@@ -158,65 +245,11 @@ const RoutineSection = ({ consultationId }: ConsultationTabProps) => {
return (
-
-
-
-
- |
- {Object.keys(results).map((date) => (
-
- {formatDate(date)}
- {formatTime(date)}
- |
- ))}
-
-
-
- {ROUTINE_ROWS.map((row) => (
-
-
- {row.title ?? t(`LOG_UPDATE_FIELD_LABEL__${row.field!}`)}
- |
- {row.field &&
- Object.values(results).map((obj, idx) => (
-
- {(() => {
- const value = obj[row.field];
- if (value == null) {
- return "-";
- }
- if (typeof value === "boolean") {
- return t(value ? "yes" : "no");
- }
- const choices = REVERSE_CHOICES[row.field];
- const choice = `${row.field.toUpperCase()}__${choices[value as keyof typeof choices]}`;
- return t(choice);
- })()}
- |
- ))}
-
- ))}
-
-
-
+
{totalCount != null && totalCount > PAGINATION_LIMIT && (
@@ -231,3 +264,24 @@ const RoutineSection = ({ consultationId }: ConsultationTabProps) => {
);
};
+
+export default function ConsultationNursingTab(props: ConsultationTabProps) {
+ const { t } = useTranslation();
+ return (
+
+
+
+
{t("routine")}
+
+
+
+
{t("nursing_care")}
+
+
+
+ );
+}
diff --git a/src/components/Facility/Consultations/LogUpdateAnalayseTable.tsx b/src/components/Facility/Consultations/LogUpdateAnalayseTable.tsx
new file mode 100644
index 00000000000..cd8f0a32ed8
--- /dev/null
+++ b/src/components/Facility/Consultations/LogUpdateAnalayseTable.tsx
@@ -0,0 +1,95 @@
+import React from "react";
+import { useTranslation } from "react-i18next";
+
+import { classNames, formatDate, formatTime } from "@/Utils/utils";
+
+interface SharedSectionTableProps {
+ data: Record
;
+ rows: Array<{ title?: string; field?: string; subField?: boolean }>;
+ choices?: Record>;
+}
+
+const LogUpdateAnalayseTable: React.FC = ({
+ data,
+ rows,
+ choices = {},
+}) => {
+ const { t } = useTranslation();
+
+ // Helper function to get the display value
+ const getDisplayValue = (
+ value: string | boolean | null | undefined,
+ field?: string,
+ ): string => {
+ if (value == null) {
+ return " ";
+ }
+
+ if (typeof value === "boolean") {
+ return t(value ? "yes" : "no");
+ }
+ if (field && choices[field]) {
+ const choice =
+ choices[field][value as keyof (typeof choices)[typeof field]];
+ return choice ? t(`${field.toUpperCase()}__${choice}`) : "-";
+ }
+ if (value && typeof value == "string") return value;
+
+ return "-";
+ };
+
+ return (
+
+
+
+
+ |
+ {Object.keys(data).map((date) => (
+ <>
+
+ {formatDate(date)}
+ {formatTime(date)}
+ |
+ >
+ ))}
+
+
+
+ {rows.map((row) => (
+
+
+ {row.title ?? t(`LOG_UPDATE_FIELD_LABEL__${row.field!}`)}
+ |
+ {Object.values(data).map((obj, idx) => {
+ const value = obj[row.field!];
+ return (
+
+ {getDisplayValue(value, row.field)}
+ |
+ );
+ })}
+
+ ))}
+
+
+
+ );
+};
+
+export default LogUpdateAnalayseTable;
diff --git a/src/components/Facility/Consultations/NursingPlot.tsx b/src/components/Facility/Consultations/NursingPlot.tsx
deleted file mode 100644
index 13f5bb64201..00000000000
--- a/src/components/Facility/Consultations/NursingPlot.tsx
+++ /dev/null
@@ -1,132 +0,0 @@
-import { useEffect, useState } from "react";
-import { useTranslation } from "react-i18next";
-
-import Pagination from "@/components/Common/Pagination";
-import { NursingPlotFields } from "@/components/Facility/models";
-
-import { NURSING_CARE_PROCEDURES, PAGINATION_LIMIT } from "@/common/constants";
-
-import routes from "@/Utils/request/api";
-import request from "@/Utils/request/request";
-import { formatDateTime } from "@/Utils/utils";
-
-export const NursingPlot = ({ consultationId }: any) => {
- const { t } = useTranslation();
- const [results, setResults] = useState({});
- const [currentPage, setCurrentPage] = useState(1);
- const [totalCount, setTotalCount] = useState(0);
-
- useEffect(() => {
- const fetchDailyRounds = async (
- currentPage: number,
- consultationId: string,
- ) => {
- const { res, data } = await request(routes.dailyRoundsAnalyse, {
- body: { page: currentPage, fields: NursingPlotFields },
- pathParams: {
- consultationId,
- },
- });
- if (res && res.ok && data) {
- setResults(data.results);
- setTotalCount(data.count);
- }
- };
-
- fetchDailyRounds(currentPage, consultationId);
- }, [consultationId, currentPage]);
-
- const handlePagination = (page: number) => {
- setCurrentPage(page);
- };
-
- const data = Object.entries(results).map((key: any) => {
- return {
- date: formatDateTime(key[0]),
- nursing: key[1]["nursing"],
- };
- });
-
- const dataToDisplay = data
- .map((x) =>
- x.nursing.map((f: any) => {
- f["date"] = x.date;
- return f;
- }),
- )
- .reduce((accumulator, value) => accumulator.concat(value), []);
-
- const filterEmpty = (field: (typeof NURSING_CARE_PROCEDURES)[number]) => {
- const filtered = dataToDisplay.filter((i: any) => i.procedure === field);
- return filtered.length > 0;
- };
-
- const areFieldsEmpty = () => {
- let emptyFieldCount = 0;
- for (const field of NURSING_CARE_PROCEDURES) {
- if (!filterEmpty(field)) emptyFieldCount++;
- }
- if (emptyFieldCount === NURSING_CARE_PROCEDURES.length) return true;
- else return false;
- };
-
- return (
-
-
-
-
- {areFieldsEmpty() && (
-
-
- {t("no_data_found")}
-
-
- )}
- {NURSING_CARE_PROCEDURES.map(
- (f) =>
- filterEmpty(f) && (
-
-
-
-
- {t(`NURSING_CARE_PROCEDURE__${f}`)}
-
-
-
-
- {dataToDisplay
- .filter((i: any) => i.procedure === f)
- .map((care: any, index: number) => (
-
-
- {care.date}
-
-
- {care.description}
-
-
- ))}
-
-
- ),
- )}
-
-
-
-
- {!areFieldsEmpty() && totalCount > PAGINATION_LIMIT && (
-
- )}
-
- );
-};