diff --git a/package.json b/package.json index ca213c58..6aac82ca 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "carbon-components-react": "^7.25.0", "carbon-icons": "^7.0.7", "moment": "^2.29.4", + "prop-types": "^15.8.1", "react": "16.14.0", "react-dom": "16.14.0", "react-intl": "^3.3.2", diff --git a/public/i18n/locale_en.json b/public/i18n/locale_en.json index bec45b33..e847e80c 100644 --- a/public/i18n/locale_en.json +++ b/public/i18n/locale_en.json @@ -11,5 +11,11 @@ "DRUG_CHART_MODAL_SCHEDULE_ORDER_WARNING": "Times entered should be in ascending order", "DRUG_CHART_MODAL_EMPTY_SCHEDULE_WARNING": "Please enter Schedule(s)", "DRUG_CHART_MODAL_EMPTY_START_TIME_WARNING": "Please enter Start Time", - "DRUG_CHART_MODAL_START_TIME_BEYOND_NEXT_DOSE": "Start Time does not match with the frequency" + "DRUG_CHART_MODAL_START_TIME_BEYOND_NEXT_DOSE": "Start Time does not match with the frequency", + "ADMINISTERED_LATE": "Administered Late", + "NOT_ADMINISTERED": "Not Administered", + "LATE": "Late", + "ADMINISTERED": "Administered", + "PENDING": "Pending", + "NOTE": "Note" } diff --git a/src/features/Calendar/Calendar.scss b/src/features/Calendar/Calendar.scss index 54dbfe30..7e61aab2 100644 --- a/src/features/Calendar/Calendar.scss +++ b/src/features/Calendar/Calendar.scss @@ -2,7 +2,12 @@ width: max-content; border-spacing: 0; margin: 0; - tbody > tr:nth-child(even) { - background-color: #f4f4f4; + tbody { + tr { + border-bottom: 0; + &:nth-child(even) { + background-color: #f4f4f4; + } + } } } diff --git a/src/features/CalendarHeader/CalendarHeader.scss b/src/features/CalendarHeader/CalendarHeader.scss index 872e686d..3908af25 100644 --- a/src/features/CalendarHeader/CalendarHeader.scss +++ b/src/features/CalendarHeader/CalendarHeader.scss @@ -22,6 +22,7 @@ padding-bottom: 8px; display: grid; align-items: end; + box-sizing: border-box; &:last-child { --additional-borders: 1; diff --git a/src/features/DrugChart/DrugChart.jsx b/src/features/DrugChart/DrugChart.jsx index dcb9c236..11ec1131 100644 --- a/src/features/DrugChart/DrugChart.jsx +++ b/src/features/DrugChart/DrugChart.jsx @@ -4,24 +4,40 @@ import moment from "moment"; import Calendar from "../Calendar/Calendar"; import CalendarHeader from "../CalendarHeader/CalendarHeader"; import "./DrugChart.scss"; +import DrugList from "../DrugList/DrugList"; +import DrugChartLegend from "../DrugChartLegend/DrugChartLegend"; export const TransformDrugChartData = (drugChartData) => { + const drugOrderData = []; const transformedDrugChartData = drugChartData.map((schedule) => { - const { slots } = schedule; + const { slots, order } = schedule; const slotData = {}; + const drugOrder = { + drugName: order.drug.display, + drugRoute: order.route.display, + administrationInfo: [], + }; + if (order.duration) { + drugOrder.duration = order.duration + " " + order.durationUnits.display; + } + if (order.doseUnits.display !== "ml") { + drugOrder.dosage = order.dose; + drugOrder.doseType = order.doseUnits.display; + } else { + drugOrder.dosage = order.dose + order.doseUnits.display; + } + if (order.duration) { + drugOrder.duration = order.duration + " " + order.durationUnits.display; + } slots.forEach((slot) => { const { startDateTime, status } = slot; const startDateTimeObj = new Date(startDateTime); - let adminInfo; + let adminInfo, administeredTime; if (slot.admin) { const { administeredBy, administeredAt } = slot.admin; - const administeredTime = moment(new Date(administeredAt)).format( - "HH:mm" - ); - + administeredTime = moment(new Date(administeredAt)).format("HH:mm"); adminInfo = administeredBy + " [" + administeredTime + "]"; } - const startHour = startDateTimeObj.getHours(); const startMinutes = startDateTimeObj.getMinutes(); slotData[startHour] = { @@ -29,10 +45,17 @@ export const TransformDrugChartData = (drugChartData) => { status: status, administrationInfo: adminInfo, }; + if (status === "Administered" || status === "Administered-Late") { + drugOrder.administrationInfo.push({ + kind: status, + time: administeredTime, + }); + } }); + drugOrderData.push(drugOrder); return slotData; }); - return transformedDrugChartData; + return [transformedDrugChartData, drugOrderData]; }; export default function DrugChart(props) { @@ -49,6 +72,25 @@ export default function DrugChart(props) { comment: "some comment", startDate: "2023-08-08T18:30:00.000Z", endDate: "2023-08-08T18:30:00.000Z", + order: { + drug: { + display: "Paracetamol 120 mg/5 mL Suspension (Liquid)", + }, + route: { + uuid: "9d6bc13f-3f10-11e4-adec-0800271c1b75", + display: "Oral", + }, + dose: 25, + doseUnits: { + uuid: "86239663-7b04-4563-b877-d7efc4fe6c46", + display: "ml", + }, + duration: 3, + durationUnits: { + uuid: "9d7437a9-3f10-11e4-adec-0800271c1b75", + display: "Day(s)", + }, + }, slots: [ { id: 1, @@ -71,7 +113,7 @@ export default function DrugChart(props) { notes: "some slot text", admin: { administeredBy: "Dr. John Doe", - administeredAt: "2023-08-08T08:30:00.000Z", + administeredAt: "2023-08-08T11:35:00.000Z", adminid: "1234", }, }, @@ -95,6 +137,20 @@ export default function DrugChart(props) { comment: "some comment", scheduleStartDate: "2023-08-08T18:30:00.000Z", scheduleEndDate: "2023-08-08T18:30:00.000Z", + order: { + drug: { + display: "Ibuprofen 400 mg Tablet", + }, + route: { + uuid: "9d6bc13f-3f10-11e4-adec-0800271c1b75", + display: "Oral", + }, + dose: 4, + doseUnits: { + uuid: "86239663-7b04-4563-b877-d7efc4fe6c46", + display: "Tablet(s)", + }, + }, slots: [ { id: 3, @@ -107,7 +163,7 @@ export default function DrugChart(props) { notes: "some slot text", admin: { administeredBy: "Dr. John Doe", - administeredAt: "2023-08-08T08:30:00.000Z", + administeredAt: "2023-08-08T08:45:00.000Z", adminid: "1234", }, }, @@ -136,6 +192,25 @@ export default function DrugChart(props) { comment: "some comment", scheduleStartDate: "2023-08-08T18:30:00.000Z", scheduleEndDate: "2023-08-08T18:30:00.000Z", + order: { + drug: { + display: "Amoxicillin/Clavulanic Acid 1000 mg Tablet (Tablet)", + }, + route: { + uuid: "9d6bc13f-3f10-11e4-adec-0800271c1b75", + display: "Oral", + }, + dose: "4", + doseUnits: { + uuid: "86239663-7b04-4563-b877-d7efc4fe6c46", + display: "Tablet(s)", + }, + duration: 2, + durationUnits: { + uuid: "9d7437a9-3f10-11e4-adec-0800271c1b75", + display: "Day(s)", + }, + }, slots: [ { id: 3, @@ -172,16 +247,45 @@ export default function DrugChart(props) { comment: "some comment", startDate: "2023-08-08T18:30:00.000Z", endDate: "2023-08-08T18:30:00.000Z", + order: { + drug: { + display: "Isoflurane 250 mL Inhalation Anesthetic Liquid (Liquid)", + }, + route: { + uuid: "9d6bc13f-3f10-11e4-adec-0800271c1b75", + display: "Oral", + }, + dose: 30, + doseUnits: { + uuid: "86239663-7b04-4563-b877-d7efc4fe6c46", + display: "ml", + }, + duration: 4, + durationUnits: { + uuid: "9d7437a9-3f10-11e4-adec-0800271c1b75", + display: "Day(s)", + }, + }, + dose: 2, + doseUnits: { + uuid: "86239663-7b04-4563-b877-d7efc4fe6c46", + display: "Tablet(s)", + }, slots: [ { id: 1, uuid: "738aa77d-03fc-438f-a87a-ae8a8867c421", orderId: 13, serviceType: "Medication Administration", - status: "Not-Administered", + status: "Administered-Late", startDateTime: "2023-08-08T10:45:00.000Z", endDateTime: "2023-08-08T09:30:00.000Z", notes: "some slot text", + admin: { + administeredBy: "Dr. John Doe", + administeredAt: "2023-08-08T10:50:00.000Z", + adminid: "1234", + }, }, { id: 2, @@ -212,14 +316,19 @@ export default function DrugChart(props) { }, ]; const transformedDrugchartData = TransformDrugChartData(drugChartData); - return ( -
-
-
- - +
+
+
+
+ +
+
+ + +
+
); } diff --git a/src/features/DrugChart/DrugChart.scss b/src/features/DrugChart/DrugChart.scss index d94e6fe0..5e2d67f9 100644 --- a/src/features/DrugChart/DrugChart.scss +++ b/src/features/DrugChart/DrugChart.scss @@ -1,3 +1,6 @@ +.drug-chart-dashboard { + font-family: "IBM Plex Sans", sans-serif; +} .drug-chart { --time-cell-height: 66px; --half-hour-width: 21px; @@ -15,6 +18,34 @@ .drug-chart-left-panel { border-right: 1px solid #ccc; + font-size: 13px; + overflow-y: auto; + height: 100%; + min-width: 225px; + .header { + height: 63px; + background-color: #ededed; + position: sticky; + top: 0; + } + table { + table-layout: fixed; + border-spacing: 0; + width: 100%; + tbody > tr:nth-child(even) { + background-color: #f4f4f4; + } + tr { + height: 66px; + border-bottom: 0; + td { + line-height: 20px; + padding: 0 10px; + max-width: 100%; + border: 0; + } + } + } } .drug-chart-content { diff --git a/src/features/DrugChart/DrugChart.spec.jsx b/src/features/DrugChart/DrugChart.spec.jsx index 11b54bc0..af760deb 100644 --- a/src/features/DrugChart/DrugChart.spec.jsx +++ b/src/features/DrugChart/DrugChart.spec.jsx @@ -25,6 +25,25 @@ const drugChartData = [ comment: "some comment", startDate: "2023-08-08T18:30:00.000Z", endDate: "2023-08-08T18:30:00.000Z", + order: { + drug: { + display: "Paracetamol 120 mg/5 mL Suspension (Liquid)", + }, + route: { + uuid: "9d6bc13f-3f10-11e4-adec-0800271c1b75", + display: "Oral", + }, + dose: 25, + doseUnits: { + uuid: "86239663-7b04-4563-b877-d7efc4fe6c46", + display: "ml", + }, + duration: 3, + durationUnits: { + uuid: "9d7437a9-3f10-11e4-adec-0800271c1b75", + display: "Day(s)", + }, + }, slots: [ { id: 1, @@ -85,23 +104,39 @@ describe("TransformDrugChartData", () => { it("should transform drug chart data", () => { const TransformedDrugChartData = TransformDrugChartData(drugChartData); expect(TransformedDrugChartData).toEqual([ - { - 11: { - administrationInfo: "Dr. John Doe [11:40]", - minutes: 30, - status: "Administered", - }, - 15: { - administrationInfo: "Dr. John Doe [15:30]", - minutes: 30, - status: "Pending", + [ + { + 11: { + administrationInfo: "Dr. John Doe [11:40]", + minutes: 30, + status: "Administered", + }, + 15: { + administrationInfo: "Dr. John Doe [15:30]", + minutes: 30, + status: "Pending", + }, + 8: { + administrationInfo: "Dr. John Doe [08:30]", + minutes: 30, + status: "Not-Administered", + }, }, - 8: { - administrationInfo: "Dr. John Doe [08:30]", - minutes: 30, - status: "Not-Administered", + ], + [ + { + administrationInfo: [ + { + kind: "Administered", + time: "11:40", + }, + ], + dosage: "25ml", + drugName: "Paracetamol 120 mg/5 mL Suspension (Liquid)", + drugRoute: "Oral", + duration: "3 Day(s)", }, - }, + ], ]); }); }); diff --git a/src/features/DrugChart/__snapshots__/DrugChart.spec.jsx.snap b/src/features/DrugChart/__snapshots__/DrugChart.spec.jsx.snap index 81703243..954b4f71 100644 --- a/src/features/DrugChart/__snapshots__/DrugChart.spec.jsx.snap +++ b/src/features/DrugChart/__snapshots__/DrugChart.spec.jsx.snap @@ -3,19 +3,200 @@ exports[`DrugChart should match snapshot 1`] = `
+ class="drug-chart" + > +
+
+
+ + + + + + + + + + + + + + + +
+
+ Paracetamol 120 mg/5 mL Suspension (Liquid) +
+
+ + 25ml + + +  - Oral + + +  - 3 Day(s) + +
+
+ + + 11:35 + +
+
+
+ Ibuprofen 400 mg Tablet +
+
+ + 4 + + +  - Tablet(s) + + +  - Oral + +
+
+ + + 08:45 + +
+
+
+ Amoxicillin/Clavulanic Acid 1000 mg Tablet (Tablet) +
+
+ + 4 + + +  - Tablet(s) + + +  - Oral + + +  - 2 Day(s) + +
+
+ + + 06:30 + +
+
+
+ Isoflurane 250 mL Inhalation Anesthetic Liquid (Liquid) +
+
+ + 30ml + + +  - Oral + + +  - 4 Day(s) + +
+
+ + + 10:50 + + , + + + + 13:40 + +
+
+
+
+
+
+ CalendarHeader +
+
+ Calendar +
+
+
- CalendarHeader + + Administered +
+
+ + Not Administered +
+
+ + Late +
+
+ + Administered Late
- Calendar + + Pending
diff --git a/src/features/DrugChartLegend/DrugChartLegend.jsx b/src/features/DrugChartLegend/DrugChartLegend.jsx new file mode 100644 index 00000000..69a45455 --- /dev/null +++ b/src/features/DrugChartLegend/DrugChartLegend.jsx @@ -0,0 +1,41 @@ +import React from "react"; +import AdministeredIcon from "../../icons/administered.svg"; +import AdministeredLateIcon from "../../icons/administered-late.svg"; +import LateIcon from "../../icons/late.svg"; +import NotAdministeredIcon from "../../icons/not-administered.svg"; +import PendingIcon from "../../icons/pending.svg"; +import "./DrugChartLegend.scss"; +import { FormattedMessage } from "react-intl"; + +export default function DrugChartLegend() { + return ( +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ ); +} diff --git a/src/features/DrugChartLegend/DrugChartLegend.scss b/src/features/DrugChartLegend/DrugChartLegend.scss new file mode 100644 index 00000000..6ee96e3c --- /dev/null +++ b/src/features/DrugChartLegend/DrugChartLegend.scss @@ -0,0 +1,12 @@ +.drug-chart-legend { + display: flex; + align-items: center; + gap: 15px; + padding: 10px; + font-size: 12px; + > * { + display: flex; + align-items: center; + gap: 5px; + } +} diff --git a/src/features/DrugList/DrugList.jsx b/src/features/DrugList/DrugList.jsx new file mode 100644 index 00000000..ec2701cd --- /dev/null +++ b/src/features/DrugList/DrugList.jsx @@ -0,0 +1,26 @@ +import React from "react"; +import PropTypes from "prop-types"; +import DrugListCell from "../DrugListCell/DrugListCell.jsx"; + +export default function DrugList(props) { + const { drugDetails } = props; + return ( +
+ + + {drugDetails.map((drugDetail, index) => { + return ( + + + + ); + })} + +
+
+ ); +} + +DrugList.propTypes = { + drugDetails: PropTypes.array.isRequired, +}; diff --git a/src/features/DrugListCell/DrugListCell.jsx b/src/features/DrugListCell/DrugListCell.jsx new file mode 100644 index 00000000..29c97e4e --- /dev/null +++ b/src/features/DrugListCell/DrugListCell.jsx @@ -0,0 +1,58 @@ +import React from "react"; +import PropTypes from "prop-types"; +import Clock from "../../icons/clock.svg"; +import "./DrugListCell.scss"; +// import { TooltipDefinitionCarbon } from "bahmni-carbon-ui"; + +export default function DrugListCell(props) { + const { drugInfo } = props; + const { + drugName, + dosage, + doseType, + drugRoute, + duration, + administrationInfo, + } = drugInfo; + const drugNameText =
{drugName}
; + //TODO: Add tooltip for drug name + return ( + + {/**/} + {drugNameText} +
+ {dosage} + {doseType &&  - {doseType}} +  - {drugRoute} + {duration &&  - {duration}} +
+ {administrationInfo.length >= 1 && ( +
+ + {administrationInfo.map((adminInfo, index) => { + if (adminInfo.kind === "Administered-Late") { + return ( + + {adminInfo.time} + {index !== administrationInfo.length - 1 && ( + , + )} + + ); + } else { + return ( + + {adminInfo.time} + {index !== administrationInfo.length - 1 && ,} + + ); + } + })} +
+ )} + + ); +} +DrugListCell.propTypes = { + drugInfo: PropTypes.object.isRequired, +}; diff --git a/src/features/DrugListCell/DrugListCell.scss b/src/features/DrugListCell/DrugListCell.scss new file mode 100644 index 00000000..15087bbc --- /dev/null +++ b/src/features/DrugListCell/DrugListCell.scss @@ -0,0 +1,17 @@ +.drug-name { + max-width: 85%; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + font-weight: 500; + color: #393939; + font-style: normal; +} +.dosage { + font-size: 12px; + font-weight: 400; + color: #393939; + > * { + display: inline-block; + } +} diff --git a/src/icons/clock.svg b/src/icons/clock.svg new file mode 100644 index 00000000..f0e7a4ac --- /dev/null +++ b/src/icons/clock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index d4b2aaf1..88b3ad7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4319,10 +4319,10 @@ babel-preset-jest@^29.5.0: babel-plugin-jest-hoist "^29.5.0" babel-preset-current-node-syntax "^1.0.0" -bahmni-carbon-ui@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/bahmni-carbon-ui/-/bahmni-carbon-ui-0.1.0.tgz#c84cae6109a5cd608a95e3d7c44b702380dd1967" - integrity sha512-jSE7yKDj4iAI45f2VgpbxfBgFhhDXLmt51I26UpTMpA3dk+S6PMkhkDzCq7o5KLLN17KMKd1q3LSNtEnHKWQAA== +bahmni-carbon-ui@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/bahmni-carbon-ui/-/bahmni-carbon-ui-0.1.2.tgz#1fd0282fa7d47ff430f27207404ed7eb92c34bf7" + integrity sha512-y3lWKaf3UidWVdBGbSI4ILx6wL+zANb5nzFk31x9i+Tvnf3+qHnJIJXWxXge3jCy4CRa19uoEeWtID1PgJmyfw== dependencies: "@storybook/addon-styling" "0.3" "@storybook/react" "6.5"