diff --git a/.eslintrc.js b/.eslintrc.js index b914cb73..aeae1ae0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -98,7 +98,7 @@ module.exports = { 'jsdoc/require-property-name': 1, 'jsdoc/require-property-type': 1, 'jsdoc/require-returns-check': 1, - 'jsdoc/require-returns-description': 'off', // todo: when there is time + 'jsdoc/require-returns-description': 'off', 'jsdoc/require-returns-type': 1, 'jsdoc/require-throws': 1, 'jsdoc/require-yields': 1, diff --git a/CHANGELOG.md b/CHANGELOG.md index a319d0c6..7563ef5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,1044 @@ + * ๐Ÿ““ docs Updated the comments and docblocks for each new module + * ๐Ÿ‘— style Code formatting changes and UI improvements + * ๐Ÿ‘— style Code formatting changes + * โ™ป refactor Moved `activePillbox` state up to MedicinePage + * โ™ป refactor Factored out remaining Rx Tab components + * โ™ป refactor Moved `` logic from MedicinePage to RxMedicine + * โ™ป refactor Moved `` logic from MedicinePage to RxMedicine + * โ™ป refactor Moved a good chunk of logic from MedicinePage to RxMedicine + * ๐Ÿ‘— style Changed tab layout for LandingPage and MedicinePage to left align for more screen real estate + * โ™ป refactor [WIP] Change MedicinePage to use Tab instead of radio buttons + * Merge pull request #292 from RyanNerd/290-inputhtmlattributes + * โ™ป refactor Changed all @types packages to be devDependencies + * ๐Ÿ““ docs CHANGELOG.md updated + * 0.10.15 + * Merge pull request #291 from RyanNerd/278-settings-tab + * ๐Ÿ‘— feat Preferences tab added + * Merge pull request #288 from RyanNerd/266-med-check-existing + * ๐Ÿ‘— feat When adding meds check for existing drugs + * Merge pull request #287 from RyanNerd/262-ts-work-with-forward-ref + * โ™ป refactor TypeScript formatting for DrugNameDropdown.tsx + * Merge pull request #286 from RyanNerd/279-turn-off-drug-suggestions-for-otc + * โ™ป refactor Do not suggest meds for OTC in MedicineEdit + * 0.10.14 + * Merge pull request #285 from RyanNerd/tab-poc + * โ›ฒ feat Client Load + * ๐Ÿ‘— style Changed the LandingPage tabs to use radio ToggleButtons + * 0.10.13 + * Merge pull request #284 from RyanNerd/275-med-edit-tab-blur + * ๐Ÿ› bug Drug input tab and enter not focusing + * Merge pull request #281 from RyanNerd/272-use-new-link-for-med-look-up + * ๐Ÿ‘— style Use MedlinePlus URL for medication details + * Merge pull request #280 from RyanNerd/274-medicineedit-modal-close-button + * ๐Ÿ› bug Clicking close button on MedicineEdit did not close the modal + * ๐Ÿ‘—style Added two drugs to drugNameList + * 0.10.12 + * Merge pull request #277 from RyanNerd/276-bug-medicineedit-no-suggest + * ๐Ÿ› bug MedicineEdit Drug field showing 'no suggestions' when it shouldn't + * ๐Ÿ““ docs CHANGELOG.md updated + * 0.10.11 + * ๐Ÿ‘— style Name of a drug updated in drugNameList.ts + * Merge pull request #271 from RyanNerd/270-typeahead-is-buggy-med-edit + * ๐Ÿ‘— style UI improvements and drug list update + * ๐Ÿ‘— style fixed some markdown formatting in cl-guide.md + * ๐Ÿ‘— style Removed TS disable in MedicineEdit and fixed a spelling error + * ๐Ÿ› bug Typeahead behaved strangly in MedicineEdit + * 0.10.10 + * 0.10.9 + * ๐Ÿ““ docs Updated CHANGELOG.md + * Merge pull request #269 from RyanNerd/138-med-suggestions + * 0.10.9 + * ๐Ÿ““ docs Updated CHANGELOG.md + * โ›ฒ feat MedicineEdit Drug field suggests medication names in a dropdown list + * ๐Ÿ““ docs Updated CHANGELOG.md + * 0.10.8 + * Merge pull request #265 from RyanNerd/264-fix-warning-react-does-not-recognize-prop + * ๐Ÿ› bug Unrecognized props on components + * ๐Ÿ› bug Fixed a cosmetic ๐Ÿ’„ issue in MedicineEdit + * Merge pull request #261 from RyanNerd/258-handle-log-pillbox-simple + * โ™ป refactor MedicinePage `handleLogPillbox()` + * โ™ป refactor MedicinePage combined the logic in `.onClose()` into `saveMedicine()` + * Merge pull request #260 from RyanNerd/255-add-asyncwrapper-to-medicinepage + * โ™ป refactor MedicinePage to use `asyncWrapper()` for error control + * โ™ป refactor MedicinePage to use `asyncWrapper()` for error control. + * 0.10.7 + * ๐Ÿ““ docs update the CHANGELOG.md + * Merge pull request #257 from RyanNerd/254-recfactor-adddruglog-functions + * โ™ป refactor `addEditOtcLog()` into `AddEditDrugLog()` in MedicinePage + * Merge pull request #256 from RyanNerd/refactor-handleLogOtcAmount-to-logamount + * โ™ป refactor `handleLogOtcDrugAmount() has been refactored int `handleLogDrugAmount() in MedicinePage + * โ™ป refactor `setLastTaken()` in MedicinePage is now declarative + * โ™ป refactor Remove unneded type guards + * Merge pull request #253 from RyanNerd/244-set-the-activemed-item-build + * โ™ป refactor setActiveMed when the med dropdown list is created + * ๐Ÿ‘— style Change a variable from `n` to `medicineId` in DeleteMedicineModal + * Merge pull request #251 from RyanNerd/249-medicinepage-refactor-delete-druglog + * โ™ป refactor DeleteDrugLogModal as a separate component in MedicinePage + * ๐Ÿ‘— style Renamed the Changelog Commit Guide + * Merge pull request #250 from RyanNerd/245-medicine-page-code-improvement + * ๐Ÿ‘— style Props and arguments are more declarative + * Merge pull request #248 from RyanNerd/246-ok-as-confirm + * ๐Ÿ‘— style Change `a` to `isAccepted` for Confirm results + * Merge pull request #247 from RyanNerd/243-checkout-grid-del-otc + * ๐Ÿ‘— style Remove OTC from CheckoutGrid.tsx + * ๐Ÿ‘— style Clean up code for Pages + * ๐Ÿ‘— style Code clean up Toasts + * ๐Ÿ‘— style Code clean up for Modals + * ๐Ÿ‘— style Code clean up for ListGroups + * ๐Ÿ‘— style Code Simplification for many components + * 0.10.6 + * ๐Ÿ› bug Fix LastTakenButton not showing when hours taken === 0 + * ๐Ÿ‘— style Code formatting changes + * ๐Ÿ““ docs Created a Conventional Commit Guide to standardize commit messages and types + * Merge pull request #242 from RyanNerd/changelog-create + * Added ๐Ÿ“„ CHANGELOG.md ๐Ÿ“„ file + * 0.10.5 + * Code Cleanup :broom: + * Improve Client Selection Performance :bullettrain_side: + * 0.10.4 + * Merge pull request #241 from RyanNerd/239-otc-delete + * Allow OTC medicine to be deleted :bomb: + * 0.10.3 + * Merge pull request #238 from RyanNerd/237-modal-improvements + * Modal Improvements + * Code Improvements for MedicineEdit + * 0.10.2 + * Refactored the Checkout All Meds confirm modal + * 0.10.1 + * Fix :wrench: inactive meds showing up in dropdown + * 0.10.0 + * Merge pull request #235 from RyanNerd/198-soft-delete-medicine + * Allow Medication to be Deleted :bomb: + * Change tsconfig.json to not include comments :notebook: in the output + * Code Clean Up :broom: `` - Use ` `${variable}` ` instead of string concatination - Moved TooltipContainer.tsx to a Container directory under Pages + * Fix :wrench: problems found with inspections + * MedicineDrugPage UI Improvement :lipstick: + * MedicineEdit UI Improvements :lipstick: + * UI :lipstick: Improvements for `` + * Change how the search text is ClientPage is handled + * 0.9.8 + * 0.9.7 + * ResidentGrid => ClientGrid Name change + * Fix :wrench: Med Labels from Client dropdown printing all clients + * 0.9.6 + * Merge pull request #234 from RyanNerd/224-inactive-meds-in-pillbox-item-grid + * Fix :wrench: PillboxItemGrid showing inactive meds + * Merge pull request #233 from RyanNerd/231-deconstruct-props-manage-drug-page + * ManageDrugPage Improvements :sparkle: + * 0.9.5 + * Confirm component Enhancement :sparkler: + * 0.9.4 + * Merge pull request #232 from RyanNerd/227-conslidate-confirms + * Consolidation of confirm modals + * 0.9.3 + * Merge pull request #226 from RyanNerd/225-active-field-otc + * UI change for Manage Rx and Manage OTC + * Merge pull request #221 from RyanNerd/216-client-object + * Get confirmation for checkout all if there are exiting checked out meds + * MedicineEdit Improvements :sparkler: + * Fixed a :bug: bug where if all medications are inactivated the Med dropdown would be empty showing the last active medication + * - Changed the name of the `Client` type to `TClient` and moved it into global.d.ts - Added some documentation in RecordTypes.ts + * Use a Client object that contains all the data for the client + * Prevent tooltip from showing in printout for checkout all feature + * Merge branch 'no-toast-printed' + * 0.9.2 + * Prevent Toasts from showing in printouts + * 0.9.1 + * Merge pull request #215 from RyanNerd/213-med-dropdown-checkout-indicator + * UI :lipstick: enhancement for Med Dropdown + * 0.9.0 + * Tweaking the Checkout All feature UI :lipstick: + * 0.8.27 + * Remove ; from ManageDrugPage.tsx render + * Merge pull request #214 from RyanNerd/143-checkout-meds-on-deactivate + * All Medication Checkout Feature + * PillPopover Improvements + * Merge pull request #212 from RyanNerd/211-state-render-med-pb-list-group + * Fix :wrench: state sync with MedicinePage and PillboxListGroup + * Fix warnings :warning: in the React console + * 0.8.26 + * Added `medicineOtcList` constant to MedicinePage + * Global State moved up for PillboxCard + * Simplify `gridLists` prop processing + * 0.8.25 + * Merge pull request #210 from RyanNerd/209-pillbox-log-grid + * Pillbox components use `gridLists` prop to reduce prop drilling + * Last of the JSDoc linting rules and fixes :wrench: + * More JSDoc linting rules and fixes :wrench: + * 0.8.24 + * JSDoc linting rules and many fixes :wrench: + * Even More linting settings and fixes + * 0.8.23 + * More linting settings and fixes + * Added ``'react-hooks/exhaustive-deps':'error'` to the linter + * OverlayTrigger has a TS bug :bug: for required attributes, that aren't really required + * 0.8.22 + * Added some plugins to the linter + * 0.8.21 + * Merge pull request #206 from RyanNerd/201-show-pillbox-name + * Show pillbox name in Grids + * 0.8.20 + * Make it more obvious when in DEV mode + * 0.8.19 + * Merge pull request #205 from RyanNerd/169-error-boundary + * Experiment with + * Merge pull request #204 from RyanNerd/tslint2eslint-pretty + * Convert from tslint to eslint and using prettier + * 0.8.18 + * Make all props in CheckoutGrid required + * 0.8.17 + * Merge pull request #203 from RyanNerd/202-create-checkout-grid + * Create CheckoutGrid + * 0.8.16 + * Added missing hook dependancy + * 0.8.15 + * Merge pull request #200 from RyanNerd/196-refactor-medlistgroup + * Pillbox logging and UI Improvements + * 0.8.14 + * Merge pull request #197 from RyanNerd/195-remove-local-storage + * Pillbox Remove Local Storage + * 0.8.13 + * Merge pull request #194 from RyanNerd/pillbox-log-history + * Pillbox Drug Log History + * 0.8.12 + * Merge pull request #192 from RyanNerd/190-ui-pillbox-listgroup-improvement + * Log Pillbox and UI Improvements + * 0.8.11 + * Only Log Pillbox Items if the medicine is active + * Add Strength of drug to PillboxListGroup Card + * History should include inactive drugs + * 0.8.10 + * Merge pull request #189 from RyanNerd/188-fix-bs + * BS Color Fix :wrench: + * Revert "BS Color Fix" + * BS Color Fix + * 0.8.9 + * Capitalize Pillbox Name in dropdown + * Merge pull request #187 from RyanNerd/checkout-badge + * More Medicine Checkout Improvements + * Merge pull request #184 from RyanNerd/checkout-badge + * Add a badge to the Print Checkout buttons + * Merge pull request #183 from RyanNerd/179-remove-med-checkout-tab + * Remove Med Checkout Tab + * Alert pillbox name was not capitalized + * 0.8.8 + * Merge pull request #181 from RyanNerd/pillbox-ui-improvement + * Pillbox UI :lipstick: Improvements + * Merge pull request #180 from RyanNerd/druglog-notes-can-be-null + * Fix :bug: issue where drugLog.Notes could be null + * MedicinePage was pitching a fit about importing DrugLogHistory.tsx so this got renamed to MedDrugLogHistory.tsx + * 0.8.7 + * Merge pull request #178 from RyanNerd/177-credentials-need-alert + * LoginPage wasn't showing alert with failed credentials :lock: + * Merge pull request #176 from RyanNerd/simple-ifs + * Reformat :construction_worker: Code in several modules + * 0.8.6 + * Merge pull request #175 from RyanNerd/manage-rx-toast + * Add Toast :bread: to ManageDrugPage + * 0.8.5 + * Merge pull request #174 from RyanNerd/disabled-spinner-children + * DisabledSpinner UI :lipstick: Improvements + * Merge pull request #173 from RyanNerd/med-dropdown-subtext + * Better UI for other drug names in dropdown + * Merge pull request #172 from RyanNerd/170-medicine-drop-down-other + * Display Other Drug Names in dropdown + * 0.8.4 + * Merge pull request #171 from RyanNerd/code-clean-up + * Code clean up + * 0.8.3 + * Fixed About.tsx to allow using X to close + * 0.8.2 + * removal of PopoverButton.tsx as it is unused + * Add missing type declarations in reactn + * Merge pull request #168 from RyanNerd/145-remove-apikeyobserver + * Simplify the login authentication process + * Simplify About modal + * Merge pull request #167 from RyanNerd/163-fix-asyncwrapper-ts-errors + * Fix :wrench: `asyncwrapper()` typing errors + * Merge pull request #166 from RyanNerd/print-history-formatting + * Print History Formatting + * Merge pull request #165 from RyanNerd/more-ui-changes-pillboxlistgroup + * Redesign UI :lipstick: PillboxListGroup + * Merge pull request #164 from RyanNerd/pillbox-ui-change + * Redesign UI :lipstick: PillboxListGroup + * Suppress TypeScript errors that suddenly became a problem + * 0.8.1 + * Removal of the unused MedicineDetails grid + * Merge pull request #161 from RyanNerd/159-create-otclistgroupgrid + * Create a grid specifically for OtcListGroup + * Merge pull request #160 from RyanNerd/157-create-manageotcgrid + * Create a grid specifically for ManageOtcPage + * Merge pull request #158 from RyanNerd/147-add-search-manage-otc + * Add Search :mag: Textbox to Manage Otc Tab + * Merge pull request #156 from RyanNerd/154-refactor-druglog-grid + * Refactor Drug History + * Merge pull request #155 from RyanNerd/152-drug-history-rx-tab + * Add History Radio Button to Rx tab - Factored out the meat of DrugHistoryPage into DrugHistory.tsx - DrugHistoryPage and MedicinePage use DrugHistory for display of drug log history and print - Added "(OTC)"" to the DrugLogGrid indicating an OTC drug log - Memoized DrugHistoryPage in the LandingPage to reduce re-renders + * Merge pull request #153 from RyanNerd/149-remove-the-drug-log-table-from-manag + * Remove Drug Log table from ManageDrugPage + * Reorganization and Toast own component + * Merge pull request #151 from RyanNerd/fix-checkout-drug-history + * Show checkouts in drug log history + * Merge pull request #150 from RyanNerd/performance + * Performance :runner: enhancements + * Performance :runner: enhancement + * 0.8.0 + * Merge pull request #142 from RyanNerd/pillbox-full-feature + * UX :bar_chart: Improvement + * UI :lipstick: improvement + * Toasts UI :lipstick: improved + * Limit the DrugLogList to the last 5 days + * Experimental hook for checking idle + * - Improved the performance of the pillboxMedLog[] - + * UI :lipstick: Improvements + * PillboxListGroup takes children as a prop + * Pillbox Display + * PillboxListGroup.tsx changes + * Code clean up + * Removal of ClientObserver.ts + * Removal of the PillboxObserver.ts + * Removal of the PillboxItemObserver.ts + * Removal of the DrugLogObserver.ts + * Code clean up - Remove unneeded `e: React.MouseEvent` arguments in MedicineDetail.tsx and components that use it. - Moved the todo: add search box to Manage OTC from comment to an issue - Fixed a bug where even if cancel was chosen do delete an OTC drug the drug would get deleted anyway. + * Print Medicine Checkout + * - Change import for react-bootstrap to use direct imports for all components - When the OTC search textbox is cleared the `activeOtc` gets set to null. This was causing visual sync up issues. The search text would be set to an empty string but the selected drug would still be active. + * Many changes + * Make multiSort() generic + * UI :lipstick: improvements + * UI :lipstick: improvements + * UI :lipstick: improvements + * UI :lipstick: improvements + * UI :lipstick: improvements + * UI :lipstick: improvements + * Refactoring + * Fix :wrench: some UI :bug: bugs + * More UI :lipstick: on MedicinePage + * UI :lipstick: Changes + * Fixing my brain fart + * Fix :wrench: deleting a drugLog wasn't refreshing the `drugLogList` + * removed lastTaken as a useState()/useEffect() + * Removed `drugLogList` from MedicinePage + * Merge remote-tracking branch 'origin/pillbox-full-feature' into pillbox-full-feature + * Attempted a refactor of the async DrugLog + * UI :lipstick: Change to log buttons + * UI :lipstick: Change to log buttons + * Significantly Simplified PillboxListGroup + * Significantly Simplified PillboxListGroup + * Refactored some pillbox log history functions + * Partially fix "update" in the MedicineManager.ts + * Attempt at getting Log Pillbox functionality + * Show pillbox items in the med dropdown list + * = Disable the Pillbox radiobutton if `medicineList` has less than 5 items + * - OtcListGroup search textbox autoFocus - The MedicineDetail grid row will appear in bold if selected + * Minor code clean up in MedicinePage.tsx + * Simplified OtcListGroup + * Flatten and sort all imports + * - Fixed a missing useEffect dependency in MedicinePage.tsx - Flattened imports - See: https://dev.to/nilanth/no-more-import-in-react-2mbo + * Remove `activeClient` global + * Manage activeMed state better when adding med + * Keep state of activeMed when drugs are logged + * :broom: Clean up code + * Move all PillboxPage.tsx into PillboxListGroup.tsx - Moved the logic from PillboxPage as a landing page item instead making it a ListGroup item - Fixed a bug in `getPillboxItemS()` where the filter wasn't including the `pillboxId` - Removed the PillboxPage from the LandingPage - Added PillboxCard.tsx as a more feature rich PillboxItemGrid - MedicinePage.tsx updated to use PillboxListGroup new features and PillboxCard - PillboxItemGrid.tsx :building_construction: scaffolding added for click on row functionality - PillboxListGroup.tsx placeholder replaced with actual logic and features :atom_symbol: - Destroyed :bomb: PillboxPage.tsx - Destroyed :bomb: MedicineListGroup.tsx + * - Set initial state of `activeMed`, `activeOtc` & `activePillbox` by using `usePrevious()` hook. - Changed `LIST_TYPE` to `DISPLAY_TYPE` and added a Print to the enum. - Removal of the `` component in OtcListGroup.tsx - Conditional rendering fixed and updated in MedListGroup.tsx - LandingPage.tsx documentation updated + * Moved state back down to components in LandingPage + * Major :ringed_planet: :construction_worker: Overhaul of MedicinePage - MedicinePage can now be in one of three states: 1. Medicine - select and log prescriptions 2. OTC - Select and log OTC drugs 3. Pillbox - Select a pill box and log drugs therein - Complete UI :lipstick: redesign for MedicinePage.tsx - MedListGroup changed to **only** handle prescriptions (was handling pillbox and drugs -- needed separation of concerns) - Removed Show/Hide OTC buttons in OtcListGroup - Added PillboxListGroup.tsx - Added radio buttons Medicine, OTC, and Pillbox to let user set state. - OtcList global moved to LandingPage.tsx and passed into MedicinePage.tsx + * - Minor code clean-up in About.tsx - ActiveResidentObserver.ts uses the `usePrevious()` hook now. - ApiKeyObserver.ts uses `asyncWrapper()` for better error control. - Minor code clean-up in ConfirmDialogModal.tsx - Very minor code clean-up in ErrorDetailsObserver.ts (removed a space) - Added function getPillboxItems.ts so MedicinePage.tsx and PillboxPage.tsx can share code. - Added `IPillboxItemObserver` interface to global.d.ts - Some code clean-up for LandingPage.tsx as well as some todos. Also ``` takes additional attributes passed in from LandingPage. - Minor code clean-up in MedDropdown.tsx - Added `` to be displayed when the selected activeId is a pillbox. - Code clean-up for MedListGroup.tsx - `` replaced with a standard ` {hasPillboxItems && ๐Ÿ’Š indicates drug logged from a Pillbox} diff --git a/src/components/Pages/Grids/PillboxLogGrid.tsx b/src/components/Pages/Grids/PillboxLogGrid.tsx index 778673c0..420a5bcb 100644 --- a/src/components/Pages/Grids/PillboxLogGrid.tsx +++ b/src/components/Pages/Grids/PillboxLogGrid.tsx @@ -1,6 +1,6 @@ import {IGridLists} from 'components/Pages/Grids/DrugLogGrid'; import PillPopover from 'components/Pages/Grids/PillPopover'; -import {TPillboxMedLog} from 'components/Pages/MedicinePage'; +import {TPillboxMedLog} from 'components/Pages/RxTabs/RxPillbox'; import Table from 'react-bootstrap/Table'; import React from 'reactn'; import {BsColor, deconstructGridLists, randomString} from 'utility/common'; diff --git a/src/components/Pages/LandingPage.tsx b/src/components/Pages/LandingPage.tsx index c3a5fbc3..de6da63f 100644 --- a/src/components/Pages/LandingPage.tsx +++ b/src/components/Pages/LandingPage.tsx @@ -109,10 +109,12 @@ const LandingPage = (props: IProps) => { {loginPage} Clients}> - {clientPage} + {clientPage} Rx}> - {activeClient && activeTabKey === 'medicine' && {medicinePage}} + {activeClient && activeTabKey === 'medicine' && ( + {medicinePage} + )} void; } const CheckoutListGroup = (props: IProps) => { - const {checkoutList, medicineList, activeClient, onClose} = props; - const clientName = activeClient ? clientFullName(activeClient) : ''; + const {checkoutList, medicineList, clientRecord, onClose} = props; + const clientName = clientRecord ? clientFullName(clientRecord) : ''; const now = new Date(); const today = getFormattedDate(now); diff --git a/src/components/Pages/ListGroups/PillboxListGroup.tsx b/src/components/Pages/ListGroups/PillboxListGroup.tsx index 90247520..2572762f 100644 --- a/src/components/Pages/ListGroups/PillboxListGroup.tsx +++ b/src/components/Pages/ListGroups/PillboxListGroup.tsx @@ -1,7 +1,7 @@ import {IGridLists} from 'components/Pages/Grids/DrugLogGrid'; import PillboxLogGrid from 'components/Pages/Grids/PillboxLogGrid'; import DisabledSpinner from 'components/Pages/ListGroups/DisabledSpinner'; -import {TPillboxMedLog} from 'components/Pages/MedicinePage'; +import {TPillboxMedLog} from 'components/Pages/RxTabs/RxPillbox'; import Confirm from 'components/Pages/Modals/Confirm'; import Alert from 'react-bootstrap/Alert'; import Badge from 'react-bootstrap/Badge'; diff --git a/src/components/Pages/ManageDrugPage.tsx b/src/components/Pages/ManageDrugPage.tsx index cc214468..f025afb5 100644 --- a/src/components/Pages/ManageDrugPage.tsx +++ b/src/components/Pages/ManageDrugPage.tsx @@ -234,7 +234,7 @@ const ManageDrugPage = (props: IProps): JSX.Element | null => { {showCheckoutPrint && activeClient && ( setShowCheckoutPrint(false)} diff --git a/src/components/Pages/MedicinePage.tsx b/src/components/Pages/MedicinePage.tsx index 8c5e5dfb..15ee33d1 100644 --- a/src/components/Pages/MedicinePage.tsx +++ b/src/components/Pages/MedicinePage.tsx @@ -1,51 +1,18 @@ -import MedDrugLogHistory from 'components/Pages/Grids/MedDrugLogHistory'; -import CheckoutListGroup from 'components/Pages/ListGroups/CheckoutListGroup'; -import {IDropdownItem} from 'components/Pages/ListGroups/MedDropdown'; -import DeleteDrugLogModal from 'components/Pages/Modals/DeleteDrugLogModal'; -import DeleteMedicineModal from 'components/Pages/Modals/DeleteMedicineModal'; -import DrugLogToast from 'components/Pages/Toasts/DrugLogToast'; +import RxHistory from 'components/Pages/RxTabs/RxHistory'; +import RxMedicine from 'components/Pages/RxTabs/RxMedicine'; +import RxOtc from 'components/Pages/RxTabs/RxOtc'; +import RxPillbox from 'components/Pages/RxTabs/RxPillbox'; +import RxPrint from 'components/Pages/RxTabs/RxPrint'; import Badge from 'react-bootstrap/Badge'; -import Button from 'react-bootstrap/Button'; -import Col from 'react-bootstrap/Col'; -import ListGroup from 'react-bootstrap/ListGroup'; -import Row from 'react-bootstrap/Row'; +import Tab from 'react-bootstrap/Tab'; +import Tabs from 'react-bootstrap/Tabs'; import ToggleButton from 'react-bootstrap/ToggleButton'; import React, {useEffect, useGlobal, useState} from 'reactn'; -import {DrugLogRecord, MedicineRecord, newDrugLogRecord, PillboxItemRecord, PillboxRecord} from 'types/RecordTypes'; -import { - asyncWrapper, - calculateLastTaken, - clientFullName, - getCheckoutList, - getDrugName, - getMedicineRecord, - isToday, - multiSort, - SortDirection -} from 'utility/common'; -import TabContent from '../../styles/common.css'; -import LastTakenButton from './Buttons/LastTakenButton'; -import DrugLogGrid from './Grids/DrugLogGrid'; -import PillboxCard from './Grids/PillboxCard'; -import MedListGroup from './ListGroups/MedListGroup'; -import OtcListGroup from './ListGroups/OtcListGroup'; -import PillboxListGroup from './ListGroups/PillboxListGroup'; -import DrugLogEdit from './Modals/DrugLogEdit'; -import MedicineEdit from './Modals/MedicineEdit'; +import {DrugLogRecord, PillboxRecord} from 'types/RecordTypes'; +import {getCheckoutList} from 'utility/common'; -export type TPillboxMedLog = { - Active: boolean; - Drug: string | undefined; - Notes: string | null; - PillboxId?: number | null; - PillboxItemId?: number | null; - Quantity: number; - Strength: string | null | undefined; - Updated: Date | null | undefined; -}; - -// Display states -enum DISPLAY_TYPE { +// Active Rx tab states +enum TAB_KEY { History = 'history', Medicine = 'med', OTC = 'otc', @@ -58,63 +25,23 @@ interface IProps { } /** - * MedicinePage - UI for logging prescription medications + * MedicinePage - UI for logging prescription & OTC medications as well as pillboxes and medication checkout * @param {IProps} props The props for this component * @returns {JSX.Element | null} */ const MedicinePage = (props: IProps): JSX.Element | null => { - const [activeClient, setActiveClient] = useGlobal('activeClient'); - const [activeMed, setActiveMed] = useState(null); - const [activeOtc, setActiveOtc] = useState(null); + const [activeClient] = useGlobal('activeClient'); const [activePillbox, setActivePillbox] = useState(null); - const [activeTabKey, setActiveTabKey] = useState(props.activeTabKey); + const [activeRxTab, setActiveRxTab] = useState(TAB_KEY.Medicine); const [checkoutList, setCheckoutList] = useState([]); - const [clientId, setClientId] = useState(activeClient?.clientInfo?.Id || null); - const [displayType, setDisplayType] = useState(DISPLAY_TYPE.Medicine); - const [, setErrorDetails] = useGlobal('__errorDetails'); - const [isBusy, setIsBusy] = useState(false); - const [lastTaken, setLastTaken] = useState( - activeMed?.Id && activeClient?.drugLogList ? calculateLastTaken(activeMed.Id, activeClient.drugLogList) : null - ); - const [medItemList, setMedItemList] = useState([]); const [mm] = useGlobal('medicineManager'); - const [otcList, setOtcList] = useGlobal('otcList'); - const [otcLogList, setOtcLogList] = useState([]); - const [pillboxMedLogList, setPillboxMedLogList] = useState([]); - const [showDeleteDrugLogRecord, setShowDeleteDrugLogRecord] = useState(null); - const [showDeleteMedicine, setShowDeleteMedicine] = useState(0); - const [showDrugLog, setShowDrugLog] = useState(null); - const [showMedicineEdit, setShowMedicineEdit] = useState(null); - const [toast, setToast] = useState(null); + const [otcList] = useGlobal('otcList'); - // Refresh activeClient when the activeResident global changes. - useEffect(() => { - if (activeClient) { - setClientId(activeClient?.clientInfo?.Id ? activeClient.clientInfo.Id : null); - } - }, [activeClient]); - - // activeTabKey refresh from prop + const [activeTabKey, setActiveTabKey] = useState(props.activeTabKey); useEffect(() => { setActiveTabKey(props.activeTabKey); }, [props.activeTabKey]); - // Update the otcLogList when the drugLogList is changed. - useEffect(() => { - // We only want to list the OTC drugs on this page that the resident has taken. - // @link https://stackoverflow.com/questions/31005396/filter-array-of-objects-with-another-array-of-objects - const drugLogList = activeClient?.drugLogList; - if (drugLogList) { - setOtcLogList( - drugLogList.filter((drug: DrugLogRecord) => { - return otcList.some((m) => { - return m.Id === drug?.MedicineId; - }); - }) - ); - } - }, [activeClient, activeClient?.drugLogList, otcList]); - // Update the checkoutList when drugLogList changes useEffect(() => { if (activeClient?.drugLogList) { @@ -122,415 +49,197 @@ const MedicinePage = (props: IProps): JSX.Element | null => { } }, [activeClient, setCheckoutList]); - // Refresh the pillboxDrugLog[] + // Observer to show / hide RxTabs useEffect(() => { - if (activePillbox && activeClient) { - const pillboxMedLog = [] as TPillboxMedLog[]; - const pillboxItemList = activeClient.pillboxItemList; - const drugLogList = activeClient.drugLogList; - pillboxItemList.forEach((pbi) => { - if (pbi.PillboxId === activePillbox.Id && pbi.Quantity) { - const drugLogRecord = drugLogList.find( - (dlr) => - dlr.PillboxItemId === pbi.Id && - dlr.MedicineId === pbi.MedicineId && - dlr.Updated && - isToday(dlr.Updated) - ); - - if (drugLogRecord) { - const med = activeClient.medicineList.find((m) => m.Id === drugLogRecord.MedicineId); - pillboxMedLog.push({ - Active: !!med?.Active, - Drug: med?.Drug, - Strength: med?.Strength, - Quantity: pbi.Quantity, - Notes: drugLogRecord.Notes, - PillboxItemId: drugLogRecord.PillboxItemId, - PillboxId: activePillbox.Id, - Updated: drugLogRecord.Updated - }); - } - } - }); - setPillboxMedLogList(multiSort(pillboxMedLog, {Quantity: SortDirection.asc, Drug: SortDirection.desc})); - } - }, [activeClient, activePillbox, setPillboxMedLogList]); - - // Build the dropdown items for the Medicine dropdown - useEffect(() => { - const itemList = [] as IDropdownItem[]; if (activeClient) { - const {drugLogList, pillboxList, pillboxItemList, medicineList} = activeClient; - const checkoutList = getCheckoutList(drugLogList); - - // Build the itemList with any pillboxes and meds from medicineList - let pbCnt = 0; - pillboxList.forEach((p) => { - const pbItems = pillboxItemList.filter((pbi) => pbi.PillboxId === p.Id); - const loggedPillboxItems = drugLogList.filter( - (d) => d.Updated && isToday(d.Updated) && pbItems.find((pbi) => pbi.Id === d.PillboxItemId) - ); - if (loggedPillboxItems.length === 0) { - itemList.push({ - id: -(p.Id as number), - description: p.Name.toUpperCase(), - subtext: null - }); // Pillbox have negative id - pbCnt++; + const historyEl = document.getElementById('medicine-page-tabs-tab-' + TAB_KEY.History); + if (historyEl) { + historyEl.style.display = activeClient.drugLogList.length === 0 ? 'none' : 'block'; + if (activeRxTab === TAB_KEY.History && activeClient.drugLogList.length === 0) { + setActiveRxTab(TAB_KEY.Medicine); } - }); - if (pbCnt > 0) itemList.push({id: 0, description: 'divider', subtext: null}); + } - medicineList.forEach((m) => { - if (m.Active) { - const strength = m.Strength || ''; - const other = m.OtherNames?.length > 0 ? `(${m.OtherNames})` : null; - const checkoutMed = checkoutList.find((c) => c.MedicineId === m.Id); - const description = (checkoutMed ? 'โŽ ' : '') + m.Drug + ' ' + strength; - itemList.push({ - id: m.Id as number, - description, - subtext: other - }); + const pillboxEl = document.getElementById('medicine-page-tabs-tab-' + TAB_KEY.Pillbox); + if (pillboxEl) { + pillboxEl.style.display = activeClient.medicineList.length < 5 ? 'none' : 'block'; + if (activeRxTab === TAB_KEY.Pillbox && activeClient.medicineList.length < 5) { + setActiveRxTab(TAB_KEY.Medicine); } - }); - - // If activeMed is null, and we have med items in the list then set the initial activeMed to the first item - if (activeMed === null && itemList.length > 0) { - const medsOnly = itemList.filter((i) => i.id > 0); - setActiveMed(medsOnly.length === 0 ? null : medicineList.find((m) => m.Id === medsOnly[0].id) || null); } - } else { - setActiveMed(null); - } - setMedItemList(itemList); - }, [activeClient, activeMed]); - - useEffect(() => { - setLastTaken( - activeClient && activeMed && activeMed.Id - ? calculateLastTaken(activeMed.Id, activeClient.drugLogList) - : null - ); - }, [activeClient, activeMed, activeMed?.Id]); - - // If there isn't an active client, or this isn't the active tab then do not render - if (activeTabKey !== 'medicine' || !clientId || !activeClient) return null; - const medicineOtcList = activeClient.medicineList.concat(otcList) as MedicineRecord[]; - - /** - * Given a MedicineRecord object Update/Insert the record and rehydrate the global otcList / medicineList - * Set the activeOtc or the activeMed with the updated medicine (if Active) - * @param {MedicineRecord} med Medicine record object - */ - const saveMedicine = async (med: MedicineRecord) => { - await setIsBusy(true); - const [e, m] = (await asyncWrapper(mm.updateMedicine(med))) as [unknown, Promise]; - if (e) await setErrorDetails(e); - const updatedMedicineRecord = await m; - if (updatedMedicineRecord.OTC) { - const [errLoadOtc, otcMeds] = (await asyncWrapper(mm.loadOtcList())) as [ - unknown, - Promise - ]; - if (errLoadOtc) await setErrorDetails(errLoadOtc); - else await setOtcList(await otcMeds); - setActiveOtc(updatedMedicineRecord.Active ? updatedMedicineRecord : null); - } else { - const [errLoadMeds, meds] = (await asyncWrapper(mm.loadMedicineList(clientId))) as [ - unknown, - Promise - ]; - if (errLoadMeds) await setErrorDetails(errLoadMeds); - else await setActiveClient({...activeClient, medicineList: await meds}); - const activeMeds = (await meds).filter((m) => m.Active); - setActiveMed( - updatedMedicineRecord.Active ? updatedMedicineRecord : activeMeds.length === 0 ? null : activeMeds[0] - ); - } - await setIsBusy(false); - }; - - /** - * Given a MedicineRecord PK delete the medicine - * @param {number} medicineId The PK of the Medicine record to delete - */ - const deleteMedicine = async (medicineId: number) => { - await setIsBusy(true); - const [e, deleted] = (await asyncWrapper(mm.deleteMedicine(medicineId))) as [unknown, Promise]; - if (e) await setErrorDetails(e); - if (await deleted) - await setActiveClient({ - ...activeClient, - medicineList: await mm.loadMedicineList(clientId) - }); - else await setErrorDetails(new Error('Unable to delete medicine. Id: ' + medicineId)); - await setIsBusy(false); - }; - - /** - * Given a DrugLogRecord Update or Insert the record and rehydrate the drugLogList - * @param {DrugLogRecord} drugLog Druglog record object - */ - const saveDrugLog = async (drugLog: DrugLogRecord): Promise => { - await setIsBusy(true); - const [e, updatedDrugLog] = (await asyncWrapper(mm.updateDrugLog(drugLog))) as [ - unknown, - Promise - ]; - if (e) await setErrorDetails(e); - else { - const [errLoadLog, drugLogs] = (await asyncWrapper(mm.loadDrugLog(clientId, 5))) as [ - unknown, - Promise - ]; - if (errLoadLog) await setErrorDetails(errLoadLog); - else await setActiveClient({...activeClient, drugLogList: await drugLogs}); - } - await setIsBusy(false); - return await updatedDrugLog; - }; - - /** - * Add or update a pillboxRecord record. - * @param {PillboxRecord} pillboxRecord Pillbox record object - */ - const savePillbox = async (pillboxRecord: PillboxRecord) => { - setIsBusy(true); - const [e, updatedPillbox] = (await asyncWrapper(mm.updatePillbox(pillboxRecord))) as [ - unknown, - Promise - ]; - if (e) await setErrorDetails(e); - else setActivePillbox(await updatedPillbox); - const [errLoadPillbox, pillboxes] = (await asyncWrapper(mm.loadPillboxList(clientId))) as [ - unknown, - Promise - ]; - if (errLoadPillbox) await setErrorDetails(errLoadPillbox); - else await setActiveClient({...activeClient, pillboxList: await pillboxes}); - setIsBusy(false); - }; - - /** - * Delete an existing pillbox. - * @param {number} pillboxId The PK for the Pillbox table - */ - const deletePillbox = async (pillboxId: number) => { - setIsBusy(true); - const [e, pillboxDeleted] = (await asyncWrapper(mm.deletePillbox(pillboxId))) as [unknown, Promise]; - if (e) await setErrorDetails(e); - else if (await pillboxDeleted) { - const [errLoad, pillboxes] = (await asyncWrapper(mm.loadPillboxList(clientId))) as [ - unknown, - Promise - ]; - if (errLoad) await setErrorDetails(errLoad); - else { - const pillboxList = await pillboxes; - await setActiveClient({...activeClient, pillboxList}); - await setActivePillbox(pillboxList.length > 0 ? pillboxList[0] : null); + const printEl = document.getElementById('medicine-page-tabs-tab-' + TAB_KEY.Print); + if (printEl) { + printEl.style.display = checkoutList.length === 0 ? 'none' : 'block'; + if (activeRxTab === TAB_KEY.Print && checkoutList.length === 0) { + setActiveRxTab(TAB_KEY.Medicine); + } } - } else { - await setErrorDetails(new Error('Unable to delete Pillbox. Id: ' + pillboxId)); } - setIsBusy(false); - }; - /** - * Handle when the user clicks on Log Pillbox - */ - const handleLogPillbox = async () => { - setIsBusy(true); - const toastQ = [] as DrugLogRecord[]; - const [e, loggedPillboxMeds] = (await asyncWrapper(mm.logPillbox(activePillbox?.Id as number))) as [ - unknown, - Promise - ]; - if (e) await setErrorDetails(e); - else { - const loggedPillboxDrugs = await loggedPillboxMeds; - if (loggedPillboxDrugs.length > 0) { - const [errLoadLog, drugLogs] = (await asyncWrapper(mm.loadDrugLog(clientId, 5))) as [ - unknown, - Promise - ]; - if (errLoadLog) await setErrorDetails(errLoadLog); - else await setActiveClient({...activeClient, drugLogList: await drugLogs}); - loggedPillboxDrugs.forEach((ld) => toastQ.push({...ld})); - setToast(toastQ); - } + // Move LandingPage tabs up for more screen real estate + const tabContent = document.querySelectorAll('div#root > .tab-content'); + if (tabContent && tabContent.length > 0) { + tabContent[0].style.marginTop = '-15px'; } - setIsBusy(false); - }; - /** - * Add or update a pillboxItem record - * @param {PillboxItemRecord} pillboxItemRecord The pillboxItem record object - */ - const savePillboxItem = async (pillboxItemRecord: PillboxItemRecord) => { - setIsBusy(true); - const [e, updatedPillboxItem] = (await asyncWrapper(mm.updatePillboxItem(pillboxItemRecord))) as [ - unknown, - Promise - ]; - if (e) await setErrorDetails(e); - else if (await updatedPillboxItem) { - const [errLoadPills, pillboxItems] = (await asyncWrapper(mm.loadPillboxItemList(clientId as number))) as [ - unknown, - Promise - ]; - if (errLoadPills) await setErrorDetails(errLoadPills); - else await setActiveClient({...activeClient, pillboxItemList: await pillboxItems}); + // Move MedicinePage rxTabs up for more screen real estate + const navElement = document.querySelectorAll('div.medicine-page-tablet > nav.nav'); + if (navElement && navElement.length > 0) { + navElement[0].style.marginBottom = '-15px'; } - setIsBusy(false); - }; - - /** - * Fires when user clicks on +Log or the drug log edit button - * @param {DrugLogRecord} drugLogInfo The drugLog record object - * @param {boolean} isOtc True if drug to log is an OTC med - */ - const addEditDrugLog = (drugLogInfo?: DrugLogRecord, isOtc?: boolean) => { - const drugLogRecord = drugLogInfo - ? {...drugLogInfo} - : ({ - Id: null, - ResidentId: clientId, - MedicineId: isOtc ? activeOtc?.Id : activeMed?.Id, - Notes: '' - } as DrugLogRecord); - setShowDrugLog(drugLogRecord); - }; - - /** - * Fires when the Log 1...4 buttons are clicked. - * @param {number} amount The number of pills (medication) taken - * @param {boolean} isOtc True if the amount logged is an OTC med - */ - const handleLogDrugAmount = (amount: number, isOtc?: boolean) => { - const medicineId = isOtc ? activeOtc?.Id : activeMed?.Id; - const drugLogInfo = {...newDrugLogRecord}; - drugLogInfo.ResidentId = clientId; - drugLogInfo.MedicineId = medicineId as number; - drugLogInfo.Notes = amount.toString(); - saveDrugLog(drugLogInfo).then((r) => setToast([r])); - }; - - /** - * Convenience function to get drug name - * @param {number} medicineId The PK of the Medicine table - * @returns {string | undefined} - */ - const drugName = (medicineId: number): string | undefined => { - return getDrugName(medicineId, medicineOtcList); - }; - /** - * Handle when the user has clicked on a pill - * @param {number} pillboxId The PK of the Pillbox table - */ - const handleOnPillClick = (pillboxId: number) => { - if (activeClient) { - const pb = activeClient.pillboxList.find((p) => p.Id === pillboxId); - if (pb) { - setActivePillbox(pb); - setDisplayType(DISPLAY_TYPE.Pillbox); - } + // Changing padding on this tab changes it for all RxTabs. More screen real estate + const rxMedicineTab = document.getElementsByClassName('rx-medicine-tab'); + if (rxMedicineTab) { + const rxMedTabElement = rxMedicineTab[0] as HTMLElement; + rxMedTabElement.style.paddingBottom = '1px'; } - }; + }, [activeClient, activeRxTab, checkoutList.length]); - if (!activeClient) return null; + // If there isn't an active client, or the active tab isn't 'medicine' then do not render + if (!activeClient || activeTabKey !== 'medicine') return null; - const {drugLogList, pillboxList, pillboxItemList, medicineList} = activeClient; + const {drugLogList, pillboxList, medicineList} = activeClient; return ( - <> - - - +
+ setActiveRxTab((key as TAB_KEY) || TAB_KEY.Medicine)} + > + setDisplayType(DISPLAY_TYPE.Medicine)} + onChange={() => setActiveRxTab(TAB_KEY.Medicine)} size="sm" type="radio" - value={DISPLAY_TYPE.Medicine} + value={TAB_KEY.Medicine} variant="outline-success" > Medicine - + } + > + { + setActivePillbox(pillboxList.find((p) => p.Id === id) || null); + setActiveRxTab(TAB_KEY.Pillbox); + }} + /> + + + setDisplayType(DISPLAY_TYPE.OTC)} + onChange={() => setActiveRxTab(TAB_KEY.OTC)} size="sm" type="radio" - value={DISPLAY_TYPE.OTC} + value={TAB_KEY.OTC} variant="outline-success" > OTC - + } + > + + + + setDisplayType(DISPLAY_TYPE.History)} + onChange={() => setActiveRxTab(TAB_KEY.History)} size="sm" type="radio" - value={DISPLAY_TYPE.History} + value={TAB_KEY.History} variant="outline-success" > History - + } + > + { + setActivePillbox(pillboxList.find((p) => p.Id === id) || null); + setActiveRxTab(TAB_KEY.Pillbox); + }} + /> + + + setDisplayType(DISPLAY_TYPE.Pillbox)} + onChange={() => setActiveRxTab(TAB_KEY.Pillbox)} size="sm" type="radio" - value={DISPLAY_TYPE.Pillbox} + value={TAB_KEY.Pillbox} variant="outline-success" > Pillbox - + } + > + setActivePillbox(pb)} + /> + + + setDisplayType(DISPLAY_TYPE.Print)} + onChange={() => setActiveRxTab(TAB_KEY.Print)} size="sm" type="radio" - value={DISPLAY_TYPE.Print} + value={TAB_KEY.Print} variant="outline-success" > @@ -538,215 +247,12 @@ const MedicinePage = (props: IProps): JSX.Element | null => { {checkoutList.length > 0 && {checkoutList.length}} - - - - {displayType === DISPLAY_TYPE.Medicine && ( - addEditDrugLog()} - canvasId="med-barcode" - clientId={clientId} - disabled={isBusy} - editMedicine={(medicineRecord) => setShowMedicineEdit(medicineRecord)} - itemChanged={(id) => { - if (id < 0) { - setActivePillbox(pillboxList.find((p) => p.Id === Math.abs(id)) || null); - setDisplayType(DISPLAY_TYPE.Pillbox); - } else { - setActiveMed(medicineList.find((m) => m.Id === id) || null); - } - }} - itemList={medItemList} - lastTaken={activeMed?.Id ? calculateLastTaken(activeMed.Id, drugLogList) : null} - logDrug={(n) => handleLogDrugAmount(n)} - /> - )} - - {displayType === DISPLAY_TYPE.OTC && ( - setShowMedicineEdit(medicineRecord)} - logOtcDrug={() => addEditDrugLog(undefined, true)} - logOtcDrugAmount={(n) => handleLogDrugAmount(n, true)} - otcList={otcList} - otcSelected={(medicineRecord) => setActiveOtc(medicineRecord)} - /> - )} - - {displayType === DISPLAY_TYPE.Pillbox && ( - m.Active), - pillboxList, - pillboxItemList, - drugLogList - }} - logPillbox={() => handleLogPillbox()} - onDelete={(pillboxId) => deletePillbox(pillboxId)} - onEdit={(pillboxRecord) => savePillbox(pillboxRecord)} - onSelect={(pillboxId) => - setActivePillbox(pillboxList.find((pb) => pb.Id === pillboxId) || null) - } - pillboxMedLogList={pillboxMedLogList} - /> - )} - - {displayType === DISPLAY_TYPE.History && activeClient && activeClient.clientInfo && ( - - - addEditDrugLog(drugLogRecord)} - onDelete={(drugLogRecord) => setShowDeleteDrugLogRecord(drugLogRecord)} - onPillClick={(pillboxId) => handleOnPillClick(pillboxId)} - /> - - - )} - - {displayType === DISPLAY_TYPE.Print && activeClient && activeClient?.clientInfo && ( - - )} - - - - {displayType !== DISPLAY_TYPE.Print && displayType !== DISPLAY_TYPE.History && ( - - {displayType === DISPLAY_TYPE.Medicine && ( - - - - - - {activeMed?.Id && ( - setShowDeleteDrugLogRecord(drugLogRecord)} - onEdit={(r) => addEditDrugLog(r)} - onPillClick={(n) => handleOnPillClick(n)} - /> - )} - - )} - - {displayType === DISPLAY_TYPE.OTC && ( - -
- OTC History -
- setShowDeleteDrugLogRecord(drugLogRecord)} - onEdit={(drugLogRecord) => addEditDrugLog(drugLogRecord, true)} - gridLists={{ - medicineList: otcList, - drugLogList: otcLogList, - pillboxList: undefined, - pillboxItemList: undefined - }} - /> -
- )} - - {displayType === DISPLAY_TYPE.Pillbox && activePillbox && activePillbox.Id && ( - savePillboxItem(pillboxItemRecord)} - pillboxItemList={pillboxItemList} - /> - )} -
- )} - - - d.MedicineId === showMedicineEdit?.Id)} - drugInfo={showMedicineEdit as MedicineRecord} - existingDrugs={showMedicineEdit?.Id === null ? medicineList.map((m) => m.Drug) : []} - fullName={clientFullName(activeClient.clientInfo)} - onClose={(medicineRecord) => { - setShowMedicineEdit(null); - if (medicineRecord) saveMedicine(medicineRecord); - }} - show={showMedicineEdit !== null} - /> - - { - setShowDrugLog(null); - if (drugLogRecord) - saveDrugLog(drugLogRecord).then((updatedDrugLogRecord) => setToast([updatedDrugLogRecord])); - }} - onHide={() => setShowDrugLog(null)} - otc={getMedicineRecord(showDrugLog?.MedicineId as number, medicineOtcList)?.OTC || false} - show={showDrugLog !== null} - /> - - { - setShowDeleteDrugLogRecord(null); - if (drugLogRecord) - mm.deleteDrugLog(showDeleteDrugLogRecord?.Id as number).then(() => { - mm.loadDrugLog(clientId, 5).then((drugLogRecords) => { - setActiveClient({...activeClient, drugLogList: drugLogRecords}); - }); - }); - }} - show={showDeleteDrugLogRecord !== null} - /> - - m.Id === showDeleteMedicine) as MedicineRecord} - onSelect={(medicineId) => { - setShowDeleteMedicine(0); - if (medicineId > 0) { - deleteMedicine(medicineId).then(() => { - if (medicineList.length > 0) setActiveMed(medicineList[0]); - else setActiveMed(null); - }); } - }} - show={showDeleteMedicine !== 0} - /> - - setToast(null)} - show={toast !== null} - toast={toast as DrugLogRecord[]} - /> - + > + +
+
+
); }; diff --git a/src/components/Pages/Modals/MedicineEdit.tsx b/src/components/Pages/Modals/MedicineEdit.tsx index 9ed2b53b..f7ae6a16 100644 --- a/src/components/Pages/Modals/MedicineEdit.tsx +++ b/src/components/Pages/Modals/MedicineEdit.tsx @@ -245,6 +245,7 @@ const MedicineEdit = (props: IProps): JSX.Element | null => { handleOnChange(e)} placeholder="e.g. 100 MG TABS" diff --git a/src/components/Pages/RxTabs/RxHistory.tsx b/src/components/Pages/RxTabs/RxHistory.tsx new file mode 100644 index 00000000..f2b3cbe3 --- /dev/null +++ b/src/components/Pages/RxTabs/RxHistory.tsx @@ -0,0 +1,109 @@ +import MedDrugLogHistory from 'components/Pages/Grids/MedDrugLogHistory'; +import DeleteDrugLogModal from 'components/Pages/Modals/DeleteDrugLogModal'; +import DrugLogEdit from 'components/Pages/Modals/DrugLogEdit'; +import DrugLogToast from 'components/Pages/Toasts/DrugLogToast'; +import {IMedicineManager} from 'managers/MedicineManager'; +import React, {useGlobal, useState} from 'reactn'; +import {TClient} from 'reactn/default'; +import {DrugLogRecord, MedicineRecord} from 'types/RecordTypes'; +import {asyncWrapper, getDrugName, getMedicineRecord} from 'utility/common'; + +interface IProps { + mm: IMedicineManager; + onPillboxSelected: (id: number) => void; + otcList: MedicineRecord[]; +} + +/** + * RxHistory Tab - Shows a grid of drugs logged for the past 5 days + * @param {IProps} props The props for this component + */ +const RxHistory = (props: IProps) => { + const {mm, onPillboxSelected, otcList} = props; + const [, setErrorDetails] = useGlobal('__errorDetails'); + const [activeClient, setActiveClient] = useGlobal('activeClient'); + const [isBusy, setIsBusy] = useState(false); + const [showDeleteDrugLogRecord, setShowDeleteDrugLogRecord] = useState(null); + const [showDrugLog, setShowDrugLog] = useState(null); + const [toast, setToast] = useState(null); + + /** + * Given a DrugLogRecord Update or Insert the record and rehydrate the drugLogList + * @param {DrugLogRecord} drugLog Druglog record object + */ + const saveDrugLog = async (drugLog: DrugLogRecord): Promise => { + await setIsBusy(true); + const [e, updatedDrugLog] = (await asyncWrapper(mm.updateDrugLog(drugLog))) as [ + unknown, + Promise + ]; + if (e) await setErrorDetails(e); + else { + const [errLoadLog, drugLogs] = (await asyncWrapper( + mm.loadDrugLog(activeClient?.clientInfo?.Id as number, 5) + )) as [unknown, Promise]; + if (errLoadLog) await setErrorDetails(errLoadLog); + else await setActiveClient({...(activeClient as TClient), drugLogList: await drugLogs}); + } + await setIsBusy(false); + return await updatedDrugLog; + }; + + const medicineOtcList = activeClient?.medicineList.concat(otcList) as MedicineRecord[]; + + if (activeClient === null) return null; + return ( + <> + setShowDrugLog({...drugLogRecord})} + onDelete={(drugLogRecord) => setShowDeleteDrugLogRecord(drugLogRecord)} + onPillClick={(pillboxId) => onPillboxSelected(pillboxId)} + /> + + { + setShowDrugLog(null); + if (drugLogRecord) + saveDrugLog(drugLogRecord).then((updatedDrugLogRecord) => setToast([updatedDrugLogRecord])); + }} + onHide={() => setShowDrugLog(null)} + otc={getMedicineRecord(showDrugLog?.MedicineId as number, medicineOtcList)?.OTC || false} + show={showDrugLog !== null} + /> + + { + setShowDeleteDrugLogRecord(null); + if (drugLogRecord) + mm.deleteDrugLog(showDeleteDrugLogRecord?.Id as number).then(() => { + mm.loadDrugLog(activeClient?.clientInfo?.Id as number, 5).then((drugLogRecords) => { + setActiveClient({...activeClient, drugLogList: drugLogRecords}); + }); + }); + }} + show={showDeleteDrugLogRecord !== null} + /> + + setToast(null)} + show={toast !== null} + toast={toast as DrugLogRecord[]} + /> + + ); +}; + +export default RxHistory; diff --git a/src/components/Pages/RxTabs/RxMedicine.tsx b/src/components/Pages/RxTabs/RxMedicine.tsx new file mode 100644 index 00000000..34d0529a --- /dev/null +++ b/src/components/Pages/RxTabs/RxMedicine.tsx @@ -0,0 +1,277 @@ +import LastTakenButton from 'components/Pages/Buttons/LastTakenButton'; +import DrugLogGrid from 'components/Pages/Grids/DrugLogGrid'; +import {IDropdownItem} from 'components/Pages/ListGroups/MedDropdown'; +import MedListGroup from 'components/Pages/ListGroups/MedListGroup'; +import DeleteDrugLogModal from 'components/Pages/Modals/DeleteDrugLogModal'; +import DrugLogEdit from 'components/Pages/Modals/DrugLogEdit'; +import MedicineEdit from 'components/Pages/Modals/MedicineEdit'; +import DrugLogToast from 'components/Pages/Toasts/DrugLogToast'; +import {IMedicineManager} from 'managers/MedicineManager'; +import Button from 'react-bootstrap/Button'; +import Col from 'react-bootstrap/Col'; +import ListGroup from 'react-bootstrap/ListGroup'; +import Row from 'react-bootstrap/Row'; +import React, {useEffect, useGlobal, useState} from 'reactn'; +import {TClient} from 'reactn/default'; +import {DrugLogRecord, MedicineRecord, newDrugLogRecord} from 'types/RecordTypes'; +import {asyncWrapper, calculateLastTaken, clientFullName, getCheckoutList, getDrugName, isToday} from 'utility/common'; + +interface IProps { + mm: IMedicineManager; + pillboxSelected: (id: number) => void; +} + +/** + * The RxMedicine tab - Displays the drug dropdown and drug log grid + * @param {IProps} props The props for this component + */ +const RxMedicine = (props: IProps) => { + const [, setErrorDetails] = useGlobal('__errorDetails'); + const [activeClient, setActiveClient] = useGlobal('activeClient'); + const [activeMed, setActiveMed] = useState(null); + const [isBusy, setIsBusy] = useState(false); + const [medItemList, setMedItemList] = useState([]); + const [showDeleteDrugLogRecord, setShowDeleteDrugLogRecord] = useState(null); + const [showDrugLog, setShowDrugLog] = useState(null); + const [showMedicineEdit, setShowMedicineEdit] = useState(null); + const [toast, setToast] = useState(null); + const clientId = activeClient?.clientInfo.Id; + const {drugLogList, pillboxList, pillboxItemList, medicineList} = activeClient as TClient; + const {mm, pillboxSelected} = props; + + // Build the dropdown items for the Medicine dropdown + useEffect(() => { + const itemList = [] as IDropdownItem[]; + if (activeClient) { + const {drugLogList, pillboxList, pillboxItemList, medicineList} = activeClient; + const checkoutList = getCheckoutList(drugLogList); + + // Build the itemList with any pillboxes and meds from medicineList + let pbCnt = 0; + pillboxList.forEach((p) => { + const pbItems = pillboxItemList.filter((pbi) => pbi.PillboxId === p.Id); + const loggedPillboxItems = drugLogList.filter( + (d) => d.Updated && isToday(d.Updated) && pbItems.find((pbi) => pbi.Id === d.PillboxItemId) + ); + if (loggedPillboxItems.length === 0) { + itemList.push({ + id: -(p.Id as number), + description: p.Name.toUpperCase(), + subtext: null + }); // Pillbox have negative id + pbCnt++; + } + }); + if (pbCnt > 0) itemList.push({id: 0, description: 'divider', subtext: null}); + + medicineList.forEach((m) => { + if (m.Active) { + const strength = m.Strength || ''; + const other = m.OtherNames?.length > 0 ? `(${m.OtherNames})` : null; + const checkoutMed = checkoutList.find((c) => c.MedicineId === m.Id); + const description = (checkoutMed ? 'โŽ ' : '') + m.Drug + ' ' + strength; + itemList.push({ + id: m.Id as number, + description, + subtext: other + }); + } + }); + + // If activeMed is null, and we have med items in the list then set the initial activeMed to the first item + if (activeMed === null && itemList.length > 0) { + const medsOnly = itemList.filter((i) => i.id > 0); + setActiveMed(medsOnly.length === 0 ? null : medicineList.find((m) => m.Id === medsOnly[0].id) || null); + } + } else { + setActiveMed(null); + } + setMedItemList(itemList); + }, [activeClient, activeMed]); + + /** + * Given a MedicineRecord object Update/Insert the record and rehydrate the global otcList / medicineList + * Set the activeOtc or the activeMed with the updated medicine (if Active) + * @param {MedicineRecord} med Medicine record object + */ + const saveMedicine = async (med: MedicineRecord) => { + await setIsBusy(true); + const [e, m] = (await asyncWrapper(mm.updateMedicine(med))) as [unknown, Promise]; + if (e) await setErrorDetails(e); + const updatedMedicineRecord = await m; + + const [errLoadMeds, meds] = (await asyncWrapper(mm.loadMedicineList(clientId as number))) as [ + unknown, + Promise + ]; + if (errLoadMeds) await setErrorDetails(errLoadMeds); + else await setActiveClient({...(activeClient as TClient), medicineList: await meds}); + const activeMeds = (await meds).filter((m) => m.Active); + setActiveMed( + updatedMedicineRecord.Active ? updatedMedicineRecord : activeMeds.length === 0 ? null : activeMeds[0] + ); + await setIsBusy(false); + }; + + /** + * Given a DrugLogRecord Update or Insert the record and rehydrate the drugLogList + * @param {DrugLogRecord} drugLog Druglog record object + */ + const saveDrugLog = async (drugLog: DrugLogRecord): Promise => { + await setIsBusy(true); + const [e, updatedDrugLog] = (await asyncWrapper(mm.updateDrugLog(drugLog))) as [ + unknown, + Promise + ]; + if (e) await setErrorDetails(e); + else { + const [errLoadLog, drugLogs] = (await asyncWrapper(mm.loadDrugLog(clientId as number, 5))) as [ + unknown, + Promise + ]; + if (errLoadLog) await setErrorDetails(errLoadLog); + else await setActiveClient({...(activeClient as TClient), drugLogList: await drugLogs}); + } + await setIsBusy(false); + return await updatedDrugLog; + }; + + /** + * Fires when the Log 1...4 buttons are clicked. + * @param {number} amount The number of pills (medication) taken + */ + const handleLogDrugAmount = (amount: number) => { + setIsBusy(true); + const medicineId = activeMed?.Id; + const drugLogInfo = {...newDrugLogRecord}; + drugLogInfo.MedicineId = medicineId as number; + drugLogInfo.Notes = amount.toString(); + drugLogInfo.ResidentId = clientId as number; + saveDrugLog(drugLogInfo).then((d) => setToast([d])); + setIsBusy(false); + }; + + /** + * Fires when user clicks on +Log or the drug log edit button + * @param {DrugLogRecord} drugLogInfo The drugLog record object + */ + const handleAddEditDrugLog = (drugLogInfo?: DrugLogRecord) => { + const drugLogRecord = drugLogInfo + ? {...drugLogInfo} + : ({ + Id: null, + ResidentId: clientId, + MedicineId: activeMed?.Id, + Notes: '' + } as DrugLogRecord); + setShowDrugLog(drugLogRecord); + }; + + if (activeClient === null) return null; + return ( + <> + + + handleAddEditDrugLog()} + canvasId="med-barcode" + clientId={clientId as number} + disabled={isBusy} + editMedicine={(medicineRecord) => setShowMedicineEdit(medicineRecord)} + itemChanged={(id) => { + if (id < 0) { + pillboxSelected(Math.abs(id)); + } else { + setActiveMed(medicineList.find((m) => m.Id === id) || null); + } + }} + itemList={medItemList} + lastTaken={activeMed?.Id ? calculateLastTaken(activeMed.Id, drugLogList) : null} + logDrug={(n) => handleLogDrugAmount(n)} + /> + + + + + + + + + {activeMed?.Id && ( + setShowDeleteDrugLogRecord(drugLogRecord)} + onEdit={(r) => handleAddEditDrugLog(r)} + onPillClick={(n) => pillboxSelected(n)} + /> + )} + + + + + { + setShowDeleteDrugLogRecord(null); + if (drugLogRecord) { + mm.deleteDrugLog(showDeleteDrugLogRecord?.Id as number).then(() => { + mm.loadDrugLog(clientId as number, 5).then((drugLogRecords) => { + setActiveClient({...(activeClient as TClient), drugLogList: drugLogRecords}); + }); + }); + } + }} + show={showDeleteDrugLogRecord !== null} + /> + + d.MedicineId === showMedicineEdit?.Id)} + drugInfo={showMedicineEdit as MedicineRecord} + existingDrugs={showMedicineEdit?.Id === null ? medicineList.map((m) => m.Drug) : []} + fullName={clientFullName(activeClient.clientInfo)} + onClose={(medicineRecord) => { + setShowMedicineEdit(null); + if (medicineRecord) saveMedicine(medicineRecord); + }} + show={showMedicineEdit !== null} + /> + + setToast(null)} + show={toast !== null} + toast={toast as DrugLogRecord[]} + /> + + { + setShowDrugLog(null); + if (drugLogRecord) + saveDrugLog(drugLogRecord).then((updatedDrugLogRecord) => setToast([updatedDrugLogRecord])); + }} + onHide={() => setShowDrugLog(null)} + show={showDrugLog !== null} + /> + + ); +}; + +export default RxMedicine; diff --git a/src/components/Pages/RxTabs/RxOtc.tsx b/src/components/Pages/RxTabs/RxOtc.tsx new file mode 100644 index 00000000..bb857d23 --- /dev/null +++ b/src/components/Pages/RxTabs/RxOtc.tsx @@ -0,0 +1,203 @@ +import DrugLogGrid from 'components/Pages/Grids/DrugLogGrid'; +import OtcListGroup from 'components/Pages/ListGroups/OtcListGroup'; +import DeleteDrugLogModal from 'components/Pages/Modals/DeleteDrugLogModal'; +import DrugLogEdit from 'components/Pages/Modals/DrugLogEdit'; +import MedicineEdit from 'components/Pages/Modals/MedicineEdit'; +import DrugLogToast from 'components/Pages/Toasts/DrugLogToast'; +import {IMedicineManager} from 'managers/MedicineManager'; +import Col from 'react-bootstrap/Col'; +import ListGroup from 'react-bootstrap/ListGroup'; +import Row from 'react-bootstrap/Row'; +import React, {useGlobal, useState} from 'reactn'; +import {TClient} from 'reactn/default'; +import {DrugLogRecord, MedicineRecord, newDrugLogRecord} from 'types/RecordTypes'; +import {asyncWrapper, clientFullName, getDrugName, getMedicineRecord} from 'utility/common'; + +interface IProps { + mm: IMedicineManager; +} + +/** + * The RxOtc tab - Displays the OTC drug dropdown and drug log grid + * @param {IProps} props The props for this component + */ +const RxOtc = (props: IProps) => { + const [, setErrorDetails] = useGlobal('__errorDetails'); + const [activeClient, setActiveClient] = useGlobal('activeClient'); + const [activeOtc, setActiveOtc] = useState(null); + const [isBusy, setIsBusy] = useState(false); + const [otcList, setOtcList] = useGlobal('otcList'); + const [showDeleteDrugLogRecord, setShowDeleteDrugLogRecord] = useState(null); + const [showDrugLog, setShowDrugLog] = useState(null); + const [showMedicineEdit, setShowMedicineEdit] = useState(null); + const [toast, setToast] = useState(null); + const clientId = activeClient?.clientInfo.Id; + const mm = props.mm; + + /** + * Fires when the Log 1...4 buttons are clicked. + * @param {number} amount The number of pills (medication) taken + */ + const handleLogOtcDrugAmount = (amount: number) => { + const medicineId = activeOtc?.Id; + const drugLogInfo = {...newDrugLogRecord}; + drugLogInfo.MedicineId = medicineId as number; + drugLogInfo.Notes = amount.toString(); + drugLogInfo.ResidentId = clientId as number; + saveDrugLog(drugLogInfo).then((r) => setToast([r])); + }; + + /** + * Given a DrugLogRecord Update or Insert the record and rehydrate the drugLogList + * @param {DrugLogRecord} drugLog Druglog record object + */ + const saveDrugLog = async (drugLog: DrugLogRecord): Promise => { + await setIsBusy(true); + const [e, updatedDrugLog] = (await asyncWrapper(mm.updateDrugLog(drugLog))) as [ + unknown, + Promise + ]; + if (e) await setErrorDetails(e); + else { + const [errLoadLog, drugLogs] = (await asyncWrapper(mm.loadDrugLog(clientId as number, 5))) as [ + unknown, + Promise + ]; + if (errLoadLog) await setErrorDetails(errLoadLog); + else await setActiveClient({...(activeClient as TClient), drugLogList: await drugLogs}); + } + await setIsBusy(false); + return await updatedDrugLog; + }; + + /** + * Given a MedicineRecord object Update/Insert the record and rehydrate the global otcList / medicineList + * Set the activeOtc with the updated medicine (if Active) + * @param {MedicineRecord} med Medicine record object + */ + const saveMedicine = async (med: MedicineRecord) => { + await setIsBusy(true); + const [e, m] = (await asyncWrapper(mm.updateMedicine(med))) as [unknown, Promise]; + if (e) await setErrorDetails(e); + const updatedMedicineRecord = await m; + const [errLoadOtc, otcMeds] = (await asyncWrapper(mm.loadOtcList())) as [unknown, Promise]; + if (errLoadOtc) await setErrorDetails(errLoadOtc); + else await setOtcList(await otcMeds); + setActiveOtc(updatedMedicineRecord.Active ? updatedMedicineRecord : null); + await setIsBusy(false); + }; + + /** + * Fires when user clicks on +Log or the drug log edit button + * @param {DrugLogRecord} drugLogInfo The drugLog record object + */ + const addEditDrugLog = (drugLogInfo?: DrugLogRecord) => { + const drugLogRecord = drugLogInfo + ? {...drugLogInfo} + : ({ + Id: null, + ResidentId: clientId, + MedicineId: activeOtc?.Id, + Notes: '' + } as DrugLogRecord); + setShowDrugLog(drugLogRecord); + }; + + const medicineOtcList = activeClient?.medicineList.concat(otcList) as MedicineRecord[]; + + if (activeClient === null) return null; + return ( + <> + + + setShowMedicineEdit(medicineRecord)} + logOtcDrug={() => addEditDrugLog(undefined)} + logOtcDrugAmount={(n) => handleLogOtcDrugAmount(n)} + otcList={otcList} + otcSelected={(medicineRecord) => setActiveOtc(medicineRecord)} + /> + + + + +
+ OTC Log History +
+ setShowDeleteDrugLogRecord(drugLogRecord)} + onEdit={(drugLogRecord) => addEditDrugLog(drugLogRecord)} + gridLists={{ + medicineList: otcList, + drugLogList: activeClient.drugLogList.filter((drug: DrugLogRecord) => { + return otcList.some((m) => { + return m.Id === drug?.MedicineId; + }); + }), + pillboxList: undefined, + pillboxItemList: undefined + }} + /> +
+
+
+ + d.MedicineId === showMedicineEdit?.Id)} + drugInfo={showMedicineEdit as MedicineRecord} + existingDrugs={showMedicineEdit?.Id === null ? otcList.map((m) => m.Drug) : []} + fullName={clientFullName(activeClient.clientInfo)} + onClose={(medicineRecord) => { + setShowMedicineEdit(null); + if (medicineRecord) saveMedicine(medicineRecord); + }} + show={showMedicineEdit !== null} + /> + + { + setShowDrugLog(null); + if (drugLogRecord) + saveDrugLog(drugLogRecord).then((updatedDrugLogRecord) => setToast([updatedDrugLogRecord])); + }} + onHide={() => setShowDrugLog(null)} + otc={getMedicineRecord(showDrugLog?.MedicineId as number, medicineOtcList)?.OTC || false} + show={showDrugLog !== null} + /> + + { + setShowDeleteDrugLogRecord(null); + if (drugLogRecord) + mm.deleteDrugLog(showDeleteDrugLogRecord?.Id as number).then(() => { + mm.loadDrugLog(clientId as number, 5).then((drugLogRecords) => { + setActiveClient({...activeClient, drugLogList: drugLogRecords}); + }); + }); + }} + show={showDeleteDrugLogRecord !== null} + /> + + setToast(null)} + show={toast !== null} + toast={toast as DrugLogRecord[]} + /> + + ); +}; + +export default RxOtc; diff --git a/src/components/Pages/RxTabs/RxPillbox.tsx b/src/components/Pages/RxTabs/RxPillbox.tsx new file mode 100644 index 00000000..bb308e51 --- /dev/null +++ b/src/components/Pages/RxTabs/RxPillbox.tsx @@ -0,0 +1,224 @@ +import PillboxCard from 'components/Pages/Grids/PillboxCard'; +import PillboxListGroup from 'components/Pages/ListGroups/PillboxListGroup'; +import DrugLogToast from 'components/Pages/Toasts/DrugLogToast'; +import {IMedicineManager} from 'managers/MedicineManager'; +import Col from 'react-bootstrap/Col'; +import ListGroup from 'react-bootstrap/ListGroup'; +import Row from 'react-bootstrap/Row'; +import React, {useEffect, useGlobal, useState} from 'reactn'; +import {TClient} from 'reactn/default'; +import TabContent from 'styles/common.css'; +import {DrugLogRecord, PillboxItemRecord, PillboxRecord} from 'types/RecordTypes'; +import {asyncWrapper, isToday, multiSort, SortDirection} from 'utility/common'; + +export type TPillboxMedLog = { + Active: boolean; + Drug: string | undefined; + Notes: string | null; + PillboxId?: number | null; + PillboxItemId?: number | null; + Quantity: number; + Strength: string | null | undefined; + Updated: Date | null | undefined; +}; + +interface IProps { + activePillbox: PillboxRecord | null; + activePillboxChanged: (pb: PillboxRecord | null) => void; + mm: IMedicineManager; +} + +/** + * RxPillbox tab - Displays PillboxListGroup and PillboxCard + * @param {IProps} props The props for this component + */ +const RxPillbox = (props: IProps) => { + const [, setErrorDetails] = useGlobal('__errorDetails'); + const [activeClient, setActiveClient] = useGlobal('activeClient'); + const [isBusy, setIsBusy] = useState(false); + const [pillboxMedLogList, setPillboxMedLogList] = useState([]); + const [toast, setToast] = useState(null); + const activePillboxChanged = props.activePillboxChanged; + const mm = props.mm; + + const [activePillbox, setActivePillbox] = useState(props.activePillbox); + useEffect(() => { + setActivePillbox(props.activePillbox); + }, [props.activePillbox]); + + // Refresh the pillboxDrugLog[] + useEffect(() => { + if (activePillbox && activeClient) { + const pillboxMedLog = [] as TPillboxMedLog[]; + const pillboxItemList = activeClient.pillboxItemList; + const drugLogList = activeClient.drugLogList; + pillboxItemList.forEach((pbi) => { + if (pbi.PillboxId === activePillbox.Id && pbi.Quantity) { + const drugLogRecord = drugLogList.find( + (dlr) => + dlr.PillboxItemId === pbi.Id && + dlr.MedicineId === pbi.MedicineId && + dlr.Updated && + isToday(dlr.Updated) + ); + + if (drugLogRecord) { + const med = activeClient.medicineList.find((m) => m.Id === drugLogRecord.MedicineId); + pillboxMedLog.push({ + Active: !!med?.Active, + Drug: med?.Drug, + Strength: med?.Strength, + Quantity: pbi.Quantity, + Notes: drugLogRecord.Notes, + PillboxItemId: drugLogRecord.PillboxItemId, + PillboxId: activePillbox.Id, + Updated: drugLogRecord.Updated + }); + } + } + }); + setPillboxMedLogList(multiSort(pillboxMedLog, {Quantity: SortDirection.asc, Drug: SortDirection.desc})); + } + }, [activeClient, activePillbox, setPillboxMedLogList]); + + /** + * Add or update a pillboxRecord record. + * @param {PillboxRecord} pillboxRecord Pillbox record object + */ + const savePillbox = async (pillboxRecord: PillboxRecord) => { + setIsBusy(true); + const [e, updatedPillbox] = (await asyncWrapper(mm.updatePillbox(pillboxRecord))) as [ + unknown, + Promise + ]; + if (e) await setErrorDetails(e); + else { + activePillboxChanged(await updatedPillbox); + } + const [errLoadPillbox, pillboxes] = (await asyncWrapper( + mm.loadPillboxList(activeClient?.clientInfo.Id as number) + )) as [unknown, Promise]; + if (errLoadPillbox) await setErrorDetails(errLoadPillbox); + else await setActiveClient({...(activeClient as TClient), pillboxList: await pillboxes}); + setIsBusy(false); + }; + + /** + * Delete an existing pillbox. + * @param {number} pillboxId The PK for the Pillbox table + */ + const deletePillbox = async (pillboxId: number) => { + setIsBusy(true); + const [e, pillboxDeleted] = (await asyncWrapper(mm.deletePillbox(pillboxId))) as [unknown, Promise]; + if (e) await setErrorDetails(e); + else if (await pillboxDeleted) { + const [errLoad, pillboxes] = (await asyncWrapper( + mm.loadPillboxList(activeClient?.clientInfo.Id as number) + )) as [unknown, Promise]; + if (errLoad) await setErrorDetails(errLoad); + else { + const pillboxList = await pillboxes; + await setActiveClient({...(activeClient as TClient), pillboxList}); + activePillboxChanged(pillboxList.length > 0 ? pillboxList[0] : null); + } + } else { + await setErrorDetails(new Error('Unable to delete Pillbox. Id: ' + pillboxId)); + } + setIsBusy(false); + }; + + /** + * Add or update a pillboxItem record + * @param {PillboxItemRecord} pillboxItemRecord The pillboxItem record object + */ + const savePillboxItem = async (pillboxItemRecord: PillboxItemRecord) => { + setIsBusy(true); + const [e, updatedPillboxItem] = (await asyncWrapper(mm.updatePillboxItem(pillboxItemRecord))) as [ + unknown, + Promise + ]; + if (e) await setErrorDetails(e); + else if (await updatedPillboxItem) { + const [errLoadPills, pillboxItems] = (await asyncWrapper( + mm.loadPillboxItemList(activeClient?.clientInfo.Id as number) + )) as [unknown, Promise]; + if (errLoadPills) await setErrorDetails(errLoadPills); + else await setActiveClient({...(activeClient as TClient), pillboxItemList: await pillboxItems}); + } + setIsBusy(false); + }; + + /** + * Handle when the user clicks on Log Pillbox + */ + const handleLogPillbox = async () => { + setIsBusy(true); + const toastQ = [] as DrugLogRecord[]; + const [e, loggedPillboxMeds] = (await asyncWrapper(mm.logPillbox(activePillbox?.Id as number))) as [ + unknown, + Promise + ]; + if (e) await setErrorDetails(e); + else { + const loggedPillboxDrugs = await loggedPillboxMeds; + if (loggedPillboxDrugs.length > 0) { + const [errLoadLog, drugLogs] = (await asyncWrapper( + mm.loadDrugLog(activeClient?.clientInfo.Id as number, 5) + )) as [unknown, Promise]; + if (errLoadLog) await setErrorDetails(errLoadLog); + else await setActiveClient({...(activeClient as TClient), drugLogList: await drugLogs}); + loggedPillboxDrugs.forEach((ld) => toastQ.push({...ld})); + setToast(toastQ); + } + } + setIsBusy(false); + }; + + if (activeClient === null) return null; + return ( + <> + + + m.Active), + pillboxList: activeClient.pillboxList, + pillboxItemList: activeClient.pillboxItemList, + drugLogList: activeClient.drugLogList + }} + logPillbox={() => handleLogPillbox()} + onDelete={(pillboxId) => deletePillbox(pillboxId)} + onEdit={(pillboxRecord) => savePillbox(pillboxRecord)} + onSelect={(pillboxId) => + activePillboxChanged(activeClient.pillboxList.find((pb) => pb.Id === pillboxId) || null) + } + pillboxMedLogList={pillboxMedLogList} + /> + + + + {activePillbox && activePillbox.Id && ( + savePillboxItem(pillboxItemRecord)} + pillboxItemList={activeClient.pillboxItemList} + /> + )} + + + + setToast(null)} + show={toast !== null} + toast={toast as DrugLogRecord[]} + /> + + ); +}; + +export default RxPillbox; diff --git a/src/components/Pages/RxTabs/RxPrint.tsx b/src/components/Pages/RxTabs/RxPrint.tsx new file mode 100644 index 00000000..082c7aca --- /dev/null +++ b/src/components/Pages/RxTabs/RxPrint.tsx @@ -0,0 +1,29 @@ +import CheckoutListGroup from 'components/Pages/ListGroups/CheckoutListGroup'; +import React from 'reactn'; +import {TClient} from 'reactn/default'; +import {DrugLogRecord} from 'types/RecordTypes'; + +interface IProps { + activeClient: TClient; + checkoutList: DrugLogRecord[]; +} + +/** + * RxPrint Tab -- Displays the Medication Checkout grid and signature printout + * @param {IProps} props - The props for this component + */ +const RxPrint = (props: IProps) => { + const activeClient = props.activeClient; + const checkoutList = props.checkoutList; + + if (activeClient === null) return null; + return ( + + ); +}; + +export default RxPrint;