From 27e2791c8c31e4a2e423b99fbab5e4c48569a19f Mon Sep 17 00:00:00 2001 From: Kat Date: Thu, 5 May 2022 15:05:35 +1200 Subject: [PATCH 001/131] Add a button to save changes --- src/widgets/modals/VaccinationEvent.js | 38 +++++++++++++++++++++----- 1 file changed, 31 insertions(+), 7 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index bf11faae3..9247edba1 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -11,8 +11,14 @@ import { UIDatabase } from '../../database'; import { selectMostRecentNameNote } from '../../selectors/Entities/nameNote'; import { FlexColumn } from '../FlexColumn'; import { BreachManUnhappy } from '../BreachManUnhappy'; -import { APP_FONT_FAMILY, DARKER_GREY, GREY } from '../../globalStyles'; -import { generalStrings } from '../../localization'; +import globalStyles, { + APP_FONT_FAMILY, + DARKER_GREY, + GREY, + SUSSOL_ORANGE, +} from '../../globalStyles'; +import { buttonStrings, generalStrings } from '../../localization'; +import { PageButton } from '../PageButton'; // It's possible to get into this state if vaccination events were configured but PCD events weren't // and someone dispensed a vaccine. Some data cleanup may be required. @@ -62,7 +68,7 @@ export const VaccinationEventComponent = ({ {!!vaccinationEventSchema && !!vaccinationEvent && ( - + )} - + {!!surveySchema && !!surveyForm ? ( - + @@ -95,6 +100,14 @@ export const VaccinationEventComponent = ({ + + console.log('button pressed')} + style={localStyles.saveButton} + textStyle={localStyles.saveButtonTextStyle} + /> + ); }; @@ -112,13 +125,24 @@ const mapStateToProps = () => { }; }; -const styles = StyleSheet.create({ +const localStyles = StyleSheet.create({ formContainer: { flex: 1, flexDirection: 'row', backgroundColor: 'white', alignItems: 'stretch', }, + saveButton: { + ...globalStyles.button, + flex: 1, + backgroundColor: SUSSOL_ORANGE, + alignSelf: 'center', + }, + saveButtonTextStyle: { + ...globalStyles.buttonText, + color: 'white', + fontSize: 14, + }, }); VaccinationEventComponent.defaultProps = { From 22875d6fa6fb391f5bf28f1b580962c76a59e9a6 Mon Sep 17 00:00:00 2001 From: Kat Date: Mon, 16 May 2022 17:16:11 +1200 Subject: [PATCH 002/131] Add a function to update the linked survey name note --- src/actions/Entities/NameNoteActions.js | 15 +++++++++++++ src/widgets/modals/VaccinationEvent.js | 29 +++++++++++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/actions/Entities/NameNoteActions.js b/src/actions/Entities/NameNoteActions.js index 5b57cb7df..57adf41b8 100644 --- a/src/actions/Entities/NameNoteActions.js +++ b/src/actions/Entities/NameNoteActions.js @@ -67,6 +67,20 @@ const saveEditing = () => (dispatch, getState) => { dispatch(reset()); }; +const updateLinkedSurveyNameNote = (originalNote, updatedData) => () => { + const { id, patientEvent, name, entryDate } = originalNote; + + const updatedNote = { + id, + patientEvent, + name, + entryDate: new Date(entryDate), + _data: JSON.stringify(updatedData), + }; + + UIDatabase.write(() => UIDatabase.update('NameNote', updatedNote)); +}; + const createNotes = (nameNotes = []) => { UIDatabase.write(() => { nameNotes.forEach(nameNote => { @@ -99,5 +113,6 @@ export const NameNoteActions = { reset, createSurveyNameNote, updateForm, + updateLinkedSurveyNameNote, saveEditing, }; diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 9247edba1..44a3eff1b 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -1,5 +1,5 @@ /* eslint-disable react/forbid-prop-types */ -import React, { useRef } from 'react'; +import React, { useRef, useState } from 'react'; import { View, StyleSheet, Text } from 'react-native'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; @@ -19,6 +19,7 @@ import globalStyles, { } from '../../globalStyles'; import { buttonStrings, generalStrings } from '../../localization'; import { PageButton } from '../PageButton'; +import { NameNoteActions } from '../../actions'; // It's possible to get into this state if vaccination events were configured but PCD events weren't // and someone dispensed a vaccine. Some data cleanup may be required. @@ -41,13 +42,17 @@ export const VaccinationEventComponent = ({ patient, vaccinationEvent, vaccinationEventSchema, + saveForm, surveySchema, }) => { const pcdFormRef = useRef(null); const vaccinationFormRef = useRef(null); + const [{ updatedPcdForm, isPCDValid }, setPCDForm] = useState({ + updatedPcdForm: null, + isPCDValid: false, + }); const { pcdNameNoteId } = vaccinationEvent; - const surveyForm = pcdNameNoteId ? UIDatabase.get('NameNote', pcdNameNoteId) : selectMostRecentNameNote(patient, 'PCD', vaccinationEvent.entryDate); @@ -89,6 +94,12 @@ export const VaccinationEventComponent = ({ ref={pcdFormRef} formData={surveyForm.data ?? null} surveySchema={surveySchema} + onChange={(changed, validator) => { + setPCDForm({ + updatedPcdForm: changed.formData, + isPCDValid: validator(changed.formData), + }); + }} > <> @@ -103,9 +114,10 @@ export const VaccinationEventComponent = ({ console.log('button pressed')} + onPress={() => (isPCDValid ? saveForm(surveyForm, updatedPcdForm) : null)} style={localStyles.saveButton} textStyle={localStyles.saveButtonTextStyle} + isDisabled={!isPCDValid} /> @@ -125,6 +137,11 @@ const mapStateToProps = () => { }; }; +const mapDispatchToProps = dispatch => ({ + saveForm: (oldSurveyNote, updatedSurveyData) => + dispatch(NameNoteActions.updateLinkedSurveyNameNote(oldSurveyNote, updatedSurveyData)), +}); + const localStyles = StyleSheet.create({ formContainer: { flex: 1, @@ -152,9 +169,13 @@ VaccinationEventComponent.defaultProps = { VaccinationEventComponent.propTypes = { patient: PropTypes.object, + saveForm: PropTypes.func.isRequired, surveySchema: PropTypes.object.isRequired, vaccinationEvent: PropTypes.object, vaccinationEventSchema: PropTypes.object.isRequired, }; -export const VaccinationEvent = connect(mapStateToProps, null)(VaccinationEventComponent); +export const VaccinationEvent = connect( + mapStateToProps, + mapDispatchToProps +)(VaccinationEventComponent); From 74b44ad13f0fc5830813a4dc8f0e3759234de9f0 Mon Sep 17 00:00:00 2001 From: Kat Date: Tue, 17 May 2022 14:40:22 +1200 Subject: [PATCH 003/131] Add a toast :bread: --- src/actions/Entities/NameNoteActions.js | 3 +++ src/localization/vaccineStrings.json | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/actions/Entities/NameNoteActions.js b/src/actions/Entities/NameNoteActions.js index 57adf41b8..e121e25e9 100644 --- a/src/actions/Entities/NameNoteActions.js +++ b/src/actions/Entities/NameNoteActions.js @@ -1,5 +1,6 @@ import { generateUUID } from 'react-native-database'; import merge from 'lodash.merge'; +import { ToastAndroid } from 'react-native'; import { createRecord, UIDatabase } from '../../database/index'; import { selectCreatingNameNote, @@ -7,6 +8,7 @@ import { } from '../../selectors/Entities/nameNote'; import { selectSurveySchemas } from '../../selectors/formSchema'; import { validateJsonSchemaData } from '../../utilities/ajvValidator'; +import { vaccineStrings } from '../../localization'; export const NAME_NOTE_ACTIONS = { SELECT: 'NAME_NOTE/select', @@ -79,6 +81,7 @@ const updateLinkedSurveyNameNote = (originalNote, updatedData) => () => { }; UIDatabase.write(() => UIDatabase.update('NameNote', updatedNote)); + ToastAndroid.show(vaccineStrings.vaccination_updated, ToastAndroid.LONG); }; const createNotes = (nameNotes = []) => { diff --git a/src/localization/vaccineStrings.json b/src/localization/vaccineStrings.json index e5ac9723a..c2aa049d5 100644 --- a/src/localization/vaccineStrings.json +++ b/src/localization/vaccineStrings.json @@ -74,7 +74,8 @@ "is_paused": "PAUSED", "pause": "Pause", "resume": "Resume", - "refusal_reason": "Reason for refusal" + "refusal_reason": "Reason for refusal", + "vaccination_updated": "Vaccination details updated" }, "fr": { "no_vaccine_stock": "Vous n'avez pas de vaccins en stock!", From 46d7fe5c25b7e9b088b658778a08a852eadad82c Mon Sep 17 00:00:00 2001 From: Kat Date: Tue, 17 May 2022 16:06:28 +1200 Subject: [PATCH 004/131] Undo package.json version bump (not for develop) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 84b27d69d..02f9cafaa 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ }, "name": "mSupplyMobile", "//": "version must be in the format ${majorNumber}.${minorNumber}.${patchNumber}-rc${releaseCandidateNumber}", - "version": "8.5.0-rc1", + "version": "8.4.2", "private": false, "license": "MIT", "description": "Mobile app for use with the mSupply medical inventory control software", From 3b3151b9e6a7c72ba4709962b082722fd1d2c975 Mon Sep 17 00:00:00 2001 From: Kat Date: Thu, 19 May 2022 11:19:25 +1200 Subject: [PATCH 005/131] Simplify button press logic --- src/widgets/modals/VaccinationEvent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 44a3eff1b..ba045b97f 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -114,7 +114,7 @@ export const VaccinationEventComponent = ({ (isPCDValid ? saveForm(surveyForm, updatedPcdForm) : null)} + onPress={() => saveForm(surveyForm, updatedPcdForm)} style={localStyles.saveButton} textStyle={localStyles.saveButtonTextStyle} isDisabled={!isPCDValid} From e8fd8dfd46b97e6708ff7068a284111402eda655 Mon Sep 17 00:00:00 2001 From: Kat Date: Thu, 19 May 2022 16:10:56 +1200 Subject: [PATCH 006/131] Add an audit name note when updating historical PCDs :scream_cat: --- src/actions/Entities/NameNoteActions.js | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/actions/Entities/NameNoteActions.js b/src/actions/Entities/NameNoteActions.js index e121e25e9..48bc90124 100644 --- a/src/actions/Entities/NameNoteActions.js +++ b/src/actions/Entities/NameNoteActions.js @@ -80,7 +80,30 @@ const updateLinkedSurveyNameNote = (originalNote, updatedData) => () => { _data: JSON.stringify(updatedData), }; - UIDatabase.write(() => UIDatabase.update('NameNote', updatedNote)); + const [auditEvent] = UIDatabase.objects('PatientEvent').filtered('code == "NameNoteModified"'); + + const auditNameNote = { + id: generateUUID(), + name, + auditEvent, + entryDate: new Date(), + _data: JSON.stringify({ + patientEvent, + old: { + entryDate, + data: originalNote.data, + }, + new: { + entryDate: new Date(), + data: updatedData, + }, + }), + }; + + UIDatabase.write(() => { + UIDatabase.update('NameNote', updatedNote); + UIDatabase.create('NameNote', auditNameNote); + }); ToastAndroid.show(vaccineStrings.vaccination_updated, ToastAndroid.LONG); }; From feffb796a2e32ba4353dcdf577cc88137637c8c1 Mon Sep 17 00:00:00 2001 From: Kat Date: Thu, 19 May 2022 17:44:47 +1200 Subject: [PATCH 007/131] Add isDeceased toggle to patient form --- src/localization/formInputStrings.json | 1 + src/utilities/formInputConfigs.js | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/src/localization/formInputStrings.json b/src/localization/formInputStrings.json index 2318f089c..9c0d41c68 100644 --- a/src/localization/formInputStrings.json +++ b/src/localization/formInputStrings.json @@ -16,6 +16,7 @@ "family_policy_number": "Family policy number", "first_name": "First name", "is_active": "Is active", + "is_deceased": "Is deceased", "last_name": "Last name", "location_type": "Location type", "less_than_20_characters": "less than 20 characters", diff --git a/src/utilities/formInputConfigs.js b/src/utilities/formInputConfigs.js index 40c0fc907..f3e897e8c 100644 --- a/src/utilities/formInputConfigs.js +++ b/src/utilities/formInputConfigs.js @@ -50,6 +50,7 @@ const FORM_INPUT_KEYS = { FIRST_NAME: 'firstName', GENDER: 'gender', IS_ACTIVE: 'isActive', + IS_DECEASED: 'isDeceased', LAST_NAME: 'lastName', MIDDLE_NAME: 'middleName', NATIONALITY: 'nationality', @@ -336,6 +337,15 @@ const FORM_INPUT_CONFIGS = seedObject => ({ label: formInputStrings.policy_number, isEditable: true, }, + [FORM_INPUT_KEYS.IS_DECEASED]: { + type: FORM_INPUT_TYPES.TOGGLE, + initialValue: false, + key: 'deceased', + options: [true, false], + optionLabels: [formInputStrings.yes, formInputStrings.no], + label: formInputStrings.is_deceased, + isEditable: true, + }, }); const FORM_CONFIGS = { @@ -352,6 +362,7 @@ const FORM_CONFIGS = { FORM_INPUT_KEYS.NATIONALITY, FORM_INPUT_KEYS.ETHNICITY, FORM_INPUT_KEYS.GENDER, + FORM_INPUT_KEYS.IS_DECEASED, ], prescriber: [ FORM_INPUT_KEYS.LAST_NAME, From eb122db667697de6a250ff0a74a7d44d36ba3ea2 Mon Sep 17 00:00:00 2001 From: Kat Date: Fri, 20 May 2022 16:25:18 +1200 Subject: [PATCH 008/131] Further updates to read/save isDeceased flag --- src/actions/PatientActions.js | 4 ++++ src/database/DataTypes/Name.js | 2 ++ src/database/schema.js | 2 +- src/database/utilities/createRecord.js | 6 ++++++ src/sync/incomingSyncUtils.js | 1 + src/sync/outgoingSyncUtils.js | 1 + src/utilities/formInputConfigs.js | 2 +- 7 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/actions/PatientActions.js b/src/actions/PatientActions.js index be780bcb6..5ee27f9c9 100644 --- a/src/actions/PatientActions.js +++ b/src/actions/PatientActions.js @@ -53,6 +53,7 @@ const patientUpdate = patientDetails => async (dispatch, getState) => { country: currentCountry, supplyingStoreId: currentSupplyingStoreId, isActive: currentIsActive, + isDeceased: currentIsDeceased, female: currentFemale, ethnicity: currentEthnicity, nationality: currentNationality, @@ -82,6 +83,7 @@ const patientUpdate = patientDetails => async (dispatch, getState) => { country: patientCountry, supplyingStoreId: patientSupplyingStoreId, female: patientFemale, + isDeceased: patientIsDeceased, ethnicity: patientEthnicity, nationality: patientNationality, } = patientDetails ?? {}; @@ -102,6 +104,7 @@ const patientUpdate = patientDetails => async (dispatch, getState) => { const billPostalZipCode = patientPostalZipCode ?? currentZipCode; const country = patientCountry ?? currentCountry; const female = patientFemale ?? currentFemale; + const isDeceased = patientIsDeceased ?? currentIsDeceased; const supplyingStoreId = patientSupplyingStoreId ?? currentSupplyingStoreId; const isActive = currentIsActive; const ethnicity = patientEthnicity ?? currentEthnicity; @@ -124,6 +127,7 @@ const patientUpdate = patientDetails => async (dispatch, getState) => { billPostalZipCode, country, female, + isDeceased, supplyingStoreId, isActive, ethnicity, diff --git a/src/database/DataTypes/Name.js b/src/database/DataTypes/Name.js index 097351175..6032830a5 100644 --- a/src/database/DataTypes/Name.js +++ b/src/database/DataTypes/Name.js @@ -158,6 +158,7 @@ export class Name extends Realm.Object { middleName: this.middleName, lastName: this.lastName, isActive: this.isActive, + isDeceased: this.isDeceased, isCustomer: this.isCustomer, isSupplier: this.isSupplier, isManufacturer: this.isManufacturer, @@ -195,6 +196,7 @@ Name.schema = { middleName: { type: 'string', optional: true }, lastName: { type: 'string', optional: true }, isActive: { type: 'bool', optional: true }, + isDeceased: { type: 'bool', optional: true }, isCustomer: { type: 'bool', default: false }, isSupplier: { type: 'bool', default: false }, isManufacturer: { type: 'bool', default: false }, diff --git a/src/database/schema.js b/src/database/schema.js index a48604b4d..dd284f47f 100644 --- a/src/database/schema.js +++ b/src/database/schema.js @@ -257,7 +257,7 @@ export const schema = { VaccineVialMonitorStatus, VaccineVialMonitorStatusLog, ], - schemaVersion: 27, + schemaVersion: 28, }; export default schema; diff --git a/src/database/utilities/createRecord.js b/src/database/utilities/createRecord.js index c0999d350..8b8f907e9 100644 --- a/src/database/utilities/createRecord.js +++ b/src/database/utilities/createRecord.js @@ -265,6 +265,7 @@ const createPatient = (database, patientDetails) => { female: patientFemale, supplyingStoreId: patientSupplyingStoreId, isActive: patientIsActive, + isDeceased: patientIsDeceased, nationality, ethnicity, nameNotes, @@ -292,6 +293,7 @@ const createPatient = (database, patientDetails) => { const country = patientCountry ?? ''; const female = patientFemale ?? true; + const isDeceased = patientIsDeceased ?? false; const thisStoreId = database.getSetting(SETTINGS_KEYS.THIS_STORE_ID); const supplyingStoreId = patientSupplyingStoreId || thisStoreId; @@ -325,6 +327,7 @@ const createPatient = (database, patientDetails) => { billingAddress, country, female, + isDeceased, supplyingStoreId, thisStoresPatient, isActive, @@ -334,6 +337,9 @@ const createPatient = (database, patientDetails) => { createdDate, }); + console.log('patient'); + console.log(patient); + nameNotes?.forEach(nameNote => createNameNote(database, nameNote)); return patient; diff --git a/src/sync/incomingSyncUtils.js b/src/sync/incomingSyncUtils.js index a7b3ef2bc..cd08b9c5f 100644 --- a/src/sync/incomingSyncUtils.js +++ b/src/sync/incomingSyncUtils.js @@ -588,6 +588,7 @@ export const createOrUpdateRecord = (database, settings, recordType, record) => emailAddress: record.email, type: NAME_TYPES.translate(record.type, EXTERNAL_TO_INTERNAL), isCustomer: parseBoolean(record.customer), + isDeceased: parseBoolean(record.isDeceased), isSupplier: parseBoolean(record.supplier), isManufacturer: parseBoolean(record.manufacturer), supplyingStoreId: record.supplying_store_id, diff --git a/src/sync/outgoingSyncUtils.js b/src/sync/outgoingSyncUtils.js index cba952acb..04e1163ff 100644 --- a/src/sync/outgoingSyncUtils.js +++ b/src/sync/outgoingSyncUtils.js @@ -98,6 +98,7 @@ const generateSyncData = (settings, recordType, record) => { barcode: record.barcode ?? `*${record.code}*`, 'charge code': record.code, currency_id: defaultCurrency?.id ?? '', + isDeceased: String(record.isDeceased), female: String(record.female), nationality_ID: record.nationality?.id ?? '', occupation_ID: record.occupation?.id ?? '', diff --git a/src/utilities/formInputConfigs.js b/src/utilities/formInputConfigs.js index f3e897e8c..abd324da8 100644 --- a/src/utilities/formInputConfigs.js +++ b/src/utilities/formInputConfigs.js @@ -340,7 +340,7 @@ const FORM_INPUT_CONFIGS = seedObject => ({ [FORM_INPUT_KEYS.IS_DECEASED]: { type: FORM_INPUT_TYPES.TOGGLE, initialValue: false, - key: 'deceased', + key: 'isDeceased', options: [true, false], optionLabels: [formInputStrings.yes, formInputStrings.no], label: formInputStrings.is_deceased, From b54fccc2d811de8ca5e350134002a45b0f2a61a3 Mon Sep 17 00:00:00 2001 From: Kat Date: Wed, 25 May 2022 11:07:28 +1200 Subject: [PATCH 009/131] =?UTF-8?q?=F0=9F=99=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/database/utilities/createRecord.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/database/utilities/createRecord.js b/src/database/utilities/createRecord.js index 8b8f907e9..f8232e0ce 100644 --- a/src/database/utilities/createRecord.js +++ b/src/database/utilities/createRecord.js @@ -337,9 +337,6 @@ const createPatient = (database, patientDetails) => { createdDate, }); - console.log('patient'); - console.log(patient); - nameNotes?.forEach(nameNote => createNameNote(database, nameNote)); return patient; From 8e4bc74dfd9a31b599481cf999ef729fbfcefbbf Mon Sep 17 00:00:00 2001 From: Kat Date: Wed, 1 Jun 2022 18:43:21 +1200 Subject: [PATCH 010/131] Fix broken name note duplicate filter --- src/hooks/useLocalAndRemoteHistory.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/hooks/useLocalAndRemoteHistory.js b/src/hooks/useLocalAndRemoteHistory.js index a0bf818cf..ad7e3503a 100644 --- a/src/hooks/useLocalAndRemoteHistory.js +++ b/src/hooks/useLocalAndRemoteHistory.js @@ -5,6 +5,7 @@ import { getServerURL, getPatientHistoryResponseProcessor, } from '../sync/lookupApiUtils'; +import { convertVaccinationEntryToISOString } from '../utilities/parsers'; import { useFetch } from './useFetch'; import { useThrottled } from './useThrottled'; @@ -25,12 +26,13 @@ const reducer = (state, action) => { const { data: initialData } = state; const localTransactionIds = initialData.map(transactions => transactions.id); - // Name notes do not have transaction ids, create filter instead based on itemCode/confirmDate + // Name notes do not have transaction objects + // Create filter instead based on itemCode/confirmDate const localNameNoteFilter = initialData - .filter(transaction => !transaction.id) + .filter(record => !record.transaction) .map(nameNotes => ({ itemCode: nameNotes.itemCode, - confirmDate: nameNotes.confirmDate, + confirmDate: new Date(convertVaccinationEntryToISOString(nameNotes.vaccineDate)), select: '>', })); From 4e4a4e0950735ef920dae9b7039d0c8e653f0745 Mon Sep 17 00:00:00 2001 From: Kat Date: Thu, 2 Jun 2022 16:03:28 +1200 Subject: [PATCH 011/131] Dispatch reloadItems action to store --- src/widgets/Tabs/ItemSelect.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/widgets/Tabs/ItemSelect.js b/src/widgets/Tabs/ItemSelect.js index cb3ad628b..e95f0bccf 100644 --- a/src/widgets/Tabs/ItemSelect.js +++ b/src/widgets/Tabs/ItemSelect.js @@ -60,6 +60,7 @@ const ItemSelectComponent = ({ items, selectedRows, onDelete, + reloadItems, }) => { const columns = React.useMemo(() => getColumns(TABS.ITEM), []); const showToast = React.useCallback( @@ -69,7 +70,7 @@ const ItemSelectComponent = ({ // Reload items when component is mounted (component is mounted on every DB action) useEffect(() => { - PrescriptionActions.reloadItems(); + reloadItems(); }, []); // sort and filter the items @@ -134,7 +135,8 @@ const mapDispatchToProps = dispatch => { const updateQuantity = (id, quantity) => dispatch(PrescriptionActions.editQuantity(id, quantity)); const filterItems = searchTerm => dispatch(PrescriptionActions.filter(searchTerm)); const onDelete = () => dispatch(PrescriptionActions.cancelPrescription()); - return { onDelete, filterItems, nextTab, chooseItem, updateQuantity }; + const reloadItems = () => dispatch(PrescriptionActions.reloadItems()); + return { onDelete, filterItems, nextTab, chooseItem, updateQuantity, reloadItems }; }; const mapStateToProps = state => { @@ -158,6 +160,7 @@ ItemSelectComponent.propTypes = { items: PropTypes.array.isRequired, selectedRows: PropTypes.object.isRequired, onDelete: PropTypes.func.isRequired, + reloadItems: PropTypes.func.isRequired, }; export const ItemSelect = connect(mapStateToProps, mapDispatchToProps)(ItemSelectComponent); From 7999304c77dc81acb8f82f3592119836dfe9af1b Mon Sep 17 00:00:00 2001 From: Kat Date: Mon, 13 Jun 2022 14:42:42 +1200 Subject: [PATCH 012/131] Style updates to Checkbox/Togglebar --- src/widgets/JSONForm/widgets/Checkbox.js | 17 ++++++++++++++++ src/widgets/ToggleBar/ToggleBar.js | 25 +++++++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/widgets/JSONForm/widgets/Checkbox.js b/src/widgets/JSONForm/widgets/Checkbox.js index 5be873d0c..a7e56a45d 100644 --- a/src/widgets/JSONForm/widgets/Checkbox.js +++ b/src/widgets/JSONForm/widgets/Checkbox.js @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; import { StyleSheet } from 'react-native'; import { SUSSOL_ORANGE, WARMER_GREY } from '../../../globalStyles/colors'; import { ToggleBar } from '../../index'; +import { APP_FONT_FAMILY } from '../../../globalStyles'; export const Checkbox = ({ options: { enumOptions }, @@ -27,9 +28,11 @@ export const Checkbox = ({ return ( @@ -38,6 +41,11 @@ export const Checkbox = ({ const styles = StyleSheet.create({ container: { borderWidth: 0, width: 150 }, + textOffDisabledStyle: { + fontFamily: APP_FONT_FAMILY, + fontSize: 12, + color: WARMER_GREY, + }, toggleOnStyle: { flex: 1, alignItems: 'center', @@ -63,6 +71,15 @@ const styles = StyleSheet.create({ borderRadius: 20, margin: 5, }, + toggleOffDisabledStyle: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + borderColor: WARMER_GREY, + borderRadius: 20, + borderWidth: 1, + margin: 5, + }, }); Checkbox.propTypes = { diff --git a/src/widgets/ToggleBar/ToggleBar.js b/src/widgets/ToggleBar/ToggleBar.js index 4ca8330bf..2d34b01e4 100644 --- a/src/widgets/ToggleBar/ToggleBar.js +++ b/src/widgets/ToggleBar/ToggleBar.js @@ -8,7 +8,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { StyleSheet, Text, TouchableOpacity, ViewPropTypes, View } from 'react-native'; -import globalStyles, { WARMER_GREY, SUSSOL_ORANGE } from '../../globalStyles'; +import globalStyles, { WARMER_GREY, SUSSOL_ORANGE, APP_FONT_FAMILY } from '../../globalStyles'; /** * Renders a bar of multiple toggling buttons, defined by the array 'toggles' passed in. @@ -38,10 +38,12 @@ export const ToggleBarComponent = props => { toggleOffStyle, toggleOnStyle, textOffStyle, + textOffDisabledStyle, textOnStyle, toggles, isDisabled, toggleOnDisabledStyle, + toggleOffDisabledStyle, ...containerProps } = props; @@ -50,12 +52,14 @@ export const ToggleBarComponent = props => { const renderOutput = []; const defaultOnStyle = isDisabled ? toggleOnDisabledStyle : toggleOnStyle; + const defaultOffStyle = isDisabled ? toggleOffDisabledStyle : toggleOffStyle; + const defaultOffTextStyle = isDisabled ? textOffDisabledStyle : textOffStyle; buttons.forEach((button, i) => { const currentTextStyle = button.isOn ? [localStyles.textOnStyle, textOnStyle] - : [localStyles.textOffStyle, textOffStyle]; - const currentToggleStyle = button.isOn ? defaultOnStyle : toggleOffStyle; + : [localStyles.textOffStyle, defaultOffTextStyle]; + const currentToggleStyle = button.isOn ? defaultOnStyle : defaultOffStyle; const Container = isDisabled ? View : TouchableOpacity; renderOutput.push( @@ -113,6 +117,17 @@ const localStyles = StyleSheet.create({ backgroundColor: WARMER_GREY, width: 142, }, + toggleOffDisabledStyle: { + alignItems: 'center', + justifyContent: 'center', + borderColor: WARMER_GREY, + width: 142, + }, + toggleTextDisabledSelected: { + fontFamily: APP_FONT_FAMILY, + fontSize: 12, + color: WARMER_GREY, + }, }); ToggleBarComponent.propTypes = { @@ -122,9 +137,11 @@ ToggleBarComponent.propTypes = { toggleOffStyle: ViewPropTypes.style, toggleOnStyle: ViewPropTypes.style, textOffStyle: Text.propTypes.style, + textOffDisabledStyle: Text.propTypes.style, textOnStyle: Text.propTypes.style, isDisabled: PropTypes.bool, toggleOnDisabledStyle: PropTypes.object, + toggleOffDisabledStyle: PropTypes.object, }; ToggleBarComponent.defaultProps = { @@ -132,7 +149,9 @@ ToggleBarComponent.defaultProps = { toggleOffStyle: localStyles.toggleOffStyle, toggleOnStyle: localStyles.toggleOnStyle, textOffStyle: globalStyles.toggleText, + textOffDisabledStyle: globalStyles.toggleTextDisabledSelected, textOnStyle: globalStyles.toggleTextSelected, toggleOnDisabledStyle: localStyles.toggleOnDisabledStyle, + toggleOffDisabledStyle: localStyles.toggleOffDisabledStyle, isDisabled: false, }; From d6ee94340aea16b013623e583e80aef76ae0ebae Mon Sep 17 00:00:00 2001 From: Kat Date: Wed, 15 Jun 2022 14:33:49 +1200 Subject: [PATCH 013/131] Add ability to edit supplemental data form --- src/actions/Entities/NameNoteActions.js | 42 ++++--- src/localization/buttonStrings.json | 1 + src/localization/vaccineStrings.json | 1 + src/widgets/Tabs/PatientSelect.js | 2 +- src/widgets/modals/VaccinationEvent.js | 146 +++++++++++++++++++----- 5 files changed, 148 insertions(+), 44 deletions(-) diff --git a/src/actions/Entities/NameNoteActions.js b/src/actions/Entities/NameNoteActions.js index 48bc90124..9f449b532 100644 --- a/src/actions/Entities/NameNoteActions.js +++ b/src/actions/Entities/NameNoteActions.js @@ -69,17 +69,33 @@ const saveEditing = () => (dispatch, getState) => { dispatch(reset()); }; -const updateLinkedSurveyNameNote = (originalNote, updatedData) => () => { - const { id, patientEvent, name, entryDate } = originalNote; +const updateNameNote = (originalNote, updatedData) => () => { + const { id, patientEvent, name, entryDate, _data } = originalNote; - const updatedNote = { - id, - patientEvent, - name, - entryDate: new Date(entryDate), - _data: JSON.stringify(updatedData), - }; + // Quick & dirty check if the object was updated, trims out some un-needed updates + const isDirty = _data !== JSON.stringify(updatedData); + + if (isDirty) { + const updatedNote = { + id, + patientEvent, + name, + entryDate: new Date(entryDate), + _data: JSON.stringify(updatedData), + }; + + UIDatabase.write(() => { + UIDatabase.update('NameNote', updatedNote); + UIDatabase.create('NameNote', createNameNoteAudit(originalNote, updatedData)); + }); + ToastAndroid.show(vaccineStrings.vaccination_updated, ToastAndroid.LONG); + } else { + ToastAndroid.show('No updates were made', ToastAndroid.LONG); + } +}; +const createNameNoteAudit = (originalNote, updatedData) => { + const { patientEvent, name, entryDate } = originalNote; const [auditEvent] = UIDatabase.objects('PatientEvent').filtered('code == "NameNoteModified"'); const auditNameNote = { @@ -100,11 +116,7 @@ const updateLinkedSurveyNameNote = (originalNote, updatedData) => () => { }), }; - UIDatabase.write(() => { - UIDatabase.update('NameNote', updatedNote); - UIDatabase.create('NameNote', auditNameNote); - }); - ToastAndroid.show(vaccineStrings.vaccination_updated, ToastAndroid.LONG); + return auditNameNote; }; const createNotes = (nameNotes = []) => { @@ -139,6 +151,6 @@ export const NameNoteActions = { reset, createSurveyNameNote, updateForm, - updateLinkedSurveyNameNote, + updateNameNote, saveEditing, }; diff --git a/src/localization/buttonStrings.json b/src/localization/buttonStrings.json index 4c054d632..bcda6384a 100644 --- a/src/localization/buttonStrings.json +++ b/src/localization/buttonStrings.json @@ -13,6 +13,7 @@ "create_automatic_order": "Create Automatic Order", "current": "Current", "done": "Done", + "edit": "Edit", "export_data": "Export Data", "import_data": "Import Data", "hide_stockouts": "Hide Stockouts", diff --git a/src/localization/vaccineStrings.json b/src/localization/vaccineStrings.json index c2aa049d5..692c8dcc7 100644 --- a/src/localization/vaccineStrings.json +++ b/src/localization/vaccineStrings.json @@ -7,6 +7,7 @@ "vaccine_dispense_step_two_title": "Step: 2 Edit the patients details", "vaccine_dispense_vaccine_select_title": "Select the vaccine", "vaccine_dispense_supplemental_data_title": "Step: 3 Enter additional vaccination details", + "vaccine_event_supplemental_data_title": "Page 2: Additional Details", "vaccinator": "Vaccinator", "logging_delayed_until": "Logging delayed until", "hot_consecutive": "Hot consecutive", diff --git a/src/widgets/Tabs/PatientSelect.js b/src/widgets/Tabs/PatientSelect.js index 5501c5bc1..1de08bf1c 100644 --- a/src/widgets/Tabs/PatientSelect.js +++ b/src/widgets/Tabs/PatientSelect.js @@ -342,7 +342,7 @@ const PatientSelectComponent = ({ onClose={() => setVaccinationEvent(null)} title={`${dispensingStrings.vaccination_details}`} > - + ); diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index ba045b97f..81ab38608 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -1,3 +1,4 @@ +/* eslint-disable react/jsx-curly-newline */ /* eslint-disable react/forbid-prop-types */ import React, { useRef, useState } from 'react'; import { View, StyleSheet, Text } from 'react-native'; @@ -5,7 +6,11 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { FlexRow } from '../FlexRow'; import { JSONForm } from '../JSONForm/JSONForm'; -import { selectSurveySchemas, selectVaccinationEventSchemas } from '../../selectors/formSchema'; +import { + selectSupplementalDataSchemas, + selectSurveySchemas, + selectVaccinationEventSchemas, +} from '../../selectors/formSchema'; import { FlexView } from '../FlexView'; import { UIDatabase } from '../../database'; import { selectMostRecentNameNote } from '../../selectors/Entities/nameNote'; @@ -17,9 +22,11 @@ import globalStyles, { GREY, SUSSOL_ORANGE, } from '../../globalStyles'; -import { buttonStrings, generalStrings } from '../../localization'; +import { buttonStrings, generalStrings, vaccineStrings } from '../../localization'; import { PageButton } from '../PageButton'; import { NameNoteActions } from '../../actions'; +import { Paper } from '../Paper'; +import { Title } from '../JSONForm/fields'; // It's possible to get into this state if vaccination events were configured but PCD events weren't // and someone dispensed a vaccine. Some data cleanup may be required. @@ -40,17 +47,27 @@ export const NoPCDForm = () => ( export const VaccinationEventComponent = ({ patient, - vaccinationEvent, + supplementalDataSchema, + vaccinationEventId, vaccinationEventSchema, - saveForm, + savePCDForm, + saveSupplementalData, surveySchema, }) => { const pcdFormRef = useRef(null); + const supplementalFormRef = useRef(null); const vaccinationFormRef = useRef(null); + const [{ updatedPcdForm, isPCDValid }, setPCDForm] = useState({ updatedPcdForm: null, isPCDValid: false, }); + const [{ updatedSupplementalDataForm, isSupplementalDataValid }, setSupplementalData] = useState({ + updatedSupplementalDataForm: null, + isSupplementalDataValid: false, + }); + const vaccinationEventNameNote = UIDatabase.get('NameNote', vaccinationEventId); + const vaccinationEvent = vaccinationEventNameNote.data; const { pcdNameNoteId } = vaccinationEvent; const surveyForm = pcdNameNoteId @@ -64,27 +81,13 @@ export const VaccinationEventComponent = ({ const parsedVaccinationEvent = { ...vaccinationEvent, extra: { - prescription: { ...vaccinationEvent.prescription, customData: customDataObject }, + prescription: { ...vaccinationEvent.extra.prescription, customData: customDataObject }, }, }; return ( - - {!!vaccinationEventSchema && !!vaccinationEvent && ( - - - - <> - - - - )} + {!!surveySchema && !!surveyForm ? ( @@ -110,15 +113,70 @@ export const VaccinationEventComponent = ({ )} + + + + } + contentContainerStyle={{ flex: 1 }} + style={{ flex: 1 }} + headerContainerStyle={localStyles.title} + > + { + setSupplementalData({ + updatedSupplementalDataForm: changed.formData, + isSupplementalDataValid: validator(changed.formData), + }); + }} + > + <> + + + + + {!!vaccinationEventSchema && !!vaccinationEvent && ( + + + + <> + + + + )} saveForm(surveyForm, updatedPcdForm)} + onPress={() => savePCDForm(surveyForm, updatedPcdForm)} style={localStyles.saveButton} textStyle={localStyles.saveButtonTextStyle} isDisabled={!isPCDValid} /> + + saveSupplementalData(vaccinationEventNameNote, updatedSupplementalDataForm) + } + style={localStyles.saveButton} + textStyle={localStyles.saveButtonTextStyle} + isDisabled={!isSupplementalDataValid} + /> + ); @@ -131,16 +189,39 @@ const mapStateToProps = () => { const vaccinationEventSchemas = selectVaccinationEventSchemas(); const [vaccinationEventSchema] = vaccinationEventSchemas; + const supplementalDataSchemas = selectSupplementalDataSchemas(); + const [supplementalDataSchema] = supplementalDataSchemas; + return { surveySchema, vaccinationEventSchema, + supplementalDataSchema, }; }; -const mapDispatchToProps = dispatch => ({ - saveForm: (oldSurveyNote, updatedSurveyData) => - dispatch(NameNoteActions.updateLinkedSurveyNameNote(oldSurveyNote, updatedSurveyData)), -}); +const mapDispatchToProps = dispatch => { + const savePCDForm = (oldSurveyNote, updatedSurveyData) => { + dispatch(NameNoteActions.updateNameNote(oldSurveyNote, updatedSurveyData)); + }; + + const saveSupplementalData = (vaccinationEventNameNote, updatedSupplementalData) => { + const vaccinationEvent = vaccinationEventNameNote.data; + + const updatedData = { + ...vaccinationEvent, + extra: { + ...vaccinationEvent.extra, + prescription: { + ...vaccinationEvent.extra.prescription, + customData: JSON.stringify(updatedSupplementalData), + }, + }, + }; + dispatch(NameNoteActions.updateNameNote(vaccinationEventNameNote, updatedData)); + }; + + return { savePCDForm, saveSupplementalData }; +}; const localStyles = StyleSheet.create({ formContainer: { @@ -160,18 +241,27 @@ const localStyles = StyleSheet.create({ color: 'white', fontSize: 14, }, + title: { + textAlignVertical: 'center', + fontWeight: 'bold', + fontSize: 22, + fontFamily: APP_FONT_FAMILY, + color: DARKER_GREY, + }, }); VaccinationEventComponent.defaultProps = { patient: {}, - vaccinationEvent: {}, + vaccinationEventId: '', }; VaccinationEventComponent.propTypes = { patient: PropTypes.object, - saveForm: PropTypes.func.isRequired, + savePCDForm: PropTypes.func.isRequired, + saveSupplementalData: PropTypes.func.isRequired, + supplementalDataSchema: PropTypes.object.isRequired, surveySchema: PropTypes.object.isRequired, - vaccinationEvent: PropTypes.object, + vaccinationEventId: PropTypes.string, vaccinationEventSchema: PropTypes.object.isRequired, }; From 5c282637b92f33d5c992f6b20a451292028d378a Mon Sep 17 00:00:00 2001 From: Kat Date: Wed, 15 Jun 2022 16:03:55 +1200 Subject: [PATCH 014/131] String localisation --- src/actions/Entities/NameNoteActions.js | 2 +- src/localization/vaccineStrings.json | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/actions/Entities/NameNoteActions.js b/src/actions/Entities/NameNoteActions.js index 9f449b532..9514928a6 100644 --- a/src/actions/Entities/NameNoteActions.js +++ b/src/actions/Entities/NameNoteActions.js @@ -90,7 +90,7 @@ const updateNameNote = (originalNote, updatedData) => () => { }); ToastAndroid.show(vaccineStrings.vaccination_updated, ToastAndroid.LONG); } else { - ToastAndroid.show('No updates were made', ToastAndroid.LONG); + ToastAndroid.show(vaccineStrings.vaccination_not_updated, ToastAndroid.LONG); } }; diff --git a/src/localization/vaccineStrings.json b/src/localization/vaccineStrings.json index 692c8dcc7..d009a6978 100644 --- a/src/localization/vaccineStrings.json +++ b/src/localization/vaccineStrings.json @@ -8,6 +8,7 @@ "vaccine_dispense_vaccine_select_title": "Select the vaccine", "vaccine_dispense_supplemental_data_title": "Step: 3 Enter additional vaccination details", "vaccine_event_supplemental_data_title": "Page 2: Additional Details", + "vaccine_event_transact_data_title": "Page 3: Vaccination Event", "vaccinator": "Vaccinator", "logging_delayed_until": "Logging delayed until", "hot_consecutive": "Hot consecutive", @@ -76,7 +77,8 @@ "pause": "Pause", "resume": "Resume", "refusal_reason": "Reason for refusal", - "vaccination_updated": "Vaccination details updated" + "vaccination_updated": "Vaccination details updated", + "vaccination_not_updated": "No updates were made to the vaccination record" }, "fr": { "no_vaccine_stock": "Vous n'avez pas de vaccins en stock!", From 9adf69eded1a7b34986d051e35a6e9469956b4ce Mon Sep 17 00:00:00 2001 From: Kat Date: Thu, 16 Jun 2022 13:12:29 +1200 Subject: [PATCH 015/131] Allow length of > 3 for email TLD --- src/authentication/DemoSiteRequest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/authentication/DemoSiteRequest.js b/src/authentication/DemoSiteRequest.js index 4359d649d..7c73aa30c 100644 --- a/src/authentication/DemoSiteRequest.js +++ b/src/authentication/DemoSiteRequest.js @@ -52,7 +52,7 @@ export class DemoSiteRequest { // TODO: Could be extracted to a helper file to be used elsewhere. // eslint-disable-next-line class-methods-use-this validateEmail(text) { - const reg = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/; + const reg = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w+)+$/; return reg.test(text); } From d89246cb81a9f26fa72b40f8951726fb05666f92 Mon Sep 17 00:00:00 2001 From: Kat Date: Fri, 17 Jun 2022 12:55:31 +1200 Subject: [PATCH 016/131] Less restrictive regex --- src/authentication/DemoSiteRequest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/authentication/DemoSiteRequest.js b/src/authentication/DemoSiteRequest.js index 7c73aa30c..09b6f2d14 100644 --- a/src/authentication/DemoSiteRequest.js +++ b/src/authentication/DemoSiteRequest.js @@ -52,7 +52,7 @@ export class DemoSiteRequest { // TODO: Could be extracted to a helper file to be used elsewhere. // eslint-disable-next-line class-methods-use-this validateEmail(text) { - const reg = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w+)+$/; + const reg = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/; return reg.test(text); } From e209168b144380f9576945ae63fc5759e746fad6 Mon Sep 17 00:00:00 2001 From: Kat Date: Mon, 20 Jun 2022 12:00:56 +1200 Subject: [PATCH 017/131] Make the buttons match --- src/widgets/modals/VaccinationEvent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 81ab38608..b27708136 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -172,7 +172,7 @@ export const VaccinationEventComponent = ({ isDisabled={!isSupplementalDataValid} /> Date: Wed, 22 Jun 2022 14:59:49 +1200 Subject: [PATCH 018/131] For future - save the storeId/name into the NameNote --- src/actions/Entities/VaccinePrescriptionActions.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/actions/Entities/VaccinePrescriptionActions.js b/src/actions/Entities/VaccinePrescriptionActions.js index a68ce1e56..f5a22445b 100644 --- a/src/actions/Entities/VaccinePrescriptionActions.js +++ b/src/actions/Entities/VaccinePrescriptionActions.js @@ -17,6 +17,7 @@ import { NameNoteActions } from './NameNoteActions'; import { goBack, gotoVaccineDispensingPage } from '../../navigation/actions'; import { selectSupplementalDataSchemas } from '../../selectors/formSchema'; import { validateJsonSchemaData } from '../../utilities/ajvValidator'; +import { SETTINGS_KEYS } from '../../settings'; export const VACCINE_PRESCRIPTION_ACTIONS = { CREATE: 'VACCINE_PRESCRIPTION/create', @@ -191,6 +192,8 @@ const createVaccinationNameNote = ( // Extract name notes from the patient before saving as this can get huuuge(!) const { nameNotes, ...patientObject } = patient.toJSON(); + const storeId = UIDatabase.getSetting(SETTINGS_KEYS.THIS_STORE_ID); + const storeNameId = UIDatabase.getSetting(SETTINGS_KEYS.THIS_STORE_NAME_ID); const data = { refused, @@ -208,6 +211,8 @@ const createVaccinationNameNote = ( patient: patientObject, }, pcdNameNoteId: getPcdNameNoteID(patient.id), + storeId, + storeName: UIDatabase.objects('Name').filtered('id == $0', storeNameId)[0]?.name, }; const newNameNote = { id, From a735e7c5b3360fb7c78aba0e96f9ca39f5c6bcbc Mon Sep 17 00:00:00 2001 From: Kat Date: Wed, 22 Jun 2022 17:51:45 +1200 Subject: [PATCH 019/131] Update Modal to not render cancel button if no cancel button text is provided --- src/widgets/PaperModal/PaperConfirmModal.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/widgets/PaperModal/PaperConfirmModal.js b/src/widgets/PaperModal/PaperConfirmModal.js index 0c89bbeff..6f3a6801f 100644 --- a/src/widgets/PaperModal/PaperConfirmModal.js +++ b/src/widgets/PaperModal/PaperConfirmModal.js @@ -24,7 +24,16 @@ export const PaperConfirmModal = ({ {questionText} - + {cancelText ? ( + + ) : ( + <> + )} , + cancelText: undefined, + onCancel: undefined, }; PaperConfirmModal.propTypes = { Icon: PropTypes.node, questionText: PropTypes.string.isRequired, confirmText: PropTypes.string.isRequired, - cancelText: PropTypes.string.isRequired, + cancelText: PropTypes.string, onConfirm: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired, + onCancel: PropTypes.func, }; From 959a983378202ccd293aa7fa9874f9b4dedcca5a Mon Sep 17 00:00:00 2001 From: Kat Date: Thu, 23 Jun 2022 15:34:16 +1200 Subject: [PATCH 020/131] Extract VaccinatorDropDown into separate file --- src/widgets/VaccinatorDropDown.js | 33 ++++++++++++++++++++++++++ src/widgets/VaccinePrescriptionInfo.js | 27 +-------------------- 2 files changed, 34 insertions(+), 26 deletions(-) create mode 100644 src/widgets/VaccinatorDropDown.js diff --git a/src/widgets/VaccinatorDropDown.js b/src/widgets/VaccinatorDropDown.js new file mode 100644 index 000000000..9899b3893 --- /dev/null +++ b/src/widgets/VaccinatorDropDown.js @@ -0,0 +1,33 @@ +/* eslint-disable react/forbid-prop-types */ +import React from 'react'; +import PropTypes from 'prop-types'; +import { StyleSheet } from 'react-native'; +import { UIDatabase } from '../database/index'; +import { DropDown } from './DropDown'; + +export const VaccinatorDropDown = ({ value, onChange }) => { + const medicineAdmins = UIDatabase.objects('MedicineAdministrator').sorted('lastName'); + const values = medicineAdmins.map(({ displayString }) => displayString); + + return ( + onChange(medicineAdmins[i])} + selectedValue={value?.displayString} + /> + ); +}; + +const styles = StyleSheet.create({ + dropdown: { height: 35, marginTop: 0, marginBottom: 0, marginLeft: 0 }, +}); + +VaccinatorDropDown.defaultProps = { + value: null, +}; + +VaccinatorDropDown.propTypes = { + value: PropTypes.object, + onChange: PropTypes.func.isRequired, +}; diff --git a/src/widgets/VaccinePrescriptionInfo.js b/src/widgets/VaccinePrescriptionInfo.js index a54cb9711..5348d00d7 100644 --- a/src/widgets/VaccinePrescriptionInfo.js +++ b/src/widgets/VaccinePrescriptionInfo.js @@ -18,8 +18,6 @@ import { dispensingStrings, vaccineStrings } from '../localization'; import { FlexColumn } from './FlexColumn'; import { FlexView } from './FlexView'; import { APP_FONT_FAMILY, DARKER_GREY, SUSSOL_ORANGE } from '../globalStyles'; -import { UIDatabase } from '../database/index'; -import { DropDown } from './DropDown'; import { VaccinePrescriptionActions } from '../actions/Entities'; import { selectFoundBonusDose, @@ -29,6 +27,7 @@ import { import { Spacer } from './Spacer'; import { BACKGROUND_COLOR, LIGHT_GREY } from '../globalStyles/colors'; import { selectCurrentTab } from '../selectors/wizard'; +import { VaccinatorDropDown } from './VaccinatorDropDown'; const WithLabel = ({ label, ...props }) => ( @@ -43,29 +42,6 @@ WithLabel.propTypes = { label: PropTypes.string.isRequired, }; -const VaccinatorDropDown = ({ value, onChange }) => { - const medicineAdmins = UIDatabase.objects('MedicineAdministrator').sorted('lastName'); - const values = medicineAdmins.map(({ displayString }) => displayString); - - return ( - onChange(medicineAdmins[i])} - selectedValue={value?.displayString} - /> - ); -}; - -VaccinatorDropDown.defaultProps = { - value: null, -}; - -VaccinatorDropDown.propTypes = { - value: PropTypes.object, - onChange: PropTypes.func.isRequired, -}; - const Header = ({ title }) => ( {title} @@ -186,7 +162,6 @@ const styles = StyleSheet.create({ fontFamily: APP_FONT_FAMILY, fontSize: 12, }, - dropdown: { height: 35, marginTop: 0, marginBottom: 0, marginLeft: 0 }, headerContainer: { flex: 1, height: 40, From f06d38fb4bbccb338327388951e9c65cc125df7f Mon Sep 17 00:00:00 2001 From: Kat Date: Thu, 23 Jun 2022 17:42:55 +1200 Subject: [PATCH 021/131] Updates to allow creation of customer credit and refund lines --- src/database/utilities/createRecord.js | 54 +++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/src/database/utilities/createRecord.js b/src/database/utilities/createRecord.js index f8232e0ce..91ac60bd5 100644 --- a/src/database/utilities/createRecord.js +++ b/src/database/utilities/createRecord.js @@ -532,21 +532,31 @@ const createOffsetCustomerCredit = (database, receipt) => { }; const createCustomerRefundLine = (database, customerCredit, transactionBatch) => { - const { total, itemBatch, numberOfPacks } = transactionBatch; - const { item, batch, expiryDate, packSize, costPrice, sellPrice, donor } = itemBatch; + const { totalQuantity, itemBatch, numberOfPacks } = transactionBatch; - const inverseTotal = -total; + const { batch, expiryDate, packSize, costPrice, sellPrice, donor } = itemBatch; + + const inverseTotal = -totalQuantity; + + // Create a TransactionItem to link between the new TransactionBatch and Transaction. + const transactionItem = createTransactionItem( + database, + customerCredit, + itemBatch.item, + inverseTotal + ); const refundLine = database.create('TransactionBatch', { id: generateUUID(), - item, + itemId: transactionItem.id, + itemName: transactionItem.name, + itemBatch, batch, expiryDate, packSize, costPrice, sellPrice, donor, - itemBatch, transaction: customerCredit, total: inverseTotal, type: 'stock_in', @@ -556,15 +566,45 @@ const createCustomerRefundLine = (database, customerCredit, transactionBatch) => customerCredit.outstanding += inverseTotal; itemBatch.addTransactionBatch(refundLine); + itemBatch.totalQuantity -= totalQuantity; + refundLine.setTotalQuantity(database, numberOfPacks); database.save('Transaction', customerCredit); database.save('TransactionBatch', refundLine); - database.save('ItemBatch', customerCredit); + database.save('ItemBatch', itemBatch); return refundLine; }; +/** + * Create a customer credit + * + * @param {Realm} database + * @param {Name} customer Customer associated with invoice. + * @return {Transaction} + */ +const createCustomerCredit = (database, user, otherParty, mode = 'store') => { + const { CUSTOMER_INVOICE_NUMBER } = NUMBER_SEQUENCE_KEYS; + const currentDate = new Date(); + const customerCredit = database.create('Transaction', { + id: generateUUID(), + serialNumber: getNextNumber(database, CUSTOMER_INVOICE_NUMBER), + entryDate: currentDate, + confirmDate: currentDate, + type: 'customer_credit', + status: 'finalised', + comment: '', + otherParty, + enteredBy: user, + mode, + }); + + database.save('Transaction', customerCredit); + + return customerCredit; +}; + /** * Create a customer invoice associated with a given customer. * @@ -1237,6 +1277,8 @@ const createAdverseDrugReaction = (database, patient, formData, user) => { */ export const createRecord = (database, type, ...args) => { switch (type) { + case 'CustomerCredit': + return createCustomerCredit(database, ...args); case 'CustomerRequisition': return createCustomerRequisition(database, ...args); case 'CustomerInvoice': From b4b496472d8437fddf9b0bf15403fdf48359aa0e Mon Sep 17 00:00:00 2001 From: Kat Date: Mon, 27 Jun 2022 16:53:02 +1200 Subject: [PATCH 022/131] UI changes to trigger credit when editing of vaccine transaction. Does not save change --- .../Entities/VaccinePrescriptionActions.js | 20 +++ src/localization/modalStrings.json | 1 + src/localization/vaccineStrings.json | 1 + src/widgets/VaccinatorDropDown.js | 7 +- src/widgets/modals/VaccinationEvent.js | 126 +++++++++++++++--- 5 files changed, 133 insertions(+), 22 deletions(-) diff --git a/src/actions/Entities/VaccinePrescriptionActions.js b/src/actions/Entities/VaccinePrescriptionActions.js index f5a22445b..63c043e12 100644 --- a/src/actions/Entities/VaccinePrescriptionActions.js +++ b/src/actions/Entities/VaccinePrescriptionActions.js @@ -325,12 +325,32 @@ const toggleHistory = toggle => ({ payload: { toggle }, }); +const revertFinalisedVaccination = (patientID, transactionBatch) => (dispatch, getState) => { + const { user } = getState(); + const { currentUser } = user; + const patient = UIDatabase.get('Name', patientID); + + UIDatabase.write(() => { + const customerCredit = createRecord( + UIDatabase, + 'CustomerCredit', + currentUser, + patient, + 'dispensary' + ); + const refundLine = createRecord(UIDatabase, 'RefundLine', customerCredit, transactionBatch); + console.log(customerCredit); + console.log(refundLine); + }); +}; + export const VaccinePrescriptionActions = { cancel, confirm, create, createSupplementaryData, reset, + revertFinalisedVaccination, selectBatch, selectSupplementalData, selectVaccine, diff --git a/src/localization/modalStrings.json b/src/localization/modalStrings.json index 85783ca72..352843e8c 100644 --- a/src/localization/modalStrings.json +++ b/src/localization/modalStrings.json @@ -7,6 +7,7 @@ "cancel": "Cancel", "confirm": "Confirm", "confirm_double_dose": "This patient already has a vaccination recorded within the last 24 hours, do you want to proceed?", + "vaccine_event_not_editable": "Vaccinator and stock details can only be modified on the device it was originally entered on. Please find the tablet used to enter the vaccination.", "create": "Create", "delete_these_fridges": "Are you sure you want to delete these fridges?", "create_cash_transaction": "Create new cash transaction", diff --git a/src/localization/vaccineStrings.json b/src/localization/vaccineStrings.json index d009a6978..c21520436 100644 --- a/src/localization/vaccineStrings.json +++ b/src/localization/vaccineStrings.json @@ -9,6 +9,7 @@ "vaccine_dispense_supplemental_data_title": "Step: 3 Enter additional vaccination details", "vaccine_event_supplemental_data_title": "Page 2: Additional Details", "vaccine_event_transact_data_title": "Page 3: Vaccination Event", + "vaccine_event_transact_data_description": "You may edit the vaccinator or vaccine type.", "vaccinator": "Vaccinator", "logging_delayed_until": "Logging delayed until", "hot_consecutive": "Hot consecutive", diff --git a/src/widgets/VaccinatorDropDown.js b/src/widgets/VaccinatorDropDown.js index 9899b3893..8679b4b11 100644 --- a/src/widgets/VaccinatorDropDown.js +++ b/src/widgets/VaccinatorDropDown.js @@ -5,13 +5,12 @@ import { StyleSheet } from 'react-native'; import { UIDatabase } from '../database/index'; import { DropDown } from './DropDown'; -export const VaccinatorDropDown = ({ value, onChange }) => { +export const VaccinatorDropDown = ({ value, onChange, style }) => { const medicineAdmins = UIDatabase.objects('MedicineAdministrator').sorted('lastName'); const values = medicineAdmins.map(({ displayString }) => displayString); - return ( onChange(medicineAdmins[i])} selectedValue={value?.displayString} @@ -25,9 +24,11 @@ const styles = StyleSheet.create({ VaccinatorDropDown.defaultProps = { value: null, + style: {}, }; VaccinatorDropDown.propTypes = { value: PropTypes.object, onChange: PropTypes.func.isRequired, + style: PropTypes.object, }; diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index b27708136..819c1cc4c 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -1,6 +1,6 @@ /* eslint-disable react/jsx-curly-newline */ /* eslint-disable react/forbid-prop-types */ -import React, { useRef, useState } from 'react'; +import React, { useCallback, useRef, useState } from 'react'; import { View, StyleSheet, Text } from 'react-native'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; @@ -22,11 +22,16 @@ import globalStyles, { GREY, SUSSOL_ORANGE, } from '../../globalStyles'; -import { buttonStrings, generalStrings, vaccineStrings } from '../../localization'; +import { buttonStrings, generalStrings, modalStrings, vaccineStrings } from '../../localization'; import { PageButton } from '../PageButton'; -import { NameNoteActions } from '../../actions'; +import { NameNoteActions, VaccinePrescriptionActions } from '../../actions'; import { Paper } from '../Paper'; import { Title } from '../JSONForm/fields'; +import { useToggle } from '../../hooks'; +import { VaccinatorDropDown } from '../VaccinatorDropDown'; +import { DropDown } from '../DropDown'; +import { PaperModalContainer } from '../PaperModal/PaperModalContainer'; +import { PaperConfirmModal } from '../PaperModal/PaperConfirmModal'; // It's possible to get into this state if vaccination events were configured but PCD events weren't // and someone dispensed a vaccine. Some data cleanup may be required. @@ -46,18 +51,34 @@ export const NoPCDForm = () => ( ); export const VaccinationEventComponent = ({ + createCustomerCredit, patient, - supplementalDataSchema, - vaccinationEventId, - vaccinationEventSchema, savePCDForm, saveSupplementalData, + supplementalDataSchema, surveySchema, + vaccinationEventId, + vaccinationEventSchema, + vaccines, }) => { const pcdFormRef = useRef(null); const supplementalFormRef = useRef(null); const vaccinationFormRef = useRef(null); + const vaccinationEventNameNote = UIDatabase.get('NameNote', vaccinationEventId); + const vaccinationEvent = vaccinationEventNameNote.data; + const transaction = UIDatabase.get('Transaction', vaccinationEvent?.extra?.prescription?.id); + const transactionBatch = UIDatabase.objects('TransactionBatch').filtered( + 'transaction.id == $0', + transaction?.id + )[0]; + + const [isEditingTransaction, toggleEditTransaction] = useToggle(false); + const [isModalOpen, toggleModal] = useToggle(false); + const [vaccinator, setVaccinator] = useState(transactionBatch?.medicineAdministrator); + const [vaccine, setVaccine] = useState(transactionBatch?.itemBatch?.item); + const vaccineDropDownValues = vaccines.map(({ code, name }) => `${code}: ${name}`); + const [{ updatedPcdForm, isPCDValid }, setPCDForm] = useState({ updatedPcdForm: null, isPCDValid: false, @@ -66,8 +87,19 @@ export const VaccinationEventComponent = ({ updatedSupplementalDataForm: null, isSupplementalDataValid: false, }); - const vaccinationEventNameNote = UIDatabase.get('NameNote', vaccinationEventId); - const vaccinationEvent = vaccinationEventNameNote.data; + + // User cannot edit 'Vaccination Event' panel if vaccination was done on a different tablet/store + const tryEdit = useCallback(() => { + if (!transaction) { + toggleModal(); + } else { + toggleEditTransaction(); + } + }, [transaction]); + + const trySave = useCallback(() => { + createCustomerCredit(patient, transactionBatch); + }, [patient, transactionBatch]); const { pcdNameNoteId } = vaccinationEvent; const surveyForm = pcdNameNoteId @@ -142,14 +174,53 @@ export const VaccinationEventComponent = ({ {!!vaccinationEventSchema && !!vaccinationEvent && ( - + } + contentContainerStyle={{ flex: 1 }} + style={{ flex: 1 }} + headerContainerStyle={localStyles.title} > - <> - + {!isEditingTransaction ? ( + + <> + + ) : ( + + + + {vaccineStrings.vaccine_event_transact_data_description} + + + <VaccinatorDropDown + onChange={setVaccinator} + value={vaccinator} + style={{ width: null, flex: 1 }} + /> + <Title title={vaccineStrings.vaccines} size="medium" /> + <DropDown + style={(localStyles.dropdown, { width: null, flex: 1 })} + values={vaccineDropDownValues} + onValueChange={(_, i) => setVaccine(vaccines[i])} + selectedValue={`${vaccine?.code}: ${vaccine?.name}`} + /> + </FlexColumn> + <FlexRow flex={1} style={{ marginBottom: 10 }}> + <PageButton + style={{ flex: 1, alignSelf: 'flex-end' }} + text="Cancel Editing" + onPress={toggleEditTransaction} + /> + </FlexRow> + </FlexView> + )} + </Paper> </View> </FlexRow> )} @@ -172,12 +243,19 @@ export const VaccinationEventComponent = ({ isDisabled={!isSupplementalDataValid} /> <PageButton - text={buttonStrings.save_changes} + text={isEditingTransaction ? buttonStrings.save_changes : buttonStrings.edit} style={localStyles.saveButton} textStyle={localStyles.saveButtonTextStyle} - isDisabled={true} + onPress={isEditingTransaction ? trySave : tryEdit} /> </FlexRow> + <PaperModalContainer isVisible={isModalOpen} onClose={toggleModal}> + <PaperConfirmModal + questionText={modalStrings.vaccine_event_not_editable} + confirmText={modalStrings.confirm} + onConfirm={toggleModal} + /> + </PaperModalContainer> </FlexView> ); }; @@ -192,10 +270,13 @@ const mapStateToProps = () => { const supplementalDataSchemas = selectSupplementalDataSchemas(); const [supplementalDataSchema] = supplementalDataSchemas; + const vaccines = UIDatabase.objects('Vaccine').sorted('name'); + return { + supplementalDataSchema, surveySchema, vaccinationEventSchema, - supplementalDataSchema, + vaccines, }; }; @@ -220,10 +301,15 @@ const mapDispatchToProps = dispatch => { dispatch(NameNoteActions.updateNameNote(vaccinationEventNameNote, updatedData)); }; - return { savePCDForm, saveSupplementalData }; + const createCustomerCredit = (patient, transactionBatch) => { + dispatch(VaccinePrescriptionActions.revertFinalisedVaccination(patient.id, transactionBatch)); + }; + + return { createCustomerCredit, savePCDForm, saveSupplementalData }; }; const localStyles = StyleSheet.create({ + dropdown: { height: 35, marginTop: 0, marginBottom: 0, marginLeft: 0 }, formContainer: { flex: 1, flexDirection: 'row', @@ -256,6 +342,7 @@ VaccinationEventComponent.defaultProps = { }; VaccinationEventComponent.propTypes = { + createCustomerCredit: PropTypes.func.isRequired, patient: PropTypes.object, savePCDForm: PropTypes.func.isRequired, saveSupplementalData: PropTypes.func.isRequired, @@ -263,6 +350,7 @@ VaccinationEventComponent.propTypes = { surveySchema: PropTypes.object.isRequired, vaccinationEventId: PropTypes.string, vaccinationEventSchema: PropTypes.object.isRequired, + vaccines: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired, }; export const VaccinationEvent = connect( From 8a48d6641c82d4a8dc175ecc4fbb5dacb6b74194 Mon Sep 17 00:00:00 2001 From: Kat <katherine@sussol.net> Date: Tue, 28 Jun 2022 17:29:36 +1200 Subject: [PATCH 023/131] Add stock instead of subtracting stock when creating credit --- src/database/utilities/createRecord.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/database/utilities/createRecord.js b/src/database/utilities/createRecord.js index 91ac60bd5..0886ae3f2 100644 --- a/src/database/utilities/createRecord.js +++ b/src/database/utilities/createRecord.js @@ -565,11 +565,11 @@ const createCustomerRefundLine = (database, customerCredit, transactionBatch) => customerCredit.outstanding += inverseTotal; - itemBatch.addTransactionBatch(refundLine); - itemBatch.totalQuantity -= totalQuantity; - refundLine.setTotalQuantity(database, numberOfPacks); + itemBatch.addTransactionBatch(refundLine); + itemBatch.totalQuantity += totalQuantity; + database.save('Transaction', customerCredit); database.save('TransactionBatch', refundLine); database.save('ItemBatch', itemBatch); From aae73df4d11f777c8c971577ff00ff9a9d38e28f Mon Sep 17 00:00:00 2001 From: Kat <katherine@sussol.net> Date: Wed, 29 Jun 2022 15:07:18 +1200 Subject: [PATCH 024/131] Correct dodgy stock maths and fix customer invoice display for credits --- .../Entities/VaccinePrescriptionActions.js | 18 ++++++------------ src/database/utilities/createRecord.js | 18 +++++------------- src/navigation/constants.js | 1 + src/pages/CustomerInvoicePage.js | 9 +++++++-- src/pages/dataTableUtilities/getColumns.js | 10 ++++++++++ .../Entities/VaccinePrescriptionReducer.js | 8 -------- src/selectors/Entities/vaccinePrescription.js | 6 ------ src/widgets/modals/VaccinationEvent.js | 12 ++++++------ 8 files changed, 35 insertions(+), 47 deletions(-) diff --git a/src/actions/Entities/VaccinePrescriptionActions.js b/src/actions/Entities/VaccinePrescriptionActions.js index 63c043e12..e965d3adf 100644 --- a/src/actions/Entities/VaccinePrescriptionActions.js +++ b/src/actions/Entities/VaccinePrescriptionActions.js @@ -30,7 +30,6 @@ export const VACCINE_PRESCRIPTION_ACTIONS = { SELECT_BATCH: 'VACCINE_PRESCRIPTION/selectBatch', SELECT_VACCINATOR: 'VACCINE_PRESCRIPTION/selectVaccinator', SET_BONUS_DOSE: 'VACCINE_PRESCRIPTION/setBonusDose', - TOGGLE_HISTORY: 'VACCINE_PRESCRIPTION/toggleHistory', SELECT_DEFAULT_VACCINE: 'VACCINE_PRESCRIPTION/selectDefaultVaccine', }; @@ -320,14 +319,10 @@ const confirmAndRepeat = () => dispatch => dispatch(gotoVaccineDispensingPage()); }); -const toggleHistory = toggle => ({ - type: VACCINE_PRESCRIPTION_ACTIONS.TOGGLE_HISTORY, - payload: { toggle }, -}); - -const revertFinalisedVaccination = (patientID, transactionBatch) => (dispatch, getState) => { +const returnVaccineToStock = (patientID, transactionBatch) => (dispatch, getState) => { const { user } = getState(); const { currentUser } = user; + const { totalQuantity } = transactionBatch; const patient = UIDatabase.get('Name', patientID); UIDatabase.write(() => { @@ -336,11 +331,11 @@ const revertFinalisedVaccination = (patientID, transactionBatch) => (dispatch, g 'CustomerCredit', currentUser, patient, + -totalQuantity, 'dispensary' ); - const refundLine = createRecord(UIDatabase, 'RefundLine', customerCredit, transactionBatch); - console.log(customerCredit); - console.log(refundLine); + createRecord(UIDatabase, 'RefundLine', customerCredit, transactionBatch); + customerCredit.finalise(UIDatabase); }); }; @@ -350,7 +345,7 @@ export const VaccinePrescriptionActions = { create, createSupplementaryData, reset, - revertFinalisedVaccination, + returnVaccineToStock, selectBatch, selectSupplementalData, selectVaccine, @@ -359,7 +354,6 @@ export const VaccinePrescriptionActions = { selectVaccinator, confirmAndRepeat, setBonusDose, - toggleHistory, selectDefaultVaccine, updateSupplementalData, }; diff --git a/src/database/utilities/createRecord.js b/src/database/utilities/createRecord.js index 0886ae3f2..7bd0e5e75 100644 --- a/src/database/utilities/createRecord.js +++ b/src/database/utilities/createRecord.js @@ -532,18 +532,16 @@ const createOffsetCustomerCredit = (database, receipt) => { }; const createCustomerRefundLine = (database, customerCredit, transactionBatch) => { - const { totalQuantity, itemBatch, numberOfPacks } = transactionBatch; + const { totalQuantity, itemBatch } = transactionBatch; const { batch, expiryDate, packSize, costPrice, sellPrice, donor } = itemBatch; - const inverseTotal = -totalQuantity; - // Create a TransactionItem to link between the new TransactionBatch and Transaction. const transactionItem = createTransactionItem( database, customerCredit, itemBatch.item, - inverseTotal + totalQuantity ); const refundLine = database.create('TransactionBatch', { @@ -558,19 +556,13 @@ const createCustomerRefundLine = (database, customerCredit, transactionBatch) => sellPrice, donor, transaction: customerCredit, - total: inverseTotal, + total: totalQuantity, type: 'stock_in', note: 'credit', }); - customerCredit.outstanding += inverseTotal; - - refundLine.setTotalQuantity(database, numberOfPacks); - itemBatch.addTransactionBatch(refundLine); - itemBatch.totalQuantity += totalQuantity; - database.save('Transaction', customerCredit); database.save('TransactionBatch', refundLine); database.save('ItemBatch', itemBatch); @@ -584,7 +576,7 @@ const createCustomerRefundLine = (database, customerCredit, transactionBatch) => * @param {Name} customer Customer associated with invoice. * @return {Transaction} */ -const createCustomerCredit = (database, user, otherParty, mode = 'store') => { +const createCustomerCredit = (database, user, otherParty, returnAmount, mode = 'store') => { const { CUSTOMER_INVOICE_NUMBER } = NUMBER_SEQUENCE_KEYS; const currentDate = new Date(); const customerCredit = database.create('Transaction', { @@ -593,11 +585,11 @@ const createCustomerCredit = (database, user, otherParty, mode = 'store') => { entryDate: currentDate, confirmDate: currentDate, type: 'customer_credit', - status: 'finalised', comment: '', otherParty, enteredBy: user, mode, + subtotal: returnAmount, }); database.save('Transaction', customerCredit); diff --git a/src/navigation/constants.js b/src/navigation/constants.js index b498b9e5e..e1af0a8dc 100644 --- a/src/navigation/constants.js +++ b/src/navigation/constants.js @@ -22,6 +22,7 @@ export const ROUTES = { SUPPLIER_REQUISITIONS_WITH_PROGRAMS: 'supplierRequisitionsWithPrograms', CUSTOMER_CREDIT: 'customerCredit', + CUSTOMER_CREDIT_WITH_VACCINES: 'customerCreditWithVaccines', CUSTOMER_INVOICE: 'customerInvoice', CUSTOMER_INVOICE_WITH_VACCINES: 'customerInvoiceWithVaccines', CUSTOMER_INVOICES: 'customerInvoices', diff --git a/src/pages/CustomerInvoicePage.js b/src/pages/CustomerInvoicePage.js index 32dc4d468..1888f3ebc 100644 --- a/src/pages/CustomerInvoicePage.js +++ b/src/pages/CustomerInvoicePage.js @@ -117,7 +117,6 @@ export const CustomerInvoice = ({ const renderRow = useCallback( listItem => { const { item, index } = listItem; - const rowKey = keyExtractor(item); return ( <DataTableRow @@ -230,7 +229,13 @@ const mapStateToProps = state => { const { usingVaccines } = modules; const { pageObject } = customerInvoice ?? {}; const { isCredit = false } = pageObject ?? {}; - if (isCredit) return { ...customerInvoice, columns: getColumns(ROUTES.CUSTOMER_CREDIT) }; + + if (isCredit) { + if (usingVaccines && pageObject.hasVaccine) { + return { ...customerInvoice, columns: getColumns(ROUTES.CUSTOMER_CREDIT_WITH_VACCINES) }; + } + return { ...customerInvoice, columns: getColumns(ROUTES.CUSTOMER_CREDIT) }; + } if (usingVaccines && pageObject?.hasVaccine) { return { ...customerInvoice, columns: getColumns(ROUTES.CUSTOMER_INVOICE_WITH_VACCINES) }; } diff --git a/src/pages/dataTableUtilities/getColumns.js b/src/pages/dataTableUtilities/getColumns.js index 4ba137d1c..3fa178e73 100644 --- a/src/pages/dataTableUtilities/getColumns.js +++ b/src/pages/dataTableUtilities/getColumns.js @@ -32,6 +32,7 @@ const PAGE_COLUMN_WIDTHS = { [MODALS.SUPPLIER_CREDIT_FROM_ITEM]: [1, 1, 1, 1, 1], [ROUTES.CASH_REGISTER]: [1, 2, 1, 1, 1, 2, 2], [ROUTES.CUSTOMER_CREDIT]: [2, 4, 2, 2, 1], + [ROUTES.CUSTOMER_CREDIT_WITH_VACCINES]: [2, 4, 2, 2, 1, 1, 1], [ROUTES.CUSTOMER_INVOICE]: [2, 4, 2, 2, 1], [ROUTES.CUSTOMER_INVOICE_WITH_VACCINES]: [2, 4, 2, 2, 1, 1, 1], [ROUTES.CUSTOMER_INVOICES]: [1.5, 2.5, 2, 1.5, 3, 1], @@ -290,6 +291,15 @@ const PAGE_COLUMNS = { COLUMN_NAMES.EDITABLE_TOTAL_QUANTITY, COLUMN_NAMES.REMOVE, ], + [ROUTES.CUSTOMER_CREDIT_WITH_VACCINES]: [ + COLUMN_NAMES.ITEM_CODE, + COLUMN_NAMES.ITEM_NAME, + COLUMN_NAMES.AVAILABLE_QUANTITY, + COLUMN_NAMES.EDITABLE_TOTAL_QUANTITY, + COLUMN_NAMES.EDITABLE_DOSES, + COLUMN_NAMES.BREACH, + COLUMN_NAMES.REMOVE, + ], [ROUTES.CUSTOMER_INVOICE]: [ COLUMN_NAMES.ITEM_CODE, COLUMN_NAMES.ITEM_NAME, diff --git a/src/reducers/Entities/VaccinePrescriptionReducer.js b/src/reducers/Entities/VaccinePrescriptionReducer.js index c62b4bfb2..04cba9dad 100644 --- a/src/reducers/Entities/VaccinePrescriptionReducer.js +++ b/src/reducers/Entities/VaccinePrescriptionReducer.js @@ -12,7 +12,6 @@ const initialState = () => ({ vaccines: UIDatabase.objects('Vaccine').sorted('name'), vaccinator: null, bonusDose: false, - historyIsOpen: false, isSupplementalDataValid: false, }); @@ -116,13 +115,6 @@ export const VaccinePrescriptionReducer = (state = initialState(), action) => { return { ...state, bonusDose: toggle }; } - case VACCINE_PRESCRIPTION_ACTIONS.TOGGLE_HISTORY: { - const { payload } = action; - const { toggle } = payload; - - return { ...state, historyIsOpen: toggle }; - } - default: { return state; } diff --git a/src/selectors/Entities/vaccinePrescription.js b/src/selectors/Entities/vaccinePrescription.js index e6a3c5bb6..1e5d92fa3 100644 --- a/src/selectors/Entities/vaccinePrescription.js +++ b/src/selectors/Entities/vaccinePrescription.js @@ -93,12 +93,6 @@ export const selectFoundBonusDose = state => { return bonusDose; }; -export const selectHistoryIsOpen = state => { - const VaccinePrescriptionState = selectSpecificEntityState(state, 'vaccinePrescription'); - const { historyIsOpen } = VaccinePrescriptionState; - return historyIsOpen; -}; - export const selectSelectedSupplementalData = state => { const VaccinePrescriptionState = selectSpecificEntityState(state, 'vaccinePrescription'); const { supplementalData } = VaccinePrescriptionState; diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 819c1cc4c..ddd22d064 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -51,7 +51,7 @@ export const NoPCDForm = () => ( ); export const VaccinationEventComponent = ({ - createCustomerCredit, + editTransaction, patient, savePCDForm, saveSupplementalData, @@ -98,7 +98,7 @@ export const VaccinationEventComponent = ({ }, [transaction]); const trySave = useCallback(() => { - createCustomerCredit(patient, transactionBatch); + editTransaction(patient, transactionBatch); }, [patient, transactionBatch]); const { pcdNameNoteId } = vaccinationEvent; @@ -301,11 +301,11 @@ const mapDispatchToProps = dispatch => { dispatch(NameNoteActions.updateNameNote(vaccinationEventNameNote, updatedData)); }; - const createCustomerCredit = (patient, transactionBatch) => { - dispatch(VaccinePrescriptionActions.revertFinalisedVaccination(patient.id, transactionBatch)); + const editTransaction = (patient, transactionBatch) => { + dispatch(VaccinePrescriptionActions.returnVaccineToStock(patient.id, transactionBatch)); }; - return { createCustomerCredit, savePCDForm, saveSupplementalData }; + return { editTransaction, savePCDForm, saveSupplementalData }; }; const localStyles = StyleSheet.create({ @@ -342,7 +342,7 @@ VaccinationEventComponent.defaultProps = { }; VaccinationEventComponent.propTypes = { - createCustomerCredit: PropTypes.func.isRequired, + editTransaction: PropTypes.func.isRequired, patient: PropTypes.object, savePCDForm: PropTypes.func.isRequired, saveSupplementalData: PropTypes.func.isRequired, From 8084d06da07a954693be00da62d1a2a990049a75 Mon Sep 17 00:00:00 2001 From: Kat <katherine@sussol.net> Date: Wed, 29 Jun 2022 15:35:50 +1200 Subject: [PATCH 025/131] More fixes (total refers to $ not quantity) --- src/database/utilities/createRecord.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/database/utilities/createRecord.js b/src/database/utilities/createRecord.js index 7bd0e5e75..f0994f5b9 100644 --- a/src/database/utilities/createRecord.js +++ b/src/database/utilities/createRecord.js @@ -552,18 +552,17 @@ const createCustomerRefundLine = (database, customerCredit, transactionBatch) => batch, expiryDate, packSize, + numberOfPacks: 0, costPrice, sellPrice, donor, transaction: customerCredit, - total: totalQuantity, type: 'stock_in', note: 'credit', + sentPackSize: packSize, }); itemBatch.addTransactionBatch(refundLine); - - database.save('TransactionBatch', refundLine); database.save('ItemBatch', itemBatch); return refundLine; @@ -589,11 +588,8 @@ const createCustomerCredit = (database, user, otherParty, returnAmount, mode = ' otherParty, enteredBy: user, mode, - subtotal: returnAmount, }); - database.save('Transaction', customerCredit); - return customerCredit; }; From 50c4f6f5dcee83af8946513309e6cf911a17956d Mon Sep 17 00:00:00 2001 From: Kat <katherine@sussol.net> Date: Thu, 30 Jun 2022 11:13:28 +1200 Subject: [PATCH 026/131] Update mobile schema --- src/database/DataTypes/Name.js | 1 + src/database/DataTypes/NameNote.js | 1 + src/database/schema.js | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/database/DataTypes/Name.js b/src/database/DataTypes/Name.js index 6032830a5..b1ec93d3f 100644 --- a/src/database/DataTypes/Name.js +++ b/src/database/DataTypes/Name.js @@ -197,6 +197,7 @@ Name.schema = { lastName: { type: 'string', optional: true }, isActive: { type: 'bool', optional: true }, isDeceased: { type: 'bool', optional: true }, + isDeleted: { type: 'bool', optional: true }, isCustomer: { type: 'bool', default: false }, isSupplier: { type: 'bool', default: false }, isManufacturer: { type: 'bool', default: false }, diff --git a/src/database/DataTypes/NameNote.js b/src/database/DataTypes/NameNote.js index 6e08b4ce3..cb2db3f8e 100644 --- a/src/database/DataTypes/NameNote.js +++ b/src/database/DataTypes/NameNote.js @@ -38,6 +38,7 @@ NameNote.schema = { id: 'string', entryDate: { type: 'date', default: new Date() }, _data: { type: 'string', optional: true }, + isDeleted: { type: 'bool', optional: true }, name: 'Name', note: { type: 'string', optional: true }, patientEvent: 'PatientEvent', diff --git a/src/database/schema.js b/src/database/schema.js index dd284f47f..a2aa71dad 100644 --- a/src/database/schema.js +++ b/src/database/schema.js @@ -257,7 +257,7 @@ export const schema = { VaccineVialMonitorStatus, VaccineVialMonitorStatusLog, ], - schemaVersion: 28, + schemaVersion: 29, }; export default schema; From d1dc44e748e9fab370343b679f72f7260ec57d4f Mon Sep 17 00:00:00 2001 From: Kat <katherine@sussol.net> Date: Thu, 30 Jun 2022 11:17:21 +1200 Subject: [PATCH 027/131] Sync updates for isDeleted --- src/sync/incomingSyncUtils.js | 2 ++ src/sync/outgoingSyncUtils.js | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/sync/incomingSyncUtils.js b/src/sync/incomingSyncUtils.js index cd08b9c5f..2e54026bc 100644 --- a/src/sync/incomingSyncUtils.js +++ b/src/sync/incomingSyncUtils.js @@ -589,6 +589,7 @@ export const createOrUpdateRecord = (database, settings, recordType, record) => type: NAME_TYPES.translate(record.type, EXTERNAL_TO_INTERNAL), isCustomer: parseBoolean(record.customer), isDeceased: parseBoolean(record.isDeceased), + isDeleted: parseBoolean(record.isDeleted), isSupplier: parseBoolean(record.supplier), isManufacturer: parseBoolean(record.manufacturer), supplyingStoreId: record.supplying_store_id, @@ -1155,6 +1156,7 @@ export const createOrUpdateRecord = (database, settings, recordType, record) => patientEvent: database.getOrCreate('PatientEvent', record.patient_event_ID), entryDate: parseDate(record.entry_date), _data: record.data, + isDeleted: parseBoolean(record.isDeleted), name: database.getOrCreate('Name', record.name_ID), note: record.note, }); diff --git a/src/sync/outgoingSyncUtils.js b/src/sync/outgoingSyncUtils.js index 04e1163ff..a92741887 100644 --- a/src/sync/outgoingSyncUtils.js +++ b/src/sync/outgoingSyncUtils.js @@ -99,6 +99,7 @@ const generateSyncData = (settings, recordType, record) => { 'charge code': record.code, currency_id: defaultCurrency?.id ?? '', isDeceased: String(record.isDeceased), + isDeleted: String(record.isDeleted), female: String(record.female), nationality_ID: record.nationality?.id ?? '', occupation_ID: record.occupation?.id ?? '', @@ -423,6 +424,7 @@ const generateSyncData = (settings, recordType, record) => { data: record.data, store_ID: settings.get(THIS_STORE_ID), note: record.note, + isDeleted: String(record.isDeleted), // The NameNote table is in the middle of a migration away from the current impl // where there are fields boolean_value, value, note etc. To avoid having to also // migrate data within mobile, just send the boolean_value field when the name_note From 018cf2f4b83f6260540bcc385e6f889568150e7e Mon Sep 17 00:00:00 2001 From: Kat <katherine@sussol.net> Date: Thu, 30 Jun 2022 12:43:02 +1200 Subject: [PATCH 028/131] Hide soft deleted patients --- src/database/DataTypes/Name.js | 1 + src/hooks/useLocalAndRemotePatients.js | 2 +- src/selectors/Entities/name.js | 3 ++- src/selectors/dispensary.js | 2 +- src/sync/incomingSyncUtils.js | 4 ++-- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/database/DataTypes/Name.js b/src/database/DataTypes/Name.js index b1ec93d3f..b57c8a5a0 100644 --- a/src/database/DataTypes/Name.js +++ b/src/database/DataTypes/Name.js @@ -159,6 +159,7 @@ export class Name extends Realm.Object { lastName: this.lastName, isActive: this.isActive, isDeceased: this.isDeceased, + isDeleted: this.isDeleted, isCustomer: this.isCustomer, isSupplier: this.isSupplier, isManufacturer: this.isManufacturer, diff --git a/src/hooks/useLocalAndRemotePatients.js b/src/hooks/useLocalAndRemotePatients.js index 9d05ead27..c4e7503c8 100644 --- a/src/hooks/useLocalAndRemotePatients.js +++ b/src/hooks/useLocalAndRemotePatients.js @@ -187,7 +187,7 @@ export const useLocalAndRemotePatients = (initialValue = []) => { } dispatch({ type: 'fetch_start' }); - const query = 'lastName BEGINSWITH[c] $0 AND firstName BEGINSWITH[c] $1'; + const query = 'isDeleted == false AND lastName BEGINSWITH[c] $0 AND firstName BEGINSWITH[c] $1'; let patients = UIDatabase.objects('Patient').filtered(query, lastName, firstName); if (dateOfBirth) { diff --git a/src/selectors/Entities/name.js b/src/selectors/Entities/name.js index da982f929..94def2d1a 100644 --- a/src/selectors/Entities/name.js +++ b/src/selectors/Entities/name.js @@ -102,7 +102,8 @@ export const selectVaccinePatientHistory = patient => { const nameNotes = patient?.nameNotes ?.filter( - ({ patientEventID, data }) => + ({ patientEventID, data, isDeleted }) => + !isDeleted && patientEventID === vaccinationPatientEventID && validateJsonSchemaData(selectVaccinationEventSchemas()[0].jsonSchema, data) ) diff --git a/src/selectors/dispensary.js b/src/selectors/dispensary.js index 2e9c72838..fa5a704d8 100644 --- a/src/selectors/dispensary.js +++ b/src/selectors/dispensary.js @@ -45,7 +45,7 @@ export const selectDataSet = ({ dispensary }) => { export const selectData = ({ dispensary }) => { const { data } = dispensary; - return data; + return data.filtered('isDeleted == false'); }; export const selectDataSetInUse = ({ dispensary }) => { diff --git a/src/sync/incomingSyncUtils.js b/src/sync/incomingSyncUtils.js index 2e54026bc..50ae63c1e 100644 --- a/src/sync/incomingSyncUtils.js +++ b/src/sync/incomingSyncUtils.js @@ -589,7 +589,7 @@ export const createOrUpdateRecord = (database, settings, recordType, record) => type: NAME_TYPES.translate(record.type, EXTERNAL_TO_INTERNAL), isCustomer: parseBoolean(record.customer), isDeceased: parseBoolean(record.isDeceased), - isDeleted: parseBoolean(record.isDeleted), + isDeleted: parseBoolean(record.is_deleted), isSupplier: parseBoolean(record.supplier), isManufacturer: parseBoolean(record.manufacturer), supplyingStoreId: record.supplying_store_id, @@ -1156,7 +1156,7 @@ export const createOrUpdateRecord = (database, settings, recordType, record) => patientEvent: database.getOrCreate('PatientEvent', record.patient_event_ID), entryDate: parseDate(record.entry_date), _data: record.data, - isDeleted: parseBoolean(record.isDeleted), + isDeleted: parseBoolean(record.is_deleted), name: database.getOrCreate('Name', record.name_ID), note: record.note, }); From 4191a9a16399f6f6bbf4944440ac6bea8bfebb3e Mon Sep 17 00:00:00 2001 From: Kat <katherine@sussol.net> Date: Thu, 30 Jun 2022 18:36:09 +1200 Subject: [PATCH 029/131] Add defaults for isDeleted --- src/database/DataTypes/Name.js | 2 +- src/database/DataTypes/NameNote.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/database/DataTypes/Name.js b/src/database/DataTypes/Name.js index b57c8a5a0..866e826fc 100644 --- a/src/database/DataTypes/Name.js +++ b/src/database/DataTypes/Name.js @@ -198,7 +198,7 @@ Name.schema = { lastName: { type: 'string', optional: true }, isActive: { type: 'bool', optional: true }, isDeceased: { type: 'bool', optional: true }, - isDeleted: { type: 'bool', optional: true }, + isDeleted: { type: 'bool', default: false, optional: true }, isCustomer: { type: 'bool', default: false }, isSupplier: { type: 'bool', default: false }, isManufacturer: { type: 'bool', default: false }, diff --git a/src/database/DataTypes/NameNote.js b/src/database/DataTypes/NameNote.js index cb2db3f8e..2259343e8 100644 --- a/src/database/DataTypes/NameNote.js +++ b/src/database/DataTypes/NameNote.js @@ -38,7 +38,7 @@ NameNote.schema = { id: 'string', entryDate: { type: 'date', default: new Date() }, _data: { type: 'string', optional: true }, - isDeleted: { type: 'bool', optional: true }, + isDeleted: { type: 'bool', default: false, optional: true }, name: 'Name', note: { type: 'string', optional: true }, patientEvent: 'PatientEvent', From d852abf319164fc743afec35f5f79e8689a33913 Mon Sep 17 00:00:00 2001 From: Kat <katherine@sussol.net> Date: Thu, 30 Jun 2022 18:43:37 +1200 Subject: [PATCH 030/131] Fix the isDeleted filter, prescribers don't have it --- src/selectors/dispensary.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/selectors/dispensary.js b/src/selectors/dispensary.js index fa5a704d8..64962e13e 100644 --- a/src/selectors/dispensary.js +++ b/src/selectors/dispensary.js @@ -44,8 +44,10 @@ export const selectDataSet = ({ dispensary }) => { }; export const selectData = ({ dispensary }) => { + const dataSet = selectDataSet({ dispensary }); const { data } = dispensary; - return data.filtered('isDeleted == false'); + + return dataSet === 'patient' ? data.filtered('isDeleted == false') : data; }; export const selectDataSetInUse = ({ dispensary }) => { From 547509000b74e30a7f096e4d1eef2e315e919370 Mon Sep 17 00:00:00 2001 From: Kat <katherine@sussol.net> Date: Thu, 30 Jun 2022 18:52:17 +1200 Subject: [PATCH 031/131] Match the desktop field name correctly --- src/sync/outgoingSyncUtils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sync/outgoingSyncUtils.js b/src/sync/outgoingSyncUtils.js index a92741887..49e6d40ac 100644 --- a/src/sync/outgoingSyncUtils.js +++ b/src/sync/outgoingSyncUtils.js @@ -99,7 +99,7 @@ const generateSyncData = (settings, recordType, record) => { 'charge code': record.code, currency_id: defaultCurrency?.id ?? '', isDeceased: String(record.isDeceased), - isDeleted: String(record.isDeleted), + is_deleted: String(record.isDeleted), female: String(record.female), nationality_ID: record.nationality?.id ?? '', occupation_ID: record.occupation?.id ?? '', @@ -424,7 +424,7 @@ const generateSyncData = (settings, recordType, record) => { data: record.data, store_ID: settings.get(THIS_STORE_ID), note: record.note, - isDeleted: String(record.isDeleted), + is_deleted: String(record.isDeleted), // The NameNote table is in the middle of a migration away from the current impl // where there are fields boolean_value, value, note etc. To avoid having to also // migrate data within mobile, just send the boolean_value field when the name_note From 4637ebdd66fecc4b5e3f84d5c75abac9f64b8ec0 Mon Sep 17 00:00:00 2001 From: Kat <katherine@sussol.net> Date: Fri, 1 Jul 2022 12:31:17 +1200 Subject: [PATCH 032/131] Create new name note and soft delete old one when vaccine is edited --- src/widgets/modals/VaccinationEvent.js | 55 +++++++++++++++++++++----- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index ddd22d064..dda0d2d73 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -1,9 +1,9 @@ /* eslint-disable react/jsx-curly-newline */ /* eslint-disable react/forbid-prop-types */ import React, { useCallback, useRef, useState } from 'react'; -import { View, StyleSheet, Text } from 'react-native'; +import { View, StyleSheet, Text, ToastAndroid } from 'react-native'; import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; +import { batch, connect } from 'react-redux'; import { FlexRow } from '../FlexRow'; import { JSONForm } from '../JSONForm/JSONForm'; import { @@ -24,7 +24,7 @@ import globalStyles, { } from '../../globalStyles'; import { buttonStrings, generalStrings, modalStrings, vaccineStrings } from '../../localization'; import { PageButton } from '../PageButton'; -import { NameNoteActions, VaccinePrescriptionActions } from '../../actions'; +import { NameActions, NameNoteActions, VaccinePrescriptionActions } from '../../actions'; import { Paper } from '../Paper'; import { Title } from '../JSONForm/fields'; import { useToggle } from '../../hooks'; @@ -76,7 +76,9 @@ export const VaccinationEventComponent = ({ const [isEditingTransaction, toggleEditTransaction] = useToggle(false); const [isModalOpen, toggleModal] = useToggle(false); const [vaccinator, setVaccinator] = useState(transactionBatch?.medicineAdministrator); - const [vaccine, setVaccine] = useState(transactionBatch?.itemBatch?.item); + const [vaccine, setVaccine] = useState( + vaccines.filter(item => item.id === transactionBatch?.itemId) + ); const vaccineDropDownValues = vaccines.map(({ code, name }) => `${code}: ${name}`); const [{ updatedPcdForm, isPCDValid }, setPCDForm] = useState({ @@ -97,10 +99,6 @@ export const VaccinationEventComponent = ({ } }, [transaction]); - const trySave = useCallback(() => { - editTransaction(patient, transactionBatch); - }, [patient, transactionBatch]); - const { pcdNameNoteId } = vaccinationEvent; const surveyForm = pcdNameNoteId ? UIDatabase.get('NameNote', pcdNameNoteId) @@ -117,6 +115,25 @@ export const VaccinationEventComponent = ({ }, }; + const trySave = useCallback(() => { + const vaccineChanged = vaccine.code !== transactionBatch?.itemBatch?.item?.code; + const vaccinatorChanged = + JSON.stringify(vaccinator) !== JSON.stringify(transactionBatch.medicineAdministrator); + + if (vaccineChanged || vaccinatorChanged) { + editTransaction( + patient, + transactionBatch, + vaccine, + vaccinator, + customDataObject, + vaccinationEventNameNote + ); + } else { + ToastAndroid.show(vaccineStrings.vaccination_not_updated, ToastAndroid.LONG); + } + }, [patient, transactionBatch, vaccine]); + return ( <FlexView> <FlexRow flex={1} style={localStyles.formContainer}> @@ -301,8 +318,26 @@ const mapDispatchToProps = dispatch => { dispatch(NameNoteActions.updateNameNote(vaccinationEventNameNote, updatedData)); }; - const editTransaction = (patient, transactionBatch) => { - dispatch(VaccinePrescriptionActions.returnVaccineToStock(patient.id, transactionBatch)); + const editTransaction = ( + patient, + transactionBatch, + vaccine, + vaccinator, + supplementalData, + vaccinationEventNameNote + ) => { + batch(() => { + dispatch(VaccinePrescriptionActions.returnVaccineToStock(patient.id, transactionBatch)); + dispatch(NameActions.select(patient)); + dispatch(VaccinePrescriptionActions.selectVaccinator(vaccinator)); + dispatch(VaccinePrescriptionActions.selectVaccine(UIDatabase.get('Item', vaccine.id))); + dispatch(VaccinePrescriptionActions.selectSupplementalData(supplementalData)); + dispatch(VaccinePrescriptionActions.confirm()); + }); + + UIDatabase.write(() => { + UIDatabase.update('NameNote', { id: vaccinationEventNameNote.id, isDeleted: true }); + }); }; return { editTransaction, savePCDForm, saveSupplementalData }; From 0a032da9b3d439111f2f8ac0bbd1c43ba52e1c72 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 5 Jul 2022 13:40:35 +0545 Subject: [PATCH 033/131] Update date-picker to properly set initialValue - Also take the same value format for all --- src/widgets/JSONForm/widgets/DatePicker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/JSONForm/widgets/DatePicker.js b/src/widgets/JSONForm/widgets/DatePicker.js index 2611670f4..07d1641a8 100644 --- a/src/widgets/JSONForm/widgets/DatePicker.js +++ b/src/widgets/JSONForm/widgets/DatePicker.js @@ -34,7 +34,7 @@ export const DatePicker = ({ underlineColorAndroid={DARKER_GREY} placeholder={placeholder} editable={!(readonly || disabled)} - value={value} + value={moment(value).format(DATE_FORMAT.DD_MM_YYYY)} ref={ref} onSubmitEditing={() => focusController.next(ref)} onChangeText={handleChange} @@ -45,7 +45,7 @@ export const DatePicker = ({ /> <DatePickerButton isDisabled={readonly || disabled} - initialValue={new Date()} + initialValue={new Date(moment(value).format(DATE_FORMAT.DD_MM_YYYY))} minimumDate={options.dateRange === 'future' ? new Date() : null} maximumDate={options.dateRange === 'past' ? new Date() : null} onDateChanged={date => handleChange(moment(date).format(DATE_FORMAT.DD_MM_YYYY))} From b991467040e31e5af27ff77f6ae4812cb09f7d1b Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 5 Jul 2022 16:54:13 +0545 Subject: [PATCH 034/131] Update datepicker date value logic --- src/widgets/JSONForm/widgets/DatePicker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/JSONForm/widgets/DatePicker.js b/src/widgets/JSONForm/widgets/DatePicker.js index 07d1641a8..f3388bf80 100644 --- a/src/widgets/JSONForm/widgets/DatePicker.js +++ b/src/widgets/JSONForm/widgets/DatePicker.js @@ -34,7 +34,7 @@ export const DatePicker = ({ underlineColorAndroid={DARKER_GREY} placeholder={placeholder} editable={!(readonly || disabled)} - value={moment(value).format(DATE_FORMAT.DD_MM_YYYY)} + value={value} ref={ref} onSubmitEditing={() => focusController.next(ref)} onChangeText={handleChange} @@ -45,7 +45,7 @@ export const DatePicker = ({ /> <DatePickerButton isDisabled={readonly || disabled} - initialValue={new Date(moment(value).format(DATE_FORMAT.DD_MM_YYYY))} + initialValue={moment(value, DATE_FORMAT.DD_MM_YYYY).toDate()} minimumDate={options.dateRange === 'future' ? new Date() : null} maximumDate={options.dateRange === 'past' ? new Date() : null} onDateChanged={date => handleChange(moment(date).format(DATE_FORMAT.DD_MM_YYYY))} From 80e93c63a5994c8b366090d60ae6b2883e53b777 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:36:57 +1200 Subject: [PATCH 035/131] added localization string --- src/localization/modalStrings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/localization/modalStrings.json b/src/localization/modalStrings.json index 352843e8c..76cf714a3 100644 --- a/src/localization/modalStrings.json +++ b/src/localization/modalStrings.json @@ -9,6 +9,7 @@ "confirm_double_dose": "This patient already has a vaccination recorded within the last 24 hours, do you want to proceed?", "vaccine_event_not_editable": "Vaccinator and stock details can only be modified on the device it was originally entered on. Please find the tablet used to enter the vaccination.", "create": "Create", + "deceased_patient_vaccination": "You cannot dispense vaccines to a deceased patient!", "delete_these_fridges": "Are you sure you want to delete these fridges?", "create_cash_transaction": "Create new cash transaction", "delete_these_invoices": "Are you sure you want to delete these invoices?", From 1c76c672ae85d8dcb32b9b67f475452ff642452c Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 12 Jul 2022 12:37:07 +1200 Subject: [PATCH 036/131] check that patient is not deceased --- src/widgets/Tabs/PatientSelect.js | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/widgets/Tabs/PatientSelect.js b/src/widgets/Tabs/PatientSelect.js index 1de08bf1c..3c9fffaa7 100644 --- a/src/widgets/Tabs/PatientSelect.js +++ b/src/widgets/Tabs/PatientSelect.js @@ -18,6 +18,8 @@ import { PageButton } from '../PageButton'; import { FlexRow } from '../FlexRow'; import { FlexView } from '../FlexView'; import { PageButtonWithOnePress } from '../PageButtonWithOnePress'; +import { PaperModalContainer } from '../PaperModal/PaperModalContainer'; +import { PaperConfirmModal } from '../PaperModal/PaperConfirmModal'; import { selectSpecificEntityState } from '../../selectors/Entities'; @@ -145,6 +147,7 @@ const PatientSelectComponent = ({ }) => { const withLoadingIndicator = useLoadingIndicator(); const [isQrModalOpen, toggleQrModal] = useToggle(); + const [isModalOpen, toggleIsDeceasedAlert] = useToggle(false); const [{ history, patient } = {}, setPatientHistory] = useState({}); const [vaccinationEvent, setVaccinationEvent] = useState(null); @@ -253,12 +256,16 @@ const PatientSelectComponent = ({ rowKey={keyExtractor(item)} columns={columns} onPress={name => { - // Only show a spinner when the name doesn't exist in the database, as we need to - // send a request to the server to add a name store join. - if (UIDatabase.get('Name', name?.id)) { - selectPatient(name); + if (!name?.isDeceased) { + // Only show a spinner when the name doesn't exist in the database, as we need to + // send a request to the server to add a name store join. + if (UIDatabase.get('Name', name?.id)) { + selectPatient(name); + } else { + withLoadingIndicator(() => selectPatient(name)); + } } else { - withLoadingIndicator(() => selectPatient(name)); + toggleIsDeceasedAlert(); } }} rowIndex={index} @@ -344,6 +351,13 @@ const PatientSelectComponent = ({ > <VaccinationEvent vaccinationEventId={vaccinationEvent?.id} patient={patient} /> </ModalContainer> + <PaperModalContainer isVisible={isModalOpen} onClose={toggleIsDeceasedAlert}> + <PaperConfirmModal + questionText={modalStrings.deceased_patient_vaccination} + confirmText={generalStrings.ok} + onConfirm={toggleIsDeceasedAlert} + /> + </PaperModalContainer> </FlexView> ); }; @@ -357,8 +371,10 @@ const mapDispatchToProps = dispatch => { const selectedPatient = await dispatch(NameActions.select(patient)); if (selectedPatient) { - dispatch(NameNoteActions.createSurveyNameNote(selectedPatient)); - dispatch(WizardActions.nextTab()); + if (!selectedPatient.isDeceased) { + dispatch(NameNoteActions.createSurveyNameNote(selectedPatient)); + dispatch(WizardActions.nextTab()); + } } }); From b8616ba12f60a200577dabcb4ff088567454c209 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:49:43 +1200 Subject: [PATCH 037/131] fixed styling fro disabled toggle --- src/widgets/FormInputs/FormToggle.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/widgets/FormInputs/FormToggle.js b/src/widgets/FormInputs/FormToggle.js index ebddc872b..d80d6af22 100644 --- a/src/widgets/FormInputs/FormToggle.js +++ b/src/widgets/FormInputs/FormToggle.js @@ -54,6 +54,7 @@ export const FormToggle = ({ toggleOnStyle={localStyles.toggleOnStyle} toggleOffStyle={localStyles.toggleOffStyle} toggleOnDisabledStyle={localStyles.toggleOnDisabledStyle} + toggleOffDisabledStyle={localStyles.toggleOffDisabledStyle} /> </FlexView> ); @@ -86,6 +87,14 @@ const localStyles = StyleSheet.create({ borderRadius: 20, margin: 5, }, + toggleOffDisabledStyle: { + flex: 1, + alignItems: 'center', + justifyContent: 'center', + borderColor: WARMER_GREY, + borderRadius: 20, + margin: 5, + }, }); FormToggle.defaultProps = { From 1e6a051df048c0cfaf2972377b2bc78069e2e4e1 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 12 Jul 2022 14:56:04 +1200 Subject: [PATCH 038/131] fixed bug with disable toggle showing incorrectly --- src/widgets/ToggleBar/ToggleBar.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/widgets/ToggleBar/ToggleBar.js b/src/widgets/ToggleBar/ToggleBar.js index 2d34b01e4..11e693061 100644 --- a/src/widgets/ToggleBar/ToggleBar.js +++ b/src/widgets/ToggleBar/ToggleBar.js @@ -56,9 +56,7 @@ export const ToggleBarComponent = props => { const defaultOffTextStyle = isDisabled ? textOffDisabledStyle : textOffStyle; buttons.forEach((button, i) => { - const currentTextStyle = button.isOn - ? [localStyles.textOnStyle, textOnStyle] - : [localStyles.textOffStyle, defaultOffTextStyle]; + const currentTextStyle = button.isOn ? textOnStyle : defaultOffTextStyle; const currentToggleStyle = button.isOn ? defaultOnStyle : defaultOffStyle; const Container = isDisabled ? View : TouchableOpacity; @@ -123,7 +121,7 @@ const localStyles = StyleSheet.create({ borderColor: WARMER_GREY, width: 142, }, - toggleTextDisabledSelected: { + textOffDisabledStyle: { fontFamily: APP_FONT_FAMILY, fontSize: 12, color: WARMER_GREY, @@ -137,9 +135,9 @@ ToggleBarComponent.propTypes = { toggleOffStyle: ViewPropTypes.style, toggleOnStyle: ViewPropTypes.style, textOffStyle: Text.propTypes.style, - textOffDisabledStyle: Text.propTypes.style, textOnStyle: Text.propTypes.style, isDisabled: PropTypes.bool, + textOffDisabledStyle: Text.propTypes.style, toggleOnDisabledStyle: PropTypes.object, toggleOffDisabledStyle: PropTypes.object, }; @@ -149,9 +147,9 @@ ToggleBarComponent.defaultProps = { toggleOffStyle: localStyles.toggleOffStyle, toggleOnStyle: localStyles.toggleOnStyle, textOffStyle: globalStyles.toggleText, - textOffDisabledStyle: globalStyles.toggleTextDisabledSelected, textOnStyle: globalStyles.toggleTextSelected, + isDisabled: false, + textOffDisabledStyle: localStyles.textOffDisabledStyle, toggleOnDisabledStyle: localStyles.toggleOnDisabledStyle, toggleOffDisabledStyle: localStyles.toggleOffDisabledStyle, - isDisabled: false, }; From 8634d79cbe3a5fa080c070a8fc86607e3253c2c8 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 12 Jul 2022 15:22:48 +1200 Subject: [PATCH 039/131] changed variable name --- src/widgets/Tabs/PatientSelect.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/Tabs/PatientSelect.js b/src/widgets/Tabs/PatientSelect.js index 3c9fffaa7..de05ecf4f 100644 --- a/src/widgets/Tabs/PatientSelect.js +++ b/src/widgets/Tabs/PatientSelect.js @@ -147,7 +147,7 @@ const PatientSelectComponent = ({ }) => { const withLoadingIndicator = useLoadingIndicator(); const [isQrModalOpen, toggleQrModal] = useToggle(); - const [isModalOpen, toggleIsDeceasedAlert] = useToggle(false); + const [isDeceasedModalOpen, toggleIsDeceasedAlert] = useToggle(false); const [{ history, patient } = {}, setPatientHistory] = useState({}); const [vaccinationEvent, setVaccinationEvent] = useState(null); @@ -351,7 +351,7 @@ const PatientSelectComponent = ({ > <VaccinationEvent vaccinationEventId={vaccinationEvent?.id} patient={patient} /> </ModalContainer> - <PaperModalContainer isVisible={isModalOpen} onClose={toggleIsDeceasedAlert}> + <PaperModalContainer isVisible={isDeceasedModalOpen} onClose={toggleIsDeceasedAlert}> <PaperConfirmModal questionText={modalStrings.deceased_patient_vaccination} confirmText={generalStrings.ok} From ed2f8825c4c10b2bd5e7cf139f6af88f836ebf6a Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 12 Jul 2022 15:25:24 +1200 Subject: [PATCH 040/131] Add an alert if user creates new Deceased patient or changes current patient to deceased --- src/widgets/Tabs/PatientEdit.js | 36 +++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/widgets/Tabs/PatientEdit.js b/src/widgets/Tabs/PatientEdit.js index a5fd53a54..14efc65ef 100644 --- a/src/widgets/Tabs/PatientEdit.js +++ b/src/widgets/Tabs/PatientEdit.js @@ -14,6 +14,8 @@ import { PageButton } from '../PageButton'; import { FlexRow } from '../FlexRow'; import { FlexView } from '../FlexView'; import { PageButtonWithOnePress } from '../PageButtonWithOnePress'; +import { PaperModalContainer } from '../PaperModal/PaperModalContainer'; +import { PaperConfirmModal } from '../PaperModal/PaperConfirmModal'; import { selectCanEditPatient, selectEditingName } from '../../selectors/Entities/name'; import { selectSurveySchemas } from '../../selectors/formSchema'; @@ -22,8 +24,15 @@ import { WizardActions } from '../../actions/WizardActions'; import { VaccinePrescriptionActions } from '../../actions/Entities/VaccinePrescriptionActions'; import { selectCanSaveForm, selectCompletedForm } from '../../selectors/form'; import { getFormInputConfig } from '../../utilities/formInputConfigs'; - -import { buttonStrings, vaccineStrings, dispensingStrings } from '../../localization'; +import { useToggle } from '../../hooks/useToggle'; + +import { + buttonStrings, + vaccineStrings, + dispensingStrings, + modalStrings, + generalStrings, +} from '../../localization'; import globalStyles from '../../globalStyles'; import { JSONForm } from '../JSONForm/JSONForm'; import { NameNoteActions } from '../../actions/Entities/NameNoteActions'; @@ -55,17 +64,21 @@ const PatientEditComponent = ({ canEditPatient, }) => { const { pageTopViewContainer } = globalStyles; + const [isDeceasedModalOpen, toggleIsDeceasedAlert] = useToggle(false); const formRef = useRef(null); - const savePatient = useCallback( e => { updatePatientDetails(completedForm); - formRef?.current?.submit(e); - - if (canSaveForm) { - onCompleted(); + if (!completedForm.isDeceased) { + formRef?.current?.submit(e); + + if (canSaveForm) { + onCompleted(); + } else { + ToastAndroid.show(dispensingStrings.validation_failed, ToastAndroid.LONG); + } } else { - ToastAndroid.show(dispensingStrings.validation_failed, ToastAndroid.LONG); + toggleIsDeceasedAlert(); } }, [completedForm, canSaveForm] @@ -118,6 +131,13 @@ const PatientEditComponent = ({ style={{ marginLeft: 'auto' }} /> </FlexRow> + <PaperModalContainer isVisible={isDeceasedModalOpen} onClose={toggleIsDeceasedAlert}> + <PaperConfirmModal + questionText={modalStrings.deceased_patient_vaccination} + confirmText={generalStrings.ok} + onConfirm={toggleIsDeceasedAlert} + /> + </PaperModalContainer> </FlexView> ); }; From 996b21e98c0cf5c17b74da5347e72f0f8faea4f3 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 12 Jul 2022 17:01:58 +1200 Subject: [PATCH 041/131] just small cosmetic bug fixed --- src/widgets/CircleButton.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/widgets/CircleButton.js b/src/widgets/CircleButton.js index 59727811a..2018a0926 100644 --- a/src/widgets/CircleButton.js +++ b/src/widgets/CircleButton.js @@ -18,9 +18,9 @@ const SIZE_VALUES = { }; const ICON_SIZE_VALUES = { - small: 10, - medium: 15, - large: 20, + small: 8, + medium: 13, + large: 17, }; /** From ad85126ede1c33b727b3be9335441a0081271cc0 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 12 Jul 2022 13:23:00 +0545 Subject: [PATCH 042/131] Date comes in as different format so make it compatible --- src/widgets/JSONForm/widgets/DatePicker.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/widgets/JSONForm/widgets/DatePicker.js b/src/widgets/JSONForm/widgets/DatePicker.js index f3388bf80..5b15cd19b 100644 --- a/src/widgets/JSONForm/widgets/DatePicker.js +++ b/src/widgets/JSONForm/widgets/DatePicker.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { TextInput } from 'react-native'; import moment from 'moment'; import PropTypes from 'prop-types'; @@ -21,11 +21,23 @@ export const DatePicker = ({ }) => { const { focusController } = useJSONFormOptions(); const ref = focusController.useRegisteredRef(); + + const [selectedDate, setSelectedDate] = useState(null); + const handleChange = dateString => { onChange(dateString); onBlur(id, dateString); }; + React.useEffect(() => { + const alternateFormatDate = moment(value, 'YYYY-MM-DD', true); + const expectedFormateDate = alternateFormatDate.isValid() + ? alternateFormatDate + : moment(value, DATE_FORMAT.DD_MM_YYYY, true); + + setSelectedDate(expectedFormateDate.isValid() ? expectedFormateDate.toDate() : null); + }, [value]); + return ( <FlexRow> <TextInput @@ -34,7 +46,7 @@ export const DatePicker = ({ underlineColorAndroid={DARKER_GREY} placeholder={placeholder} editable={!(readonly || disabled)} - value={value} + value={selectedDate ? moment(selectedDate).format(DATE_FORMAT.DD_MM_YYYY) : ''} ref={ref} onSubmitEditing={() => focusController.next(ref)} onChangeText={handleChange} @@ -45,7 +57,7 @@ export const DatePicker = ({ /> <DatePickerButton isDisabled={readonly || disabled} - initialValue={moment(value, DATE_FORMAT.DD_MM_YYYY).toDate()} + initialValue={selectedDate || moment().toDate()} minimumDate={options.dateRange === 'future' ? new Date() : null} maximumDate={options.dateRange === 'past' ? new Date() : null} onDateChanged={date => handleChange(moment(date).format(DATE_FORMAT.DD_MM_YYYY))} From 7cc1233311bd64ff03002b002899677c85834c60 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 12:01:56 +1200 Subject: [PATCH 043/131] refactor code --- src/widgets/Tabs/PatientEdit.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/widgets/Tabs/PatientEdit.js b/src/widgets/Tabs/PatientEdit.js index 14efc65ef..745289590 100644 --- a/src/widgets/Tabs/PatientEdit.js +++ b/src/widgets/Tabs/PatientEdit.js @@ -69,16 +69,16 @@ const PatientEditComponent = ({ const savePatient = useCallback( e => { updatePatientDetails(completedForm); - if (!completedForm.isDeceased) { - formRef?.current?.submit(e); + if (completedForm.isDeceased) { + toggleIsDeceasedAlert(); + return; + } - if (canSaveForm) { - onCompleted(); - } else { - ToastAndroid.show(dispensingStrings.validation_failed, ToastAndroid.LONG); - } + formRef?.current?.submit(e); + if (canSaveForm) { + onCompleted(); } else { - toggleIsDeceasedAlert(); + ToastAndroid.show(dispensingStrings.validation_failed, ToastAndroid.LONG); } }, [completedForm, canSaveForm] From 45f396cc5946091857b64a3a527ad97f4456a372 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 12:14:34 +1200 Subject: [PATCH 044/131] code refactoring 2 --- src/widgets/Tabs/PatientSelect.js | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/widgets/Tabs/PatientSelect.js b/src/widgets/Tabs/PatientSelect.js index de05ecf4f..63832a268 100644 --- a/src/widgets/Tabs/PatientSelect.js +++ b/src/widgets/Tabs/PatientSelect.js @@ -257,15 +257,16 @@ const PatientSelectComponent = ({ columns={columns} onPress={name => { if (!name?.isDeceased) { - // Only show a spinner when the name doesn't exist in the database, as we need to - // send a request to the server to add a name store join. - if (UIDatabase.get('Name', name?.id)) { - selectPatient(name); - } else { - withLoadingIndicator(() => selectPatient(name)); - } - } else { toggleIsDeceasedAlert(); + return; + } + + // Only show a spinner when the name doesn't exist in the database, as we need to + // send a request to the server to add a name store join. + if (UIDatabase.get('Name', name?.id)) { + selectPatient(name); + } else { + withLoadingIndicator(() => selectPatient(name)); } }} rowIndex={index} From af95de77ee6509e1a9d03aa3323e6b6674fcc06b Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 12:54:37 +1200 Subject: [PATCH 045/131] refactor code 3 --- src/widgets/Tabs/PatientSelect.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/widgets/Tabs/PatientSelect.js b/src/widgets/Tabs/PatientSelect.js index 63832a268..bfcd54f3d 100644 --- a/src/widgets/Tabs/PatientSelect.js +++ b/src/widgets/Tabs/PatientSelect.js @@ -371,11 +371,9 @@ const mapDispatchToProps = dispatch => { Keyboard.dismiss(); const selectedPatient = await dispatch(NameActions.select(patient)); - if (selectedPatient) { - if (!selectedPatient.isDeceased) { - dispatch(NameNoteActions.createSurveyNameNote(selectedPatient)); - dispatch(WizardActions.nextTab()); - } + if (selectedPatient && !selectedPatient.isDeceased) { + dispatch(NameNoteActions.createSurveyNameNote(selectedPatient)); + dispatch(WizardActions.nextTab()); } }); From 541975274f558084dac6227656cbda8e1edda77e Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 13:02:55 +1200 Subject: [PATCH 046/131] Edited the localization string as per Adam's comment --- src/localization/modalStrings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/localization/modalStrings.json b/src/localization/modalStrings.json index 76cf714a3..fc88ee7af 100644 --- a/src/localization/modalStrings.json +++ b/src/localization/modalStrings.json @@ -9,7 +9,7 @@ "confirm_double_dose": "This patient already has a vaccination recorded within the last 24 hours, do you want to proceed?", "vaccine_event_not_editable": "Vaccinator and stock details can only be modified on the device it was originally entered on. Please find the tablet used to enter the vaccination.", "create": "Create", - "deceased_patient_vaccination": "You cannot dispense vaccines to a deceased patient!", + "deceased_patient_vaccination": "Can not administer vaccination - this patient is deceased.", "delete_these_fridges": "Are you sure you want to delete these fridges?", "create_cash_transaction": "Create new cash transaction", "delete_these_invoices": "Are you sure you want to delete these invoices?", From b6dd3ad54e4bfd9cd031cebd01e8f686bcec97fe Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 14:36:31 +1200 Subject: [PATCH 047/131] adjustments to NameNote toObject structure (to include isDelete) --- src/database/DataTypes/NameNote.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/database/DataTypes/NameNote.js b/src/database/DataTypes/NameNote.js index 2259343e8..a6daff150 100644 --- a/src/database/DataTypes/NameNote.js +++ b/src/database/DataTypes/NameNote.js @@ -24,6 +24,7 @@ export class NameNote extends Realm.Object { id: this.id, entryDate: this.entryDate?.getTime(), data: this.data, + isDeleted: this.isDeleted, nameID: this.name?.id, note: this.note, patientEventID: this.patientEvent?.id, From 123753cb48691d62f6e560f478639bbc9044fd99 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 14:37:23 +1200 Subject: [PATCH 048/131] localizations for deletion of vaccination event --- src/localization/buttonStrings.json | 1 + src/localization/modalStrings.json | 1 + 2 files changed, 2 insertions(+) diff --git a/src/localization/buttonStrings.json b/src/localization/buttonStrings.json index bcda6384a..0e66a1efd 100644 --- a/src/localization/buttonStrings.json +++ b/src/localization/buttonStrings.json @@ -13,6 +13,7 @@ "create_automatic_order": "Create Automatic Order", "current": "Current", "done": "Done", + "delete_vaccination_event": "Delete vaccination event", "edit": "Edit", "export_data": "Export Data", "import_data": "Import Data", diff --git a/src/localization/modalStrings.json b/src/localization/modalStrings.json index 352843e8c..1bf64320f 100644 --- a/src/localization/modalStrings.json +++ b/src/localization/modalStrings.json @@ -14,6 +14,7 @@ "delete_these_invoices": "Are you sure you want to delete these invoices?", "delete_these_requisitions": "Are you sure you want to delete these requisitions?", "delete_these_stocktakes": "Are you sure you want to delete these stocktakes?", + "delete_vaccination_event": "Are you sure you want to delete this vaccination event?", "delete": "Delete", "edit_the_invoice_comment": "Edit the invoice comment", "edit_the_requisition_comment": "Edit the requisition comment", From 01766354f369655a12695a4965274def6ba462c9 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 15:14:14 +1200 Subject: [PATCH 049/131] use isDeleted field, added deleteNameNote action to nameNoteActions --- src/actions/Entities/NameNoteActions.js | 16 ++++++++++++++-- src/database/utilities/createRecord.js | 6 +++++- src/reducers/Entities/NameNoteReducer.js | 1 + 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/actions/Entities/NameNoteActions.js b/src/actions/Entities/NameNoteActions.js index 9514928a6..d3e0678b1 100644 --- a/src/actions/Entities/NameNoteActions.js +++ b/src/actions/Entities/NameNoteActions.js @@ -24,6 +24,7 @@ const createDefaultNameNote = (nameID = '') => { entryDate: new Date(), patientEventID: pcd?.id ?? '', nameID, + isDeleted: false, }; }; @@ -70,7 +71,7 @@ const saveEditing = () => (dispatch, getState) => { }; const updateNameNote = (originalNote, updatedData) => () => { - const { id, patientEvent, name, entryDate, _data } = originalNote; + const { id, patientEvent, name, entryDate, _data, isDeleted } = originalNote; // Quick & dirty check if the object was updated, trims out some un-needed updates const isDirty = _data !== JSON.stringify(updatedData); @@ -82,6 +83,7 @@ const updateNameNote = (originalNote, updatedData) => () => { name, entryDate: new Date(entryDate), _data: JSON.stringify(updatedData), + isDeleted, }; UIDatabase.write(() => { @@ -94,8 +96,14 @@ const updateNameNote = (originalNote, updatedData) => () => { } }; +const deleteNameNote = NameNote => () => { + UIDatabase.write(() => { + UIDatabase.update('NameNote', { id: NameNote.id, isDeleted: true }); + }); +}; + const createNameNoteAudit = (originalNote, updatedData) => { - const { patientEvent, name, entryDate } = originalNote; + const { patientEvent, name, entryDate, isDeleted } = originalNote; const [auditEvent] = UIDatabase.objects('PatientEvent').filtered('code == "NameNoteModified"'); const auditNameNote = { @@ -114,6 +122,7 @@ const createNameNoteAudit = (originalNote, updatedData) => { data: updatedData, }, }), + isDeleted, }; return auditNameNote; @@ -125,6 +134,7 @@ const createNotes = (nameNotes = []) => { const { patientEventID, nameID } = nameNote; const name = UIDatabase.get('Name', nameID); const patientEvent = UIDatabase.get('PatientEvent', patientEventID); + console.log('createNotes(pleeeease): ', nameNote); if (name && patientEvent) { const toSave = { id: nameNote.id, @@ -132,6 +142,7 @@ const createNotes = (nameNotes = []) => { name, _data: JSON.stringify(nameNote?.data), entryDate: new Date(nameNote?.entryDate), + isDeleted: nameNote.isDeleted, }; UIDatabase.update('NameNote', toSave); @@ -152,5 +163,6 @@ export const NameNoteActions = { createSurveyNameNote, updateForm, updateNameNote, + deleteNameNote, saveEditing, }; diff --git a/src/database/utilities/createRecord.js b/src/database/utilities/createRecord.js index f0994f5b9..d35cffa01 100644 --- a/src/database/utilities/createRecord.js +++ b/src/database/utilities/createRecord.js @@ -220,7 +220,10 @@ const getPatientUniqueCode = database => { return isPatientCodeUnique(code, database) ? code : getPatientUniqueCode(id, database); }; -const createNameNote = (database, { id, data, patientEventID, nameID, entryDate = new Date() }) => { +const createNameNote = ( + database, + { id, data, patientEventID, nameID, entryDate = new Date(), isDeleted = false } +) => { const patientEvent = database.get('PatientEvent', patientEventID); const name = database.get('Name', nameID); @@ -230,6 +233,7 @@ const createNameNote = (database, { id, data, patientEventID, nameID, entryDate name, patientEvent, entryDate: new Date(entryDate), + isDeleted, }); newNameNote.data = data; } diff --git a/src/reducers/Entities/NameNoteReducer.js b/src/reducers/Entities/NameNoteReducer.js index 9544d38c3..419245286 100644 --- a/src/reducers/Entities/NameNoteReducer.js +++ b/src/reducers/Entities/NameNoteReducer.js @@ -7,6 +7,7 @@ const initialState = () => ({ patientEventID: '', nameID: '', entryDate: 0, + isDeleted: false, }, isValid: false, }); From fd17755d22dfcdf99b6fa8d39b78f93eed9e0a35 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 15:15:30 +1200 Subject: [PATCH 050/131] isDeleted is populated for the new name note (just in case) --- src/actions/Entities/VaccinePrescriptionActions.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/actions/Entities/VaccinePrescriptionActions.js b/src/actions/Entities/VaccinePrescriptionActions.js index e965d3adf..30d20991d 100644 --- a/src/actions/Entities/VaccinePrescriptionActions.js +++ b/src/actions/Entities/VaccinePrescriptionActions.js @@ -219,6 +219,7 @@ const createVaccinationNameNote = ( patientEvent, entryDate: new Date(), _data: JSON.stringify(data), + isDeleted: false, }; UIDatabase.write(() => UIDatabase.create('NameNote', newNameNote)); }; From 2378b887a0c3a323d8722fb8ba1cdcae35cf617c Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 15:16:00 +1200 Subject: [PATCH 051/131] add delete button to the vaccination event --- src/widgets/modals/VaccinationEvent.js | 43 ++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index dda0d2d73..43ce6307b 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -21,6 +21,7 @@ import globalStyles, { DARKER_GREY, GREY, SUSSOL_ORANGE, + DANGER_RED, } from '../../globalStyles'; import { buttonStrings, generalStrings, modalStrings, vaccineStrings } from '../../localization'; import { PageButton } from '../PageButton'; @@ -52,6 +53,7 @@ export const NoPCDForm = () => ( export const VaccinationEventComponent = ({ editTransaction, + deleteVaccinationEvent, patient, savePCDForm, saveSupplementalData, @@ -75,6 +77,7 @@ export const VaccinationEventComponent = ({ const [isEditingTransaction, toggleEditTransaction] = useToggle(false); const [isModalOpen, toggleModal] = useToggle(false); + const [isDeleteModalOpen, toggleDeleteModal] = useToggle(false); const [vaccinator, setVaccinator] = useState(transactionBatch?.medicineAdministrator); const [vaccine, setVaccine] = useState( vaccines.filter(item => item.id === transactionBatch?.itemId) @@ -134,6 +137,11 @@ export const VaccinationEventComponent = ({ } }, [patient, transactionBatch, vaccine]); + const tryDelete = useCallback(() => { + deleteVaccinationEvent(patient, transactionBatch, vaccinationEventNameNote); + toggleDeleteModal(); + }, [patient]); + return ( <FlexView> <FlexRow flex={1} style={localStyles.formContainer}> @@ -228,6 +236,14 @@ export const VaccinationEventComponent = ({ selectedValue={`${vaccine?.code}: ${vaccine?.name}`} /> </FlexColumn> + <FlexRow flex={1} style={{ marginBottom: 10 }}> + <PageButton + text={buttonStrings.delete_vaccination_event} + onPress={toggleDeleteModal} + style={localStyles.deleteButton} + textStyle={localStyles.saveButtonTextStyle} + /> + </FlexRow> <FlexRow flex={1} style={{ marginBottom: 10 }}> <PageButton style={{ flex: 1, alignSelf: 'flex-end' }} @@ -273,6 +289,15 @@ export const VaccinationEventComponent = ({ onConfirm={toggleModal} /> </PaperModalContainer> + <PaperModalContainer isVisible={isDeleteModalOpen} onClose={toggleDeleteModal}> + <PaperConfirmModal + questionText={modalStrings.delete_vaccination_event} + confirmText={modalStrings.delete} + cancelText={modalStrings.cancel} + onConfirm={tryDelete} + onCancel={toggleDeleteModal} + /> + </PaperModalContainer> </FlexView> ); }; @@ -328,19 +353,24 @@ const mapDispatchToProps = dispatch => { ) => { batch(() => { dispatch(VaccinePrescriptionActions.returnVaccineToStock(patient.id, transactionBatch)); + dispatch(VaccinePrescriptionActions.softDelete(vaccinationEventNameNote)); dispatch(NameActions.select(patient)); dispatch(VaccinePrescriptionActions.selectVaccinator(vaccinator)); dispatch(VaccinePrescriptionActions.selectVaccine(UIDatabase.get('Item', vaccine.id))); dispatch(VaccinePrescriptionActions.selectSupplementalData(supplementalData)); dispatch(VaccinePrescriptionActions.confirm()); + dispatch(NameNoteActions.deleteNameNote(vaccinationEventNameNote)); }); + }; - UIDatabase.write(() => { - UIDatabase.update('NameNote', { id: vaccinationEventNameNote.id, isDeleted: true }); + const deleteVaccinationEvent = (patient, transactionBatch, vaccinationEventNameNote) => { + batch(() => { + dispatch(VaccinePrescriptionActions.returnVaccineToStock(patient.id, transactionBatch)); + dispatch(NameNoteActions.deleteNameNote(vaccinationEventNameNote)); }); }; - return { editTransaction, savePCDForm, saveSupplementalData }; + return { editTransaction, savePCDForm, saveSupplementalData, deleteVaccinationEvent }; }; const localStyles = StyleSheet.create({ @@ -369,6 +399,12 @@ const localStyles = StyleSheet.create({ fontFamily: APP_FONT_FAMILY, color: DARKER_GREY, }, + deleteButton: { + ...globalStyles.button, + flex: 1, + backgroundColor: DANGER_RED, + alignSelf: 'center', + }, }); VaccinationEventComponent.defaultProps = { @@ -378,6 +414,7 @@ VaccinationEventComponent.defaultProps = { VaccinationEventComponent.propTypes = { editTransaction: PropTypes.func.isRequired, + deleteVaccinationEvent: PropTypes.func.isRequired, patient: PropTypes.object, savePCDForm: PropTypes.func.isRequired, saveSupplementalData: PropTypes.func.isRequired, From 169d9342d032982976224b0046321d1f62d2b96c Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 16:03:39 +1200 Subject: [PATCH 052/131] oops wrong old code --- src/widgets/modals/VaccinationEvent.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 43ce6307b..5aa4e7fc5 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -353,7 +353,6 @@ const mapDispatchToProps = dispatch => { ) => { batch(() => { dispatch(VaccinePrescriptionActions.returnVaccineToStock(patient.id, transactionBatch)); - dispatch(VaccinePrescriptionActions.softDelete(vaccinationEventNameNote)); dispatch(NameActions.select(patient)); dispatch(VaccinePrescriptionActions.selectVaccinator(vaccinator)); dispatch(VaccinePrescriptionActions.selectVaccine(UIDatabase.get('Item', vaccine.id))); From 7755008f1e2cbc083983a09f078f6a3ca333f00c Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 16:16:50 +1200 Subject: [PATCH 053/131] Disable the form after the data was edited --- src/widgets/modals/VaccinationEvent.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 5aa4e7fc5..d9adc039d 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -93,6 +93,10 @@ export const VaccinationEventComponent = ({ isSupplementalDataValid: false, }); + const [{ isDeletedVaccinationEvent }, setIsDeletedVaccinationEvent] = useState({ + isDeletedVaccinationEvent: vaccinationEventNameNote.isDeleted, + }); + // User cannot edit 'Vaccination Event' panel if vaccination was done on a different tablet/store const tryEdit = useCallback(() => { if (!transaction) { @@ -132,6 +136,10 @@ export const VaccinationEventComponent = ({ customDataObject, vaccinationEventNameNote ); + toggleEditTransaction(); + setIsDeletedVaccinationEvent({ + isDeletedVaccinationEvent: true, + }); } else { ToastAndroid.show(vaccineStrings.vaccination_not_updated, ToastAndroid.LONG); } @@ -140,7 +148,12 @@ export const VaccinationEventComponent = ({ const tryDelete = useCallback(() => { deleteVaccinationEvent(patient, transactionBatch, vaccinationEventNameNote); toggleDeleteModal(); + setIsDeletedVaccinationEvent({ + isDeletedVaccinationEvent: true, + }); }, [patient]); + console.log('isDeletedVaccinationEvent: ', isDeletedVaccinationEvent); + console.log('isPCDValid: ', isSupplementalDataValid); return ( <FlexView> @@ -160,6 +173,7 @@ export const VaccinationEventComponent = ({ isPCDValid: validator(changed.formData), }); }} + disabled={isDeletedVaccinationEvent} > <></> </JSONForm> @@ -190,6 +204,7 @@ export const VaccinationEventComponent = ({ isSupplementalDataValid: validator(changed.formData), }); }} + disabled={isDeletedVaccinationEvent} > <></> </JSONForm> @@ -264,7 +279,7 @@ export const VaccinationEventComponent = ({ onPress={() => savePCDForm(surveyForm, updatedPcdForm)} style={localStyles.saveButton} textStyle={localStyles.saveButtonTextStyle} - isDisabled={!isPCDValid} + isDisabled={!isPCDValid || isDeletedVaccinationEvent} /> <PageButton text={buttonStrings.save_changes} @@ -273,13 +288,14 @@ export const VaccinationEventComponent = ({ } style={localStyles.saveButton} textStyle={localStyles.saveButtonTextStyle} - isDisabled={!isSupplementalDataValid} + isDisabled={!isSupplementalDataValid || isDeletedVaccinationEvent} /> <PageButton text={isEditingTransaction ? buttonStrings.save_changes : buttonStrings.edit} style={localStyles.saveButton} textStyle={localStyles.saveButtonTextStyle} onPress={isEditingTransaction ? trySave : tryEdit} + isDisabled={isDeletedVaccinationEvent} /> </FlexRow> <PaperModalContainer isVisible={isModalOpen} onClose={toggleModal}> From 72b8c5b373528609baca79da1b79e68c84a92bf9 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 16:17:20 +1200 Subject: [PATCH 054/131] removed debugging code --- src/widgets/modals/VaccinationEvent.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index d9adc039d..739ddf3bd 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -152,8 +152,6 @@ export const VaccinationEventComponent = ({ isDeletedVaccinationEvent: true, }); }, [patient]); - console.log('isDeletedVaccinationEvent: ', isDeletedVaccinationEvent); - console.log('isPCDValid: ', isSupplementalDataValid); return ( <FlexView> From 533c4a00498c1e72d6e88cbfd0f81b16acfd7797 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 13 Jul 2022 16:42:06 +1200 Subject: [PATCH 055/131] toggle edit Transaction after deletion as well --- src/widgets/modals/VaccinationEvent.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 739ddf3bd..0bba28379 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -148,6 +148,7 @@ export const VaccinationEventComponent = ({ const tryDelete = useCallback(() => { deleteVaccinationEvent(patient, transactionBatch, vaccinationEventNameNote); toggleDeleteModal(); + toggleEditTransaction(); setIsDeletedVaccinationEvent({ isDeletedVaccinationEvent: true, }); From 029e14f1b750800a28593d87af16ccb1a9c29310 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Thu, 14 Jul 2022 08:46:28 +1200 Subject: [PATCH 056/131] removing debug code --- src/actions/Entities/NameNoteActions.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/actions/Entities/NameNoteActions.js b/src/actions/Entities/NameNoteActions.js index d3e0678b1..a4f03d020 100644 --- a/src/actions/Entities/NameNoteActions.js +++ b/src/actions/Entities/NameNoteActions.js @@ -134,7 +134,6 @@ const createNotes = (nameNotes = []) => { const { patientEventID, nameID } = nameNote; const name = UIDatabase.get('Name', nameID); const patientEvent = UIDatabase.get('PatientEvent', patientEventID); - console.log('createNotes(pleeeease): ', nameNote); if (name && patientEvent) { const toSave = { id: nameNote.id, From c2433a514b0613811f7b13b0d773c59bf82fbabd Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Mon, 18 Jul 2022 11:54:13 +1200 Subject: [PATCH 057/131] show store name if set --- src/localization/modalStrings.json | 1 + src/widgets/modals/VaccinationEvent.js | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/localization/modalStrings.json b/src/localization/modalStrings.json index 1bf64320f..5796868d5 100644 --- a/src/localization/modalStrings.json +++ b/src/localization/modalStrings.json @@ -8,6 +8,7 @@ "confirm": "Confirm", "confirm_double_dose": "This patient already has a vaccination recorded within the last 24 hours, do you want to proceed?", "vaccine_event_not_editable": "Vaccinator and stock details can only be modified on the device it was originally entered on. Please find the tablet used to enter the vaccination.", + "vaccine_event_not_editable_store": "Vaccinator and stock details can only be modified on {0} store. Please find the tablet used to enter the vaccination.", "create": "Create", "delete_these_fridges": "Are you sure you want to delete these fridges?", "create_cash_transaction": "Create new cash transaction", diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 0bba28379..67f711a31 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -70,6 +70,10 @@ export const VaccinationEventComponent = ({ const vaccinationEventNameNote = UIDatabase.get('NameNote', vaccinationEventId); const vaccinationEvent = vaccinationEventNameNote.data; const transaction = UIDatabase.get('Transaction', vaccinationEvent?.extra?.prescription?.id); + const nameNoteStoreName = vaccinationEvent?.storeName; + const alertText = nameNoteStoreName + ? modalStrings.formatString(modalStrings.vaccine_event_not_editable_store, nameNoteStoreName) + : modalStrings.vaccine_event_not_editable; const transactionBatch = UIDatabase.objects('TransactionBatch').filtered( 'transaction.id == $0', transaction?.id @@ -299,7 +303,7 @@ export const VaccinationEventComponent = ({ </FlexRow> <PaperModalContainer isVisible={isModalOpen} onClose={toggleModal}> <PaperConfirmModal - questionText={modalStrings.vaccine_event_not_editable} + questionText={alertText} confirmText={modalStrings.confirm} onConfirm={toggleModal} /> From 465d1c18f237f7d47be1b2db985e9730581403d4 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 19 Jul 2022 11:10:48 +1200 Subject: [PATCH 058/131] fixed bug --- src/widgets/Tabs/PatientSelect.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/Tabs/PatientSelect.js b/src/widgets/Tabs/PatientSelect.js index bfcd54f3d..f0b0c8db1 100644 --- a/src/widgets/Tabs/PatientSelect.js +++ b/src/widgets/Tabs/PatientSelect.js @@ -256,7 +256,7 @@ const PatientSelectComponent = ({ rowKey={keyExtractor(item)} columns={columns} onPress={name => { - if (!name?.isDeceased) { + if (name?.isDeceased) { toggleIsDeceasedAlert(); return; } From 696a59cb991696b7d63d24e6085218841ef3c8dd Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 19 Jul 2022 15:40:50 +1200 Subject: [PATCH 059/131] added isDeleted field to lookupApiUtils for NameNote --- src/sync/lookupApiUtils.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sync/lookupApiUtils.js b/src/sync/lookupApiUtils.js index afbd983dc..1bfb9d70a 100644 --- a/src/sync/lookupApiUtils.js +++ b/src/sync/lookupApiUtils.js @@ -152,12 +152,14 @@ const processNameNoteResponse = response => patient_event_ID: patientEventID, name_ID: nameID, entry_date: entryDate, + is_deleted: isDeleted, }) => ({ id, data, patientEventID, nameID, entryDate: moment(entryDate).isValid() ? moment(entryDate).toDate() : moment().toDate(), + isDeleted, }) ); From ab33ca6c490a3c3a312e8a0b336801c35965dd79 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 19 Jul 2022 16:28:19 +1200 Subject: [PATCH 060/131] Update patient history on click --- src/widgets/Tabs/PatientSelect.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/Tabs/PatientSelect.js b/src/widgets/Tabs/PatientSelect.js index bfcd54f3d..a4e55ac20 100644 --- a/src/widgets/Tabs/PatientSelect.js +++ b/src/widgets/Tabs/PatientSelect.js @@ -234,7 +234,7 @@ const PatientSelectComponent = ({ switch (colKey) { case 'patientHistory': return patientId => { - const foundPatient = data.find(({ id }) => patientId === id); + const foundPatient = UIDatabase.get('Name', patientId); const patientsPreviousVaccinations = selectVaccinePatientHistory(foundPatient); setPatientHistory({ patient: foundPatient, history: patientsPreviousVaccinations }); From c1be518c0d8ed27f9e29b809f5b3caa927f6f3ce Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 19 Jul 2022 16:35:10 +1200 Subject: [PATCH 061/131] lineHeight bug fixed --- src/globalStyles/buttonStyles.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/globalStyles/buttonStyles.js b/src/globalStyles/buttonStyles.js index 61a4e69ce..f6e7436b4 100644 --- a/src/globalStyles/buttonStyles.js +++ b/src/globalStyles/buttonStyles.js @@ -12,6 +12,7 @@ export const buttonStyles = { fontFamily: APP_FONT_FAMILY, fontSize: 10, color: SUSSOL_ORANGE, + lineHeight: 14, }, disabledButtonText: { color: WHITE, From 636e11bbaebccb2f5d3e02e816a5377b26b5fe6c Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 19 Jul 2022 10:35:08 +0545 Subject: [PATCH 062/131] Enable manual date input in Datepicker --- src/widgets/JSONForm/widgets/DatePicker.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/widgets/JSONForm/widgets/DatePicker.js b/src/widgets/JSONForm/widgets/DatePicker.js index 5b15cd19b..225dd5c65 100644 --- a/src/widgets/JSONForm/widgets/DatePicker.js +++ b/src/widgets/JSONForm/widgets/DatePicker.js @@ -22,7 +22,7 @@ export const DatePicker = ({ const { focusController } = useJSONFormOptions(); const ref = focusController.useRegisteredRef(); - const [selectedDate, setSelectedDate] = useState(null); + const [selectedDate, setSelectedDate] = useState(''); const handleChange = dateString => { onChange(dateString); @@ -35,7 +35,7 @@ export const DatePicker = ({ ? alternateFormatDate : moment(value, DATE_FORMAT.DD_MM_YYYY, true); - setSelectedDate(expectedFormateDate.isValid() ? expectedFormateDate.toDate() : null); + setSelectedDate(expectedFormateDate.isValid() ? expectedFormateDate.toDate() : value); }, [value]); return ( @@ -46,7 +46,11 @@ export const DatePicker = ({ underlineColorAndroid={DARKER_GREY} placeholder={placeholder} editable={!(readonly || disabled)} - value={selectedDate ? moment(selectedDate).format(DATE_FORMAT.DD_MM_YYYY) : ''} + value={ + moment(selectedDate, DATE_FORMAT.DD_MM_YYYY, true).isValid() + ? moment(selectedDate).format(DATE_FORMAT.DD_MM_YYYY) + : selectedDate + } ref={ref} onSubmitEditing={() => focusController.next(ref)} onChangeText={handleChange} From ef2812f2915b1eb5a8202f529aa0775fea928978 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 19 Jul 2022 16:58:20 +1200 Subject: [PATCH 063/131] Bug fixed when wrong vaccine shown on editing --- src/widgets/modals/VaccinationEvent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 67f711a31..4fd5184e9 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -84,7 +84,7 @@ export const VaccinationEventComponent = ({ const [isDeleteModalOpen, toggleDeleteModal] = useToggle(false); const [vaccinator, setVaccinator] = useState(transactionBatch?.medicineAdministrator); const [vaccine, setVaccine] = useState( - vaccines.filter(item => item.id === transactionBatch?.itemId) + vaccines.filter(item => item.id === transactionBatch?.itemId)[0] ); const vaccineDropDownValues = vaccines.map(({ code, name }) => `${code}: ${name}`); From 10cfc793dbb52b680f70943885f77a63dac3299c Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Tue, 19 Jul 2022 17:16:50 +1200 Subject: [PATCH 064/131] Tidied up the icons --- src/widgets/Stepper.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/widgets/Stepper.js b/src/widgets/Stepper.js index a563ab0a4..3da68a439 100644 --- a/src/widgets/Stepper.js +++ b/src/widgets/Stepper.js @@ -132,7 +132,7 @@ const NewStepperNumber = ({ step, numberOfSteps, currentStep, isActive, isLast, const completedStep = currentStep > step; const Container = completedStep ? TouchableOpacity : View; - const backgroundColor = isActive || completedStep ? DARKER_GREY : TRANSPARENT; + const backgroundColor = isActive || completedStep ? WHITE : TRANSPARENT; const borderColor = isActive || completedStep ? DARKER_GREY : MISTY_CHARCOAL; const wrappedOnPress = () => onPress(step); @@ -141,11 +141,7 @@ const NewStepperNumber = ({ step, numberOfSteps, currentStep, isActive, isLast, <Container onPress={wrappedOnPress} style={container}> <Circle size={30} backgroundColor={backgroundColor} borderColor={borderColor}> {isActive || completedStep ? ( - <ConfirmIcon - color={DARKER_GREY} - size={35} - style={{ backgroundColor: WHITE, marginTop: -3 }} - /> + <ConfirmIcon color={DARKER_GREY} size={35} style={{ marginTop: -4, marginLeft: -1 }} /> ) : ( <Text style={stepNumber}>{step + 1}</Text> )} From e9afe9024e4d0b71ca3a5f7d5a759332437e307c Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 19 Jul 2022 11:10:41 +0545 Subject: [PATCH 065/131] Update variable name typo fix --- src/widgets/JSONForm/widgets/DatePicker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/JSONForm/widgets/DatePicker.js b/src/widgets/JSONForm/widgets/DatePicker.js index 225dd5c65..7f760e605 100644 --- a/src/widgets/JSONForm/widgets/DatePicker.js +++ b/src/widgets/JSONForm/widgets/DatePicker.js @@ -31,11 +31,11 @@ export const DatePicker = ({ React.useEffect(() => { const alternateFormatDate = moment(value, 'YYYY-MM-DD', true); - const expectedFormateDate = alternateFormatDate.isValid() + const expectedFormatDate = alternateFormatDate.isValid() ? alternateFormatDate : moment(value, DATE_FORMAT.DD_MM_YYYY, true); - setSelectedDate(expectedFormateDate.isValid() ? expectedFormateDate.toDate() : value); + setSelectedDate(expectedFormatDate.isValid() ? expectedFormatDate.toDate() : value); }, [value]); return ( From a73b3e756a723bf0d9b44dbd72652f7ee1752dda Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 20 Jul 2022 12:13:53 +1200 Subject: [PATCH 066/131] The PCD form and SupplemantalData forms were validated before, so it should be true by default --- src/widgets/modals/VaccinationEvent.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 4fd5184e9..411d2f263 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -90,11 +90,11 @@ export const VaccinationEventComponent = ({ const [{ updatedPcdForm, isPCDValid }, setPCDForm] = useState({ updatedPcdForm: null, - isPCDValid: false, + isPCDValid: true, }); const [{ updatedSupplementalDataForm, isSupplementalDataValid }, setSupplementalData] = useState({ updatedSupplementalDataForm: null, - isSupplementalDataValid: false, + isSupplementalDataValid: true, }); const [{ isDeletedVaccinationEvent }, setIsDeletedVaccinationEvent] = useState({ From 1b24b4c7fafdab13434613adb122ca3ecde5a738 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 20 Jul 2022 14:19:54 +1200 Subject: [PATCH 067/131] bug fixed with deceased patients --- src/widgets/Tabs/PatientSelect.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/widgets/Tabs/PatientSelect.js b/src/widgets/Tabs/PatientSelect.js index 46f0b395f..c8e5346b3 100644 --- a/src/widgets/Tabs/PatientSelect.js +++ b/src/widgets/Tabs/PatientSelect.js @@ -235,6 +235,7 @@ const PatientSelectComponent = ({ case 'patientHistory': return patientId => { const foundPatient = UIDatabase.get('Name', patientId); + console.log(foundPatient); const patientsPreviousVaccinations = selectVaccinePatientHistory(foundPatient); setPatientHistory({ patient: foundPatient, history: patientsPreviousVaccinations }); @@ -256,14 +257,15 @@ const PatientSelectComponent = ({ rowKey={keyExtractor(item)} columns={columns} onPress={name => { - if (name?.isDeceased) { + const selectedPatient = UIDatabase.get('Name', name?.id); + if (selectedPatient.isDeceased) { toggleIsDeceasedAlert(); return; } // Only show a spinner when the name doesn't exist in the database, as we need to // send a request to the server to add a name store join. - if (UIDatabase.get('Name', name?.id)) { + if (selectedPatient) { selectPatient(name); } else { withLoadingIndicator(() => selectPatient(name)); From 4e8921655070452336792b13e10c349e1053e312 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Wed, 20 Jul 2022 18:00:23 +0545 Subject: [PATCH 068/131] Add delete patient record feature - Incomplete yet. Soft delete works but I need to implement it across other ui's queries. --- src/actions/PatientActions.js | 10 ++++++++++ src/database/utilities/createRecord.js | 3 +++ src/localization/generalStrings.json | 1 + src/pages/DispensingPage.js | 2 +- src/widgets/modalChildren/PatientEditModal.js | 18 ++++++++++++++++-- yarn.lock | 13 ++++--------- 6 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/actions/PatientActions.js b/src/actions/PatientActions.js index 5ee27f9c9..26cd8c944 100644 --- a/src/actions/PatientActions.js +++ b/src/actions/PatientActions.js @@ -22,6 +22,7 @@ export const PATIENT_ACTIONS = { SAVE_ADR: 'Patient/saveADR', CANCEL_ADR: 'Patient/cancelADR', REFRESH: 'Patient/refresh', + PATIENT_DELETE: 'Patient/delete', }; const closeModal = () => ({ type: PATIENT_ACTIONS.COMPLETE }); @@ -34,6 +35,14 @@ const makePatientVisibility = async name => { return response; }; +const patientDelete = patientID => { + UIDatabase.write(() => { + UIDatabase.update('Name', { id: patientID, isDeleted: true }); + }); + + return { type: PATIENT_ACTIONS.PATIENT_DELETE }; +}; + const patientUpdate = patientDetails => async (dispatch, getState) => { const { patient } = getState(); const { currentPatient } = patient; @@ -180,6 +189,7 @@ export const PatientActions = { closeADRModal, createPatient, patientUpdate, + patientDelete, editPatient, closeModal, sortPatientHistory, diff --git a/src/database/utilities/createRecord.js b/src/database/utilities/createRecord.js index d35cffa01..827bc6850 100644 --- a/src/database/utilities/createRecord.js +++ b/src/database/utilities/createRecord.js @@ -270,6 +270,7 @@ const createPatient = (database, patientDetails) => { supplyingStoreId: patientSupplyingStoreId, isActive: patientIsActive, isDeceased: patientIsDeceased, + isDeleted: patientIsDeleted, nationality, ethnicity, nameNotes, @@ -298,6 +299,7 @@ const createPatient = (database, patientDetails) => { const country = patientCountry ?? ''; const female = patientFemale ?? true; const isDeceased = patientIsDeceased ?? false; + const isDeleted = patientIsDeleted ?? false; const thisStoreId = database.getSetting(SETTINGS_KEYS.THIS_STORE_ID); const supplyingStoreId = patientSupplyingStoreId || thisStoreId; @@ -332,6 +334,7 @@ const createPatient = (database, patientDetails) => { country, female, isDeceased, + isDeleted, supplyingStoreId, thisStoresPatient, isActive, diff --git a/src/localization/generalStrings.json b/src/localization/generalStrings.json index 588d00d2f..efe88ae35 100644 --- a/src/localization/generalStrings.json +++ b/src/localization/generalStrings.json @@ -21,6 +21,7 @@ "ok": "OK", "ok_and_next": "OK & Next", "remove": "Remove", + "delete": "Delete", "replace": "Replace", "select": "Select", "no": "No", diff --git a/src/pages/DispensingPage.js b/src/pages/DispensingPage.js index 9fa0d89c1..64cc58f73 100644 --- a/src/pages/DispensingPage.js +++ b/src/pages/DispensingPage.js @@ -340,7 +340,6 @@ const localStyles = { const mapStateToProps = state => { const { patient, prescriber, insurance, dispensary } = state; const { sortKey, isAscending, searchTerm, columns } = dispensary; - const isLookupModalOpen = selectLookupModalOpen(state); const { currentPatient } = patient; @@ -356,6 +355,7 @@ const mapStateToProps = state => { ); const insuranceModalOpen = selectInsuranceModalOpen(state); const data = selectSortedData(state); + const patientHistory = patient.currentPatient && patient.currentPatient.transactions ? selectSortedPatientHistory({ patient }) diff --git a/src/widgets/modalChildren/PatientEditModal.js b/src/widgets/modalChildren/PatientEditModal.js index b401104ca..d02f39f88 100644 --- a/src/widgets/modalChildren/PatientEditModal.js +++ b/src/widgets/modalChildren/PatientEditModal.js @@ -17,6 +17,7 @@ import { generalStrings, modalStrings } from '../../localization/index'; export const PatientEditModalComponent = ({ isDisabled, onSaveForm, + onDeleteForm, onCancel, inputConfig, surveySchema, @@ -65,6 +66,12 @@ export const PatientEditModalComponent = ({ textStyle={styles.saveButtonTextStyle} text={generalStrings.save} /> + <PageButton + onPress={onDeleteForm} + style={styles.cancelButton} + textStyle={styles.saveButtonTextStyle} + text={generalStrings.delete} + /> <PageButton onPress={onCancel} style={styles.cancelButton} @@ -116,6 +123,7 @@ PatientEditModalComponent.defaultProps = { PatientEditModalComponent.propTypes = { isDisabled: PropTypes.bool, onSaveForm: PropTypes.func.isRequired, + onDeleteForm: PropTypes.func.isRequired, onCancel: PropTypes.func.isRequired, inputConfig: PropTypes.array.isRequired, surveyForm: PropTypes.object, @@ -127,19 +135,24 @@ PatientEditModalComponent.propTypes = { const mergeProps = (stateProps, dispatchProps, ownProps) => { const { completedForm } = stateProps; - const { onSave, onSaveSurvey, ...otherDispatchProps } = dispatchProps; - const { surveySchema } = ownProps; + const { onSave, onDelete, onSaveSurvey, ...otherDispatchProps } = dispatchProps; + const { surveySchema, patient } = ownProps; const onSaveForm = () => { onSave(completedForm); if (surveySchema) onSaveSurvey(); }; + const onDeleteForm = () => { + onDelete(patient.id); + }; + return { ...ownProps, ...otherDispatchProps, ...stateProps, onSaveForm, + onDeleteForm, }; }; @@ -156,6 +169,7 @@ const dispatchToProps = dispatch => ({ onSaveSurvey: () => dispatch(NameNoteActions.saveEditing()), onUpdateForm: (form, validator) => dispatch(NameNoteActions.updateForm(form, validator)), onSave: patientDetails => dispatch(PatientActions.patientUpdate(patientDetails)), + onDelete: patientID => dispatch(PatientActions.patientDelete(patientID)), }); export const PatientEditModal = connect( diff --git a/yarn.lock b/yarn.lock index 7c03a8624..13b319180 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3338,15 +3338,10 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== -caniuse-lite@^1.0.30001208: - version "1.0.30001211" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001211.tgz#be40d528bb10272eba0037a88adc40054810f8e2" - integrity sha512-v3GXWKofIkN3PkSidLI5d1oqeKNsam9nQkqieoMhP87nxOY0RPDC8X2+jcv8pjV4dRozPLSoMqNii9sDViOlIg== - -caniuse-lite@^1.0.30001219: - version "1.0.30001222" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001222.tgz#2789b8487282cbbe1700924f53951303d28086a9" - integrity sha512-rPmwUK0YMjfMlZVmH6nVB5U3YJ5Wnx3vmT5lnRO3nIKO8bJ+TRWMbGuuiSugDJqESy/lz+1hSrlQEagCtoOAWQ== +caniuse-lite@^1.0.30001208, caniuse-lite@^1.0.30001219: + version "1.0.30001367" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001367.tgz" + integrity sha512-XDgbeOHfifWV3GEES2B8rtsrADx4Jf+juKX2SICJcaUhjYBO3bR96kvEIHa15VU6ohtOhBZuPGGYGbXMRn0NCw== capture-exit@^2.0.0: version "2.0.0" From f5b867c94b0eae5d843472ddeb2065e4a79205b1 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Thu, 21 Jul 2022 09:57:55 +1200 Subject: [PATCH 069/131] remove debug code --- src/widgets/Tabs/PatientSelect.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/widgets/Tabs/PatientSelect.js b/src/widgets/Tabs/PatientSelect.js index c8e5346b3..b1eaf64ed 100644 --- a/src/widgets/Tabs/PatientSelect.js +++ b/src/widgets/Tabs/PatientSelect.js @@ -235,7 +235,6 @@ const PatientSelectComponent = ({ case 'patientHistory': return patientId => { const foundPatient = UIDatabase.get('Name', patientId); - console.log(foundPatient); const patientsPreviousVaccinations = selectVaccinePatientHistory(foundPatient); setPatientHistory({ patient: foundPatient, history: patientsPreviousVaccinations }); From 1c318433ebee278db4adbea6450ea2d98e6140cc Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Thu, 21 Jul 2022 14:24:43 +0545 Subject: [PATCH 070/131] Update query to exclude deleted records --- src/selectors/dispensary.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/selectors/dispensary.js b/src/selectors/dispensary.js index 64962e13e..1d6320017 100644 --- a/src/selectors/dispensary.js +++ b/src/selectors/dispensary.js @@ -44,10 +44,11 @@ export const selectDataSet = ({ dispensary }) => { }; export const selectData = ({ dispensary }) => { - const dataSet = selectDataSet({ dispensary }); - const { data } = dispensary; + const { dataSet, data } = dispensary; - return dataSet === 'patient' ? data.filtered('isDeleted == false') : data; + return [RECORD_TYPES.PATIENT, 'patientWithAdverseDrugReactions'].includes(dataSet) + ? data.filtered('isDeleted == $0', false) + : data; }; export const selectDataSetInUse = ({ dispensary }) => { From 41e2aab3ea4f21c1e7d90882ce97b9df4c171949 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Thu, 21 Jul 2022 17:35:32 +0545 Subject: [PATCH 071/131] Update delete patient logic --- src/actions/PatientActions.js | 9 +++++++-- src/pages/DispensingPage.js | 1 + src/reducers/PatientReducer.js | 1 + src/widgets/modalChildren/PatientEditModal.js | 6 +++--- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/actions/PatientActions.js b/src/actions/PatientActions.js index 26cd8c944..5cd9cb4ae 100644 --- a/src/actions/PatientActions.js +++ b/src/actions/PatientActions.js @@ -5,6 +5,7 @@ import { batch } from 'react-redux'; +import { ToastAndroid } from 'react-native'; import { createRecord, UIDatabase } from '../database'; import { selectCurrentUser } from '../selectors/user'; @@ -35,11 +36,15 @@ const makePatientVisibility = async name => { return response; }; -const patientDelete = patientID => { +const patientDelete = () => (dispatch, getState) => { + const { patient } = getState(); + const { currentPatient } = patient; UIDatabase.write(() => { - UIDatabase.update('Name', { id: patientID, isDeleted: true }); + UIDatabase.update('Name', { id: currentPatient.id, isDeleted: true }); }); + ToastAndroid.show('Patient successfully deleted', ToastAndroid.LONG); + dispatch(closeModal()); return { type: PATIENT_ACTIONS.PATIENT_DELETE }; }; diff --git a/src/pages/DispensingPage.js b/src/pages/DispensingPage.js index 64cc58f73..333b2f4da 100644 --- a/src/pages/DispensingPage.js +++ b/src/pages/DispensingPage.js @@ -32,6 +32,7 @@ import { selectSortedData, selectLookupModalOpen, } from '../selectors/dispensary'; + import { selectPrescriberModalOpen, selectCanEditPrescriber } from '../selectors/prescriber'; import { selectInsuranceModalOpen, selectCanEditInsurancePolicy } from '../selectors/insurance'; import { diff --git a/src/reducers/PatientReducer.js b/src/reducers/PatientReducer.js index d89467683..dddfa7116 100644 --- a/src/reducers/PatientReducer.js +++ b/src/reducers/PatientReducer.js @@ -56,6 +56,7 @@ export const PatientReducer = (state = patientInitialState(), action) => { }; } + case PATIENT_ACTIONS.PATIENT_DELETE: case PATIENT_ACTIONS.COMPLETE: { const { currentPatient } = state; return { ...patientInitialState(), currentPatient }; diff --git a/src/widgets/modalChildren/PatientEditModal.js b/src/widgets/modalChildren/PatientEditModal.js index d02f39f88..75df2d965 100644 --- a/src/widgets/modalChildren/PatientEditModal.js +++ b/src/widgets/modalChildren/PatientEditModal.js @@ -136,7 +136,7 @@ PatientEditModalComponent.propTypes = { const mergeProps = (stateProps, dispatchProps, ownProps) => { const { completedForm } = stateProps; const { onSave, onDelete, onSaveSurvey, ...otherDispatchProps } = dispatchProps; - const { surveySchema, patient } = ownProps; + const { surveySchema } = ownProps; const onSaveForm = () => { onSave(completedForm); @@ -144,7 +144,7 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => { }; const onDeleteForm = () => { - onDelete(patient.id); + onDelete(); }; return { @@ -169,7 +169,7 @@ const dispatchToProps = dispatch => ({ onSaveSurvey: () => dispatch(NameNoteActions.saveEditing()), onUpdateForm: (form, validator) => dispatch(NameNoteActions.updateForm(form, validator)), onSave: patientDetails => dispatch(PatientActions.patientUpdate(patientDetails)), - onDelete: patientID => dispatch(PatientActions.patientDelete(patientID)), + onDelete: () => dispatch(PatientActions.patientDelete()), }); export const PatientEditModal = connect( From a6ada7be3b5d611d4aeef3b1f12cb1ddf723df7c Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Thu, 21 Jul 2022 18:12:26 +0545 Subject: [PATCH 072/131] Add logics to disable delete button if the patient has active vaccine events --- src/actions/PatientActions.js | 3 ++- src/localization/dispensingStrings.json | 3 ++- src/widgets/modalChildren/PatientEditModal.js | 15 ++++++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/actions/PatientActions.js b/src/actions/PatientActions.js index 5cd9cb4ae..398dae28a 100644 --- a/src/actions/PatientActions.js +++ b/src/actions/PatientActions.js @@ -11,6 +11,7 @@ import { selectCurrentUser } from '../selectors/user'; import { createPatientVisibility } from '../sync/lookupApiUtils'; import { DispensaryActions } from './DispensaryActions'; +import { dispensingStrings } from '../localization'; export const PATIENT_ACTIONS = { PATIENT_EDIT: 'Patient/patientEdit', @@ -43,7 +44,7 @@ const patientDelete = () => (dispatch, getState) => { UIDatabase.update('Name', { id: currentPatient.id, isDeleted: true }); }); - ToastAndroid.show('Patient successfully deleted', ToastAndroid.LONG); + ToastAndroid.show(dispensingStrings.patient_deleted, ToastAndroid.LONG); dispatch(closeModal()); return { type: PATIENT_ACTIONS.PATIENT_DELETE }; }; diff --git a/src/localization/dispensingStrings.json b/src/localization/dispensingStrings.json index df0b12ed0..613369d07 100644 --- a/src/localization/dispensingStrings.json +++ b/src/localization/dispensingStrings.json @@ -71,7 +71,8 @@ "edit_the_patient": "Edit patient details", "edit_supplemental_data": "Edit additional details", "refused_vaccine": "Refused vaccine", - "validation_failed": "Some required fields are not completed." + "validation_failed": "Some required fields are not completed.", + "patient_deleted": "Patient successfully deleted" }, "fr": { "vaccination_history": "Historique de vaccination pour", diff --git a/src/widgets/modalChildren/PatientEditModal.js b/src/widgets/modalChildren/PatientEditModal.js index 75df2d965..20de5bf2c 100644 --- a/src/widgets/modalChildren/PatientEditModal.js +++ b/src/widgets/modalChildren/PatientEditModal.js @@ -9,6 +9,7 @@ import { FlexRow } from '../FlexRow'; import { JSONForm } from '../JSONForm/JSONForm'; import { NameNoteActions } from '../../actions/Entities/NameNoteActions'; import { selectNameNoteIsValid, selectCreatingNameNote } from '../../selectors/Entities/nameNote'; +import { selectSortedPatientHistory } from '../../selectors/patient'; import { selectCompletedForm, selectCanSaveForm } from '../../selectors/form'; import { PatientActions } from '../../actions/PatientActions'; import globalStyles, { SUSSOL_ORANGE } from '../../globalStyles'; @@ -25,8 +26,10 @@ export const PatientEditModalComponent = ({ onUpdateForm, nameNoteIsValid, canSaveForm, + hasVaccineEventsForm, }) => { let canSave = canSaveForm; + const hasVaccineEvents = hasVaccineEventsForm; if (canSave) { canSave = surveySchema && surveyForm ? nameNoteIsValid : !isDisabled; } @@ -69,6 +72,7 @@ export const PatientEditModalComponent = ({ <PageButton onPress={onDeleteForm} style={styles.cancelButton} + isDisabled={hasVaccineEvents} textStyle={styles.saveButtonTextStyle} text={generalStrings.delete} /> @@ -131,6 +135,7 @@ PatientEditModalComponent.propTypes = { surveySchema: PropTypes.object, onUpdateForm: PropTypes.func.isRequired, canSaveForm: PropTypes.bool.isRequired, + hasVaccineEventsForm: PropTypes.bool.isRequired, }; const mergeProps = (stateProps, dispatchProps, ownProps) => { @@ -161,8 +166,16 @@ const stateToProps = state => { const nameNote = selectCreatingNameNote(state); const completedForm = selectCompletedForm(state); const canSaveForm = selectCanSaveForm(state); + const patientHistory = selectSortedPatientHistory(state); + const hasVaccineEventsForm = patientHistory.length > 0; - return { canSaveForm, completedForm, nameNoteIsValid, surveyForm: nameNote?.data ?? null }; + return { + canSaveForm, + hasVaccineEventsForm, + completedForm, + nameNoteIsValid, + surveyForm: nameNote?.data ?? null, + }; }; const dispatchToProps = dispatch => ({ From f841dd36bdd8928ebc2457e901edb4f7e97371d3 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Thu, 21 Jul 2022 18:19:36 +0545 Subject: [PATCH 073/131] Update fix --- src/reducers/PatientReducer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reducers/PatientReducer.js b/src/reducers/PatientReducer.js index dddfa7116..4552f1312 100644 --- a/src/reducers/PatientReducer.js +++ b/src/reducers/PatientReducer.js @@ -56,7 +56,6 @@ export const PatientReducer = (state = patientInitialState(), action) => { }; } - case PATIENT_ACTIONS.PATIENT_DELETE: case PATIENT_ACTIONS.COMPLETE: { const { currentPatient } = state; return { ...patientInitialState(), currentPatient }; @@ -108,6 +107,7 @@ export const PatientReducer = (state = patientInitialState(), action) => { return { ...state, currentPatient: null, creatingADR: false }; } + case PATIENT_ACTIONS.PATIENT_DELETE: case PATIENT_ACTIONS.REFRESH: { return { ...patientInitialState() }; } From d08f3adf226e07af88fdcd470c8d8e0ad397fd71 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Thu, 21 Jul 2022 18:27:54 +0545 Subject: [PATCH 074/131] Add are you sure confimation to delete patient --- src/localization/modalStrings.json | 1 + src/widgets/modalChildren/PatientEditModal.js | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/localization/modalStrings.json b/src/localization/modalStrings.json index b308a2d43..a262ce48d 100644 --- a/src/localization/modalStrings.json +++ b/src/localization/modalStrings.json @@ -2,6 +2,7 @@ "gb": { "adr_form_for": "ADR Form for:", "are_you_sure_delete_sensor": "Are you sure you want to delete this sensor?", + "are_you_sure_delete_patient": "Are you sure you want to delete this patient?", "add_at_least_one_item_before_finalising": "You need to add at least one item before finalising", "and": "and", "cancel": "Cancel", diff --git a/src/widgets/modalChildren/PatientEditModal.js b/src/widgets/modalChildren/PatientEditModal.js index 20de5bf2c..ace0e2436 100644 --- a/src/widgets/modalChildren/PatientEditModal.js +++ b/src/widgets/modalChildren/PatientEditModal.js @@ -13,7 +13,10 @@ import { selectSortedPatientHistory } from '../../selectors/patient'; import { selectCompletedForm, selectCanSaveForm } from '../../selectors/form'; import { PatientActions } from '../../actions/PatientActions'; import globalStyles, { SUSSOL_ORANGE } from '../../globalStyles'; -import { generalStrings, modalStrings } from '../../localization/index'; +import { generalStrings, modalStrings, navStrings } from '../../localization/index'; +import { PaperModalContainer } from '../PaperModal/PaperModalContainer'; +import { PaperConfirmModal } from '../PaperModal/PaperConfirmModal'; +import { useToggle } from '../../hooks/index'; export const PatientEditModalComponent = ({ isDisabled, @@ -34,6 +37,8 @@ export const PatientEditModalComponent = ({ canSave = surveySchema && surveyForm ? nameNoteIsValid : !isDisabled; } + const [removeModalOpen, toggleRemoveModal] = useToggle(); + return ( <FlexRow style={{ flexDirection: 'column' }} flex={1}> <FlexRow flex={1}> @@ -70,7 +75,7 @@ export const PatientEditModalComponent = ({ text={generalStrings.save} /> <PageButton - onPress={onDeleteForm} + onPress={toggleRemoveModal} style={styles.cancelButton} isDisabled={hasVaccineEvents} textStyle={styles.saveButtonTextStyle} @@ -84,6 +89,15 @@ export const PatientEditModalComponent = ({ /> </View> </FlexRow> + <PaperModalContainer isVisible={removeModalOpen} onClose={toggleRemoveModal}> + <PaperConfirmModal + questionText={modalStrings.are_you_sure_delete_patient} + confirmText={generalStrings.remove} + cancelText={navStrings.go_back} + onConfirm={onDeleteForm} + onCancel={toggleRemoveModal} + /> + </PaperModalContainer> </FlexRow> ); }; From b705574820a28b14557517bea7b5c917620ee4fa Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Fri, 22 Jul 2022 12:14:03 +0545 Subject: [PATCH 075/131] Add cancel as button text instead of Go back --- src/widgets/modalChildren/PatientEditModal.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/modalChildren/PatientEditModal.js b/src/widgets/modalChildren/PatientEditModal.js index ace0e2436..ac32beb9a 100644 --- a/src/widgets/modalChildren/PatientEditModal.js +++ b/src/widgets/modalChildren/PatientEditModal.js @@ -13,7 +13,7 @@ import { selectSortedPatientHistory } from '../../selectors/patient'; import { selectCompletedForm, selectCanSaveForm } from '../../selectors/form'; import { PatientActions } from '../../actions/PatientActions'; import globalStyles, { SUSSOL_ORANGE } from '../../globalStyles'; -import { generalStrings, modalStrings, navStrings } from '../../localization/index'; +import { generalStrings, modalStrings, buttonStrings } from '../../localization/index'; import { PaperModalContainer } from '../PaperModal/PaperModalContainer'; import { PaperConfirmModal } from '../PaperModal/PaperConfirmModal'; import { useToggle } from '../../hooks/index'; @@ -93,7 +93,7 @@ export const PatientEditModalComponent = ({ <PaperConfirmModal questionText={modalStrings.are_you_sure_delete_patient} confirmText={generalStrings.remove} - cancelText={navStrings.go_back} + cancelText={buttonStrings.cancel} onConfirm={onDeleteForm} onCancel={toggleRemoveModal} /> From 983e6006e7359a693d190312ab2f8a4e7777bf3e Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Fri, 22 Jul 2022 16:05:04 +0545 Subject: [PATCH 076/131] Please make sure that the vaccine events has been deleted if you want to delete this patient modal --- src/localization/modalStrings.json | 1 + src/widgets/modalChildren/PatientEditModal.js | 13 ++++++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/localization/modalStrings.json b/src/localization/modalStrings.json index a262ce48d..b3d208db1 100644 --- a/src/localization/modalStrings.json +++ b/src/localization/modalStrings.json @@ -3,6 +3,7 @@ "adr_form_for": "ADR Form for:", "are_you_sure_delete_sensor": "Are you sure you want to delete this sensor?", "are_you_sure_delete_patient": "Are you sure you want to delete this patient?", + "patient_cant_delete_with_vaccine_events": "You can only delete records of patients who do not have any active vaccine events. Please make sure that the vaccine events has been deleted if you want to delete this patient.", "add_at_least_one_item_before_finalising": "You need to add at least one item before finalising", "and": "and", "cancel": "Cancel", diff --git a/src/widgets/modalChildren/PatientEditModal.js b/src/widgets/modalChildren/PatientEditModal.js index ac32beb9a..d29fd6e19 100644 --- a/src/widgets/modalChildren/PatientEditModal.js +++ b/src/widgets/modalChildren/PatientEditModal.js @@ -13,7 +13,7 @@ import { selectSortedPatientHistory } from '../../selectors/patient'; import { selectCompletedForm, selectCanSaveForm } from '../../selectors/form'; import { PatientActions } from '../../actions/PatientActions'; import globalStyles, { SUSSOL_ORANGE } from '../../globalStyles'; -import { generalStrings, modalStrings, buttonStrings } from '../../localization/index'; +import { generalStrings, modalStrings, buttonStrings, navStrings } from '../../localization/index'; import { PaperModalContainer } from '../PaperModal/PaperModalContainer'; import { PaperConfirmModal } from '../PaperModal/PaperConfirmModal'; import { useToggle } from '../../hooks/index'; @@ -38,6 +38,7 @@ export const PatientEditModalComponent = ({ } const [removeModalOpen, toggleRemoveModal] = useToggle(); + const [cantDeleteModalOpen, toggleCantDeleteModal] = useToggle(); return ( <FlexRow style={{ flexDirection: 'column' }} flex={1}> @@ -75,9 +76,8 @@ export const PatientEditModalComponent = ({ text={generalStrings.save} /> <PageButton - onPress={toggleRemoveModal} + onPress={hasVaccineEvents ? toggleCantDeleteModal : toggleRemoveModal} style={styles.cancelButton} - isDisabled={hasVaccineEvents} textStyle={styles.saveButtonTextStyle} text={generalStrings.delete} /> @@ -89,6 +89,13 @@ export const PatientEditModalComponent = ({ /> </View> </FlexRow> + <PaperModalContainer isVisible={cantDeleteModalOpen} onClose={toggleCantDeleteModal}> + <PaperConfirmModal + questionText={modalStrings.patient_cant_delete_with_vaccine_events} + confirmText={navStrings.go_back} + onConfirm={toggleCantDeleteModal} + /> + </PaperModalContainer> <PaperModalContainer isVisible={removeModalOpen} onClose={toggleRemoveModal}> <PaperConfirmModal questionText={modalStrings.are_you_sure_delete_patient} From b0d9bf68f3d55e8c0f5ebd795f1144b54e8853c7 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Fri, 22 Jul 2022 16:35:57 +0545 Subject: [PATCH 077/131] Update datepicker default value --- src/widgets/JSONForm/widgets/DatePicker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/JSONForm/widgets/DatePicker.js b/src/widgets/JSONForm/widgets/DatePicker.js index 7f760e605..ad948d235 100644 --- a/src/widgets/JSONForm/widgets/DatePicker.js +++ b/src/widgets/JSONForm/widgets/DatePicker.js @@ -22,7 +22,7 @@ export const DatePicker = ({ const { focusController } = useJSONFormOptions(); const ref = focusController.useRegisteredRef(); - const [selectedDate, setSelectedDate] = useState(''); + const [selectedDate, setSelectedDate] = useState(Date()); const handleChange = dateString => { onChange(dateString); From bc81d43f26b6bb98cf990fadf23163e95c034eed Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Mon, 25 Jul 2022 12:41:46 +0545 Subject: [PATCH 078/131] Rename cannot delete modal state variables --- src/widgets/modalChildren/PatientEditModal.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/widgets/modalChildren/PatientEditModal.js b/src/widgets/modalChildren/PatientEditModal.js index d29fd6e19..14d22b66b 100644 --- a/src/widgets/modalChildren/PatientEditModal.js +++ b/src/widgets/modalChildren/PatientEditModal.js @@ -38,7 +38,7 @@ export const PatientEditModalComponent = ({ } const [removeModalOpen, toggleRemoveModal] = useToggle(); - const [cantDeleteModalOpen, toggleCantDeleteModal] = useToggle(); + const [cannotDeleteModalOpen, toggleCannotDeleteModal] = useToggle(); return ( <FlexRow style={{ flexDirection: 'column' }} flex={1}> @@ -76,7 +76,7 @@ export const PatientEditModalComponent = ({ text={generalStrings.save} /> <PageButton - onPress={hasVaccineEvents ? toggleCantDeleteModal : toggleRemoveModal} + onPress={hasVaccineEvents ? toggleCannotDeleteModal : toggleRemoveModal} style={styles.cancelButton} textStyle={styles.saveButtonTextStyle} text={generalStrings.delete} @@ -89,11 +89,11 @@ export const PatientEditModalComponent = ({ /> </View> </FlexRow> - <PaperModalContainer isVisible={cantDeleteModalOpen} onClose={toggleCantDeleteModal}> + <PaperModalContainer isVisible={cannotDeleteModalOpen} onClose={toggleCannotDeleteModal}> <PaperConfirmModal questionText={modalStrings.patient_cant_delete_with_vaccine_events} confirmText={navStrings.go_back} - onConfirm={toggleCantDeleteModal} + onConfirm={toggleCannotDeleteModal} /> </PaperModalContainer> <PaperModalContainer isVisible={removeModalOpen} onClose={toggleRemoveModal}> From 6bd1c7abc38332311ab33e0cf64f398cca7f6e1b Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Mon, 25 Jul 2022 13:55:29 +0545 Subject: [PATCH 079/131] Disable deletion of patient from other stores --- src/widgets/modalChildren/PatientEditModal.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/widgets/modalChildren/PatientEditModal.js b/src/widgets/modalChildren/PatientEditModal.js index 14d22b66b..bd09ae980 100644 --- a/src/widgets/modalChildren/PatientEditModal.js +++ b/src/widgets/modalChildren/PatientEditModal.js @@ -37,6 +37,8 @@ export const PatientEditModalComponent = ({ canSave = surveySchema && surveyForm ? nameNoteIsValid : !isDisabled; } + const canDelete = !isDisabled; + const [removeModalOpen, toggleRemoveModal] = useToggle(); const [cannotDeleteModalOpen, toggleCannotDeleteModal] = useToggle(); @@ -77,6 +79,7 @@ export const PatientEditModalComponent = ({ /> <PageButton onPress={hasVaccineEvents ? toggleCannotDeleteModal : toggleRemoveModal} + isDisabled={!canDelete} style={styles.cancelButton} textStyle={styles.saveButtonTextStyle} text={generalStrings.delete} From 6f61b10664391d1051efdddc4f11617ec51c8661 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 26 Jul 2022 10:53:42 +0545 Subject: [PATCH 080/131] Clean up --- src/pages/DispensingPage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/DispensingPage.js b/src/pages/DispensingPage.js index 333b2f4da..9fa0d89c1 100644 --- a/src/pages/DispensingPage.js +++ b/src/pages/DispensingPage.js @@ -32,7 +32,6 @@ import { selectSortedData, selectLookupModalOpen, } from '../selectors/dispensary'; - import { selectPrescriberModalOpen, selectCanEditPrescriber } from '../selectors/prescriber'; import { selectInsuranceModalOpen, selectCanEditInsurancePolicy } from '../selectors/insurance'; import { @@ -341,6 +340,7 @@ const localStyles = { const mapStateToProps = state => { const { patient, prescriber, insurance, dispensary } = state; const { sortKey, isAscending, searchTerm, columns } = dispensary; + const isLookupModalOpen = selectLookupModalOpen(state); const { currentPatient } = patient; @@ -356,7 +356,6 @@ const mapStateToProps = state => { ); const insuranceModalOpen = selectInsuranceModalOpen(state); const data = selectSortedData(state); - const patientHistory = patient.currentPatient && patient.currentPatient.transactions ? selectSortedPatientHistory({ patient }) From 0b6b4bbc228b3852b7aecf77416108f6a2ed239f Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 26 Jul 2022 12:00:16 +0545 Subject: [PATCH 081/131] If there is no transactions return empty array --- src/selectors/patient.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/selectors/patient.js b/src/selectors/patient.js index 2740cdeb5..56c768787 100644 --- a/src/selectors/patient.js +++ b/src/selectors/patient.js @@ -16,6 +16,9 @@ export const selectPatientHistory = ({ patient }) => { const { currentPatient } = patient; const { transactions } = currentPatient; + // If there is no transactions return empty array + if (!transactions) return []; + // Create a query string `transaction.id == "{id} OR transaction.id == "{id}" ...` // finding all transaction batches for the patient. const inQuery = transactions.map(({ id }) => `transaction.id == "${id}"`).join(' OR '); From 8e32b74cf4e58314cdfba6b2ef942ebfc06e48df Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Wed, 27 Jul 2022 10:48:23 +0545 Subject: [PATCH 082/131] Do not show delete button in New patient form --- src/selectors/patient.js | 10 ++++++++ src/widgets/modalChildren/PatientEditModal.js | 24 ++++++++++++------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/selectors/patient.js b/src/selectors/patient.js index 56c768787..8bea9eb0c 100644 --- a/src/selectors/patient.js +++ b/src/selectors/patient.js @@ -64,6 +64,16 @@ export const selectPatientModalOpen = ({ patient }) => { return [isCreating || isEditing, viewingHistory, creatingADR]; }; +/** + * Check if it is create Patient form. + * @param { patient } patient + * @returns boolean + */ +export const selectIsCreatePatient = ({ patient }) => { + const { isCreating } = patient; + return isCreating; +}; + export const selectCanEditPatient = ({ patient }) => { const { currentPatient } = patient; const { isEditable = true } = currentPatient ?? {}; diff --git a/src/widgets/modalChildren/PatientEditModal.js b/src/widgets/modalChildren/PatientEditModal.js index bd09ae980..f37fb8cef 100644 --- a/src/widgets/modalChildren/PatientEditModal.js +++ b/src/widgets/modalChildren/PatientEditModal.js @@ -9,7 +9,7 @@ import { FlexRow } from '../FlexRow'; import { JSONForm } from '../JSONForm/JSONForm'; import { NameNoteActions } from '../../actions/Entities/NameNoteActions'; import { selectNameNoteIsValid, selectCreatingNameNote } from '../../selectors/Entities/nameNote'; -import { selectSortedPatientHistory } from '../../selectors/patient'; +import { selectSortedPatientHistory, selectIsCreatePatient } from '../../selectors/patient'; import { selectCompletedForm, selectCanSaveForm } from '../../selectors/form'; import { PatientActions } from '../../actions/PatientActions'; import globalStyles, { SUSSOL_ORANGE } from '../../globalStyles'; @@ -30,6 +30,7 @@ export const PatientEditModalComponent = ({ nameNoteIsValid, canSaveForm, hasVaccineEventsForm, + isCreatePatient, }) => { let canSave = canSaveForm; const hasVaccineEvents = hasVaccineEventsForm; @@ -38,6 +39,7 @@ export const PatientEditModalComponent = ({ } const canDelete = !isDisabled; + const showDelete = !isCreatePatient; const [removeModalOpen, toggleRemoveModal] = useToggle(); const [cannotDeleteModalOpen, toggleCannotDeleteModal] = useToggle(); @@ -77,13 +79,14 @@ export const PatientEditModalComponent = ({ textStyle={styles.saveButtonTextStyle} text={generalStrings.save} /> - <PageButton - onPress={hasVaccineEvents ? toggleCannotDeleteModal : toggleRemoveModal} - isDisabled={!canDelete} - style={styles.cancelButton} - textStyle={styles.saveButtonTextStyle} - text={generalStrings.delete} - /> + {showDelete && ( + <PageButton + onPress={hasVaccineEvents ? toggleCannotDeleteModal : toggleRemoveModal} + style={styles.cancelButton} + textStyle={styles.saveButtonTextStyle} + text={generalStrings.delete} + /> + )} <PageButton onPress={onCancel} style={styles.cancelButton} @@ -105,6 +108,7 @@ export const PatientEditModalComponent = ({ confirmText={generalStrings.remove} cancelText={buttonStrings.cancel} onConfirm={onDeleteForm} + isDisabled={!canDelete} onCancel={toggleRemoveModal} /> </PaperModalContainer> @@ -146,6 +150,7 @@ PatientEditModalComponent.defaultProps = { surveyForm: null, surveySchema: null, nameNoteIsValid: true, + isCreatePatient: false, }; PatientEditModalComponent.propTypes = { @@ -160,6 +165,7 @@ PatientEditModalComponent.propTypes = { onUpdateForm: PropTypes.func.isRequired, canSaveForm: PropTypes.bool.isRequired, hasVaccineEventsForm: PropTypes.bool.isRequired, + isCreatePatient: PropTypes.bool, }; const mergeProps = (stateProps, dispatchProps, ownProps) => { @@ -191,6 +197,7 @@ const stateToProps = state => { const completedForm = selectCompletedForm(state); const canSaveForm = selectCanSaveForm(state); const patientHistory = selectSortedPatientHistory(state); + const isCreatePatient = selectIsCreatePatient(state); const hasVaccineEventsForm = patientHistory.length > 0; return { @@ -198,6 +205,7 @@ const stateToProps = state => { hasVaccineEventsForm, completedForm, nameNoteIsValid, + isCreatePatient, surveyForm: nameNote?.data ?? null, }; }; From 369dcae58aae31dc6b940857b5df573931c6f77a Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 2 Aug 2022 16:40:34 +0545 Subject: [PATCH 083/131] Fix PatientSelectComponent bugs out when closed --- src/widgets/FormControl.js | 7 +++++-- src/widgets/JSONForm/widgets/Checkbox.js | 6 +++++- src/widgets/JSONForm/widgets/DatePicker.js | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/widgets/FormControl.js b/src/widgets/FormControl.js index bf44d6103..a972427bf 100644 --- a/src/widgets/FormControl.js +++ b/src/widgets/FormControl.js @@ -80,8 +80,11 @@ const FormControlComponent = ({ const isFocused = useIsFocused(); React.useEffect(() => { - onInitialiseForm(); - setRefs({ length: inputConfig.length }); + // Only update state if component comes in focus + if (isFocused) { + onInitialiseForm(); + setRefs({ length: inputConfig.length }); + } }, [isFocused]); const debouncedUpdateForm = useDebounce(onUpdateForm, 500); diff --git a/src/widgets/JSONForm/widgets/Checkbox.js b/src/widgets/JSONForm/widgets/Checkbox.js index a7e56a45d..7ded98eba 100644 --- a/src/widgets/JSONForm/widgets/Checkbox.js +++ b/src/widgets/JSONForm/widgets/Checkbox.js @@ -82,9 +82,13 @@ const styles = StyleSheet.create({ }, }); +Checkbox.defaultProps = { + value: false, +}; + Checkbox.propTypes = { options: PropTypes.object.isRequired, - value: PropTypes.bool.isRequired, + value: PropTypes.bool, onChange: PropTypes.func.isRequired, onBlur: PropTypes.func.isRequired, id: PropTypes.string.isRequired, diff --git a/src/widgets/JSONForm/widgets/DatePicker.js b/src/widgets/JSONForm/widgets/DatePicker.js index ad948d235..7f760e605 100644 --- a/src/widgets/JSONForm/widgets/DatePicker.js +++ b/src/widgets/JSONForm/widgets/DatePicker.js @@ -22,7 +22,7 @@ export const DatePicker = ({ const { focusController } = useJSONFormOptions(); const ref = focusController.useRegisteredRef(); - const [selectedDate, setSelectedDate] = useState(Date()); + const [selectedDate, setSelectedDate] = useState(''); const handleChange = dateString => { onChange(dateString); From 3e6236db5612c79c11ed26a47f69894e2d0a9a89 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 3 Aug 2022 11:02:47 +1200 Subject: [PATCH 084/131] filter vaccines so it doesn't show vaccines out of stock --- src/widgets/modals/VaccinationEvent.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 411d2f263..c9f19d0cd 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -86,7 +86,9 @@ export const VaccinationEventComponent = ({ const [vaccine, setVaccine] = useState( vaccines.filter(item => item.id === transactionBatch?.itemId)[0] ); - const vaccineDropDownValues = vaccines.map(({ code, name }) => `${code}: ${name}`); + const vaccineDropDownValues = vaccines + .filter(v => v.totalQuantity !== 0) + .map(({ code, name }) => `${code}: ${name}`); const [{ updatedPcdForm, isPCDValid }, setPCDForm] = useState({ updatedPcdForm: null, From f8703af9506e04176db042ed8bdf6dce189d4af4 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Wed, 3 Aug 2022 17:53:35 +0545 Subject: [PATCH 085/131] Fix Save and delete button was still showing for remote patients --- src/widgets/modalChildren/PatientEditModal.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/widgets/modalChildren/PatientEditModal.js b/src/widgets/modalChildren/PatientEditModal.js index f37fb8cef..be04f98ba 100644 --- a/src/widgets/modalChildren/PatientEditModal.js +++ b/src/widgets/modalChildren/PatientEditModal.js @@ -35,7 +35,7 @@ export const PatientEditModalComponent = ({ let canSave = canSaveForm; const hasVaccineEvents = hasVaccineEventsForm; if (canSave) { - canSave = surveySchema && surveyForm ? nameNoteIsValid : !isDisabled; + canSave = !isDisabled && surveySchema && surveyForm && nameNoteIsValid; } const canDelete = !isDisabled; @@ -84,6 +84,7 @@ export const PatientEditModalComponent = ({ onPress={hasVaccineEvents ? toggleCannotDeleteModal : toggleRemoveModal} style={styles.cancelButton} textStyle={styles.saveButtonTextStyle} + isDisabled={!canDelete} text={generalStrings.delete} /> )} From 52f52537d9f319d99b6a8eb8648f378b2bd65052 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Thu, 4 Aug 2022 14:54:33 +1200 Subject: [PATCH 086/131] disable button after clicking --- src/widgets/modals/VaccinationEvent.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index c9f19d0cd..1b1933b9f 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -129,6 +129,9 @@ export const VaccinationEventComponent = ({ }; const trySave = useCallback(() => { + setIsDeletedVaccinationEvent({ + isDeletedVaccinationEvent: true, + }); const vaccineChanged = vaccine.code !== transactionBatch?.itemBatch?.item?.code; const vaccinatorChanged = JSON.stringify(vaccinator) !== JSON.stringify(transactionBatch.medicineAdministrator); @@ -143,10 +146,10 @@ export const VaccinationEventComponent = ({ vaccinationEventNameNote ); toggleEditTransaction(); + } else { setIsDeletedVaccinationEvent({ - isDeletedVaccinationEvent: true, + isDeletedVaccinationEvent: false, }); - } else { ToastAndroid.show(vaccineStrings.vaccination_not_updated, ToastAndroid.LONG); } }, [patient, transactionBatch, vaccine]); From 358b3630b5c4dd744b676fe7232b40ec794c00fd Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Mon, 8 Aug 2022 12:13:00 +0545 Subject: [PATCH 087/131] Disabled next button during submission enable it afterwards --- src/widgets/Tabs/PatientEdit.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/widgets/Tabs/PatientEdit.js b/src/widgets/Tabs/PatientEdit.js index 745289590..212dd1f0d 100644 --- a/src/widgets/Tabs/PatientEdit.js +++ b/src/widgets/Tabs/PatientEdit.js @@ -4,9 +4,10 @@ * Sustainable Solutions (NZ) Ltd. 2021 */ -import React, { useCallback, useRef } from 'react'; +import React, { useCallback, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import { ToastAndroid, View } from 'react-native'; +import { useIsFocused } from '@react-navigation/native'; import { connect } from 'react-redux'; import * as Animatable from 'react-native-animatable'; import { FormControl } from '../FormControl'; @@ -65,20 +66,38 @@ const PatientEditComponent = ({ }) => { const { pageTopViewContainer } = globalStyles; const [isDeceasedModalOpen, toggleIsDeceasedAlert] = useToggle(false); + const [nextButtonEnabled, setNextButtonEnabled] = useState(true); + + const isFocused = useIsFocused(); + + React.useEffect(() => { + // Enable next button when component goes out of focus + // This combined with the way nextButtonEnabled is being used below, would ensure + // and Next button cannot be clicked multiple times + if (!isFocused) { + setNextButtonEnabled(true); + } + }, [isFocused]); + const formRef = useRef(null); const savePatient = useCallback( e => { + setNextButtonEnabled(false); updatePatientDetails(completedForm); + if (completedForm.isDeceased) { toggleIsDeceasedAlert(); + setNextButtonEnabled(true); return; } formRef?.current?.submit(e); + if (canSaveForm) { onCompleted(); } else { ToastAndroid.show(dispensingStrings.validation_failed, ToastAndroid.LONG); + setNextButtonEnabled(true); } }, [completedForm, canSaveForm] @@ -127,6 +146,7 @@ const PatientEditComponent = ({ <PageButtonWithOnePress text={buttonStrings.cancel} onPress={onCancelPrescription} /> <PageButton text={buttonStrings.next} + isDisabled={!nextButtonEnabled} onPress={savePatient} style={{ marginLeft: 'auto' }} /> From f4be3f3b3cc93aa9956eb7d11c32c9d821ca26aa Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Mon, 8 Aug 2022 14:30:45 +0545 Subject: [PATCH 088/131] Make sure all steps of vaccine event creation flow would not trigger by mistake by multiple clicks --- src/hooks/useButtonEnabled.js | 15 ++++ src/widgets/Tabs/PatientEdit.js | 18 +---- src/widgets/Tabs/VaccineSelect.js | 19 ++++- src/widgets/Tabs/VaccineSupplementalData.js | 90 ++++++++++++--------- 4 files changed, 85 insertions(+), 57 deletions(-) create mode 100644 src/hooks/useButtonEnabled.js diff --git a/src/hooks/useButtonEnabled.js b/src/hooks/useButtonEnabled.js new file mode 100644 index 000000000..b3c23b15b --- /dev/null +++ b/src/hooks/useButtonEnabled.js @@ -0,0 +1,15 @@ +import { useIsFocused } from '@react-navigation/native'; +import React, { useState } from 'react'; + +export default function useButtonEnabled(defaultValue = true) { + const [enabled, setEnabled] = useState(defaultValue); + const isFocused = useIsFocused(); + + React.useEffect(() => { + if (!isFocused) { + setEnabled(true); + } + }, [isFocused]); + + return { enabled, setEnabled }; +} diff --git a/src/widgets/Tabs/PatientEdit.js b/src/widgets/Tabs/PatientEdit.js index 212dd1f0d..898925722 100644 --- a/src/widgets/Tabs/PatientEdit.js +++ b/src/widgets/Tabs/PatientEdit.js @@ -4,12 +4,12 @@ * Sustainable Solutions (NZ) Ltd. 2021 */ -import React, { useCallback, useRef, useState } from 'react'; +import React, { useCallback, useRef } from 'react'; import PropTypes from 'prop-types'; import { ToastAndroid, View } from 'react-native'; -import { useIsFocused } from '@react-navigation/native'; import { connect } from 'react-redux'; import * as Animatable from 'react-native-animatable'; +import useButtonEnabled from '../../hooks/useButtonEnabled'; import { FormControl } from '../FormControl'; import { PageButton } from '../PageButton'; import { FlexRow } from '../FlexRow'; @@ -66,18 +66,8 @@ const PatientEditComponent = ({ }) => { const { pageTopViewContainer } = globalStyles; const [isDeceasedModalOpen, toggleIsDeceasedAlert] = useToggle(false); - const [nextButtonEnabled, setNextButtonEnabled] = useState(true); - - const isFocused = useIsFocused(); - - React.useEffect(() => { - // Enable next button when component goes out of focus - // This combined with the way nextButtonEnabled is being used below, would ensure - // and Next button cannot be clicked multiple times - if (!isFocused) { - setNextButtonEnabled(true); - } - }, [isFocused]); + + const { enabled: nextButtonEnabled, setEnabled: setNextButtonEnabled } = useButtonEnabled(); const formRef = useRef(null); const savePatient = useCallback( diff --git a/src/widgets/Tabs/VaccineSelect.js b/src/widgets/Tabs/VaccineSelect.js index 481c4a93a..4e5c85964 100644 --- a/src/widgets/Tabs/VaccineSelect.js +++ b/src/widgets/Tabs/VaccineSelect.js @@ -4,7 +4,7 @@ * Sustainable Solutions (NZ) Ltd. 2021 */ -import React from 'react'; +import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; import { useNavigation } from '@react-navigation/native'; import { Dimensions, Text, StyleSheet, TextInput, View } from 'react-native'; @@ -46,6 +46,7 @@ import { PaperModalContainer } from '../PaperModal/PaperModalContainer'; import { PaperConfirmModal } from '../PaperModal/PaperConfirmModal'; import { useToggle } from '../../hooks/useToggle'; import { PageButton } from '../PageButton'; +import useButtonEnabled from '../../hooks/useButtonEnabled'; const ListEmptyComponent = () => ( <FlexView flex={1} justifyContent="center" alignItems="center"> @@ -85,6 +86,10 @@ const VaccineSelectComponent = ({ wasPatientVaccinatedWithinOneDay, }) => { const { pageTopViewContainer } = globalStyles; + const { + enabled: okConfirmButtonEnabled, + setEnabled: setOkConfirmButtonEnabled, + } = useButtonEnabled(false); const [confirmDoubleDoseModalOpen, toggleConfirmDoubleDoseModal] = useToggle(); const [confirmAndRepeatDoubleDoseModalOpen, toggleConfirmAndRepeatDoubleDoseModal] = useToggle(); const vaccineColumns = React.useMemo(() => getColumns(TABS.ITEM), []); @@ -105,6 +110,14 @@ const VaccineSelectComponent = ({ ); const runWithLoadingIndicator = useLoadingIndicator(); + useEffect(() => { + // Wait for two seconds before enabling confirm button + // to avoid accidental form submission. + setTimeout(() => { + setOkConfirmButtonEnabled(true); + }, 2000); + }, []); + const confirmPrescription = React.useCallback(() => runWithLoadingIndicator(onConfirm), [ onConfirm, ]); @@ -200,7 +213,7 @@ const VaccineSelectComponent = ({ debounceTimer={1000} text={buttonStrings.confirm} style={{ marginLeft: 'auto' }} - isDisabled={!selectedBatches && !hasRefused} + isDisabled={(!selectedBatches && !hasRefused) || !okConfirmButtonEnabled} onPress={ wasPatientVaccinatedWithinOneDay ? toggleConfirmDoubleDoseModal : confirmPrescription } @@ -209,7 +222,7 @@ const VaccineSelectComponent = ({ debounceTimer={1000} text={generalStrings.ok_and_next} style={{ marginLeft: 5 }} - isDisabled={!selectedBatches && !hasRefused} + isDisabled={(!selectedBatches && !hasRefused) || !okConfirmButtonEnabled} onPress={ wasPatientVaccinatedWithinOneDay ? toggleConfirmAndRepeatDoubleDoseModal diff --git a/src/widgets/Tabs/VaccineSupplementalData.js b/src/widgets/Tabs/VaccineSupplementalData.js index b8e5110b0..529b3d4ff 100644 --- a/src/widgets/Tabs/VaccineSupplementalData.js +++ b/src/widgets/Tabs/VaccineSupplementalData.js @@ -6,7 +6,6 @@ */ import React from 'react'; - import { ToastAndroid, View } from 'react-native'; import { connect } from 'react-redux'; @@ -14,7 +13,7 @@ import PropTypes from 'prop-types'; import * as Animatable from 'react-native-animatable'; import { FlexRow } from '../FlexRow'; import { FlexView } from '../FlexView'; - +import useButtonEnabled from '../../hooks/useButtonEnabled'; import globalStyles from '../../globalStyles/index'; import { buttonStrings, dispensingStrings, vaccineStrings } from '../../localization/index'; import { JSONForm } from '../JSONForm/JSONForm'; @@ -40,37 +39,54 @@ const VaccineSupplementalDataComponent = ({ onComplete, onFormUpdate, siteSchema, -}) => ( - <FlexView style={pageTopViewContainer}> - <Paper - headerText={vaccineStrings.vaccine_dispense_supplemental_data_title} - contentContainerStyle={{ flex: 1 }} - style={{ flex: 1 }} - > - <AfterInteractions placeholder={null}> - <Animatable.View animation="fadeIn" duration={1000} useNativeDriver style={{ flex: 1 }}> - <JSONForm - formData={formData} - surveySchema={siteSchema} - onChange={(changed, validator) => { - onFormUpdate(changed.formData, validator); - }} - > - <View /> - </JSONForm> - </Animatable.View> - </AfterInteractions> - </Paper> - <FlexRow flex={0} justifyContent="flex-end" alignItems="flex-end"> - <PageButtonWithOnePress text={buttonStrings.cancel} onPress={onCancel} /> - <PageButton - text={buttonStrings.next} - onPress={() => onComplete(isValid)} - style={{ marginLeft: 'auto' }} - /> - </FlexRow> - </FlexView> -); +}) => { + const { enabled: nextButtonEnabled, setEnabled: setNextButtonEnabled } = useButtonEnabled(); + + const submitForm = () => { + setNextButtonEnabled(false); + + if (isValid) { + onComplete(); + return; + } + + ToastAndroid.show(dispensingStrings.validation_failed, ToastAndroid.LONG); + setNextButtonEnabled(true); + }; + + return ( + <FlexView style={pageTopViewContainer}> + <Paper + headerText={vaccineStrings.vaccine_dispense_supplemental_data_title} + contentContainerStyle={{ flex: 1 }} + style={{ flex: 1 }} + > + <AfterInteractions placeholder={null}> + <Animatable.View animation="fadeIn" duration={1000} useNativeDriver style={{ flex: 1 }}> + <JSONForm + formData={formData} + surveySchema={siteSchema} + onChange={(changed, validator) => { + onFormUpdate(changed.formData, validator); + }} + > + <View /> + </JSONForm> + </Animatable.View> + </AfterInteractions> + </Paper> + <FlexRow flex={0} justifyContent="flex-end" alignItems="flex-end"> + <PageButtonWithOnePress text={buttonStrings.cancel} onPress={onCancel} /> + <PageButton + text={buttonStrings.next} + isDisabled={!nextButtonEnabled} + onPress={() => submitForm()} + style={{ marginLeft: 'auto' }} + /> + </FlexRow> + </FlexView> + ); +}; VaccineSupplementalDataComponent.propTypes = { formData: PropTypes.object, @@ -88,13 +104,7 @@ const mapDispatchToProps = dispatch => { dispatch(VaccinePrescriptionActions.updateSupplementalData(data, validator)); }; - const onComplete = isValid => { - if (isValid) { - dispatch(WizardActions.nextTab()); - } else { - ToastAndroid.show(dispensingStrings.validation_failed, ToastAndroid.LONG); - } - }; + const onComplete = () => dispatch(WizardActions.nextTab()); return { onCancel, onComplete, onFormUpdate }; }; From e12c6261d2419870b142b18add604575580955da Mon Sep 17 00:00:00 2001 From: Arjun Sah <arjunsah@sussol.net> Date: Tue, 9 Aug 2022 13:49:04 +0545 Subject: [PATCH 089/131] #4647 added toggle to hide zero stock Added toggle to hide zero stock on Current stock. Creating new reducer and action to handle it --- src/pages/StockPage.js | 50 ++++++++++++++----- .../dataTableUtilities/actions/constants.js | 1 + .../actions/tableActions.js | 6 +++ .../dataTableUtilities/getPageInitialiser.js | 1 + .../reducer/tableReducers.js | 29 ++++++++++- 5 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/pages/StockPage.js b/src/pages/StockPage.js index 02edf0620..a7a5fd028 100644 --- a/src/pages/StockPage.js +++ b/src/pages/StockPage.js @@ -4,22 +4,23 @@ * Sustainable Solutions (NZ) Ltd. 2019 */ -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import PropTypes from 'prop-types'; import { View } from 'react-native'; import { connect } from 'react-redux'; -import { getItemLayout, getPageDispatchers } from './dataTableUtilities'; +import { getItemLayout, getPageDispatchers, PageActions } from './dataTableUtilities'; import { DataTable, DataTableHeaderRow, DataTableRow } from '../widgets/DataTable'; -import { DataTablePageView, SearchBar } from '../widgets'; +import { DataTablePageView, SearchBar, ToggleBar } from '../widgets'; import globalStyles from '../globalStyles'; import { useSyncListener } from '../hooks'; -import { generalStrings } from '../localization'; +import { generalStrings, buttonStrings } from '../localization'; import { SupplierCreditActions } from '../actions/SupplierCreditActions'; import { RowDetailActions } from '../actions/RowDetailActions'; +import { ROUTES } from '../navigation'; /** * Renders a mSupply mobile page with Items and their stock levels. @@ -49,12 +50,17 @@ export const Stock = ({ onFilterData, onSortColumn, refund, + showAll, + toggleStockOut, }) => { // Refresh data on retrieving item or itembatch records from sync. useSyncListener(refreshData, ['Item', 'ItemBatch']); const refundCallback = React.useCallback(() => itemId => refund(itemId), []); + console.log(data.length); + console.log(searchTerm); + const renderRow = useCallback( listItem => { const { item, index } = listItem; @@ -83,17 +89,33 @@ export const Stock = ({ /> ); - const { pageTopSectionContainer } = globalStyles; + const toggles = useMemo( + () => [{ text: buttonStrings.hide_stockouts, onPress: toggleStockOut, isOn: !showAll }], + [showAll] + ); + + console.log(showAll); + + const { + pageTopSectionContainer, + pageTopRightSectionContainer, + pageTopLeftSectionContainer, + } = globalStyles; return ( <DataTablePageView captureUncaughtGestures={false}> <View style={pageTopSectionContainer}> - <SearchBar - onChangeText={onFilterData} - value={searchTerm} - onFocusOrBlur={selectedRow && onDeselectRow} - // eslint-disable-next-line max-len - placeholder={`${generalStrings.search_by} ${generalStrings.code} ${generalStrings.or} ${generalStrings.name}`} - /> + <View style={pageTopLeftSectionContainer}> + <SearchBar + onChangeText={onFilterData} + value={searchTerm} + onFocusOrBlur={selectedRow && onDeselectRow} + // eslint-disable-next-line max-len + placeholder={`${generalStrings.search_by} ${generalStrings.code} ${generalStrings.or} ${generalStrings.name}`} + /> + </View> + <View style={pageTopRightSectionContainer}> + <ToggleBar toggles={toggles} /> + </View> </View> <DataTable @@ -113,6 +135,7 @@ const mapDispatchToProps = dispatch => ({ ...getPageDispatchers(dispatch, '', 'stock'), onSelectRow: rowKey => dispatch(RowDetailActions.openItemDetail(rowKey)), refund: rowKey => dispatch(SupplierCreditActions.createFromItem(rowKey)), + onFilterData: value => dispatch(PageActions.filterDataWithZeroStockToggle(value, ROUTES.STOCK)), }); const mapStateToProps = state => { @@ -126,6 +149,7 @@ export const StockPage = connect(mapStateToProps, mapDispatchToProps)(Stock); Stock.defaultProps = { selectedRow: null, + showAll: true, }; Stock.propTypes = { @@ -143,4 +167,6 @@ Stock.propTypes = { onFilterData: PropTypes.func.isRequired, onSortColumn: PropTypes.func.isRequired, refund: PropTypes.func.isRequired, + showAll: PropTypes.bool, + toggleStockOut: PropTypes.func.isRequired, }; diff --git a/src/pages/dataTableUtilities/actions/constants.js b/src/pages/dataTableUtilities/actions/constants.js index 587551206..b95e7c514 100644 --- a/src/pages/dataTableUtilities/actions/constants.js +++ b/src/pages/dataTableUtilities/actions/constants.js @@ -21,6 +21,7 @@ export const ACTIONS = { FILTER_DATA_WITH_FINALISED_TOGGLE: 'filterDataWithFinalisedToggle', FILTER_DATA_WITH_OVER_STOCK_TOGGLE: 'filterDataWithOverStockToggle', TOGGLE_COLUMN_SET: 'toggleColumnSet', + FILTER_DATA_WITH_ZERO_STOCK_TOGGLE: 'filterDataWithZeroStockToggle', // pageAction constants OPEN_MODAL: 'openModal', diff --git a/src/pages/dataTableUtilities/actions/tableActions.js b/src/pages/dataTableUtilities/actions/tableActions.js index 28d191eea..8475a0297 100644 --- a/src/pages/dataTableUtilities/actions/tableActions.js +++ b/src/pages/dataTableUtilities/actions/tableActions.js @@ -305,6 +305,11 @@ export const filterDataWithOverStockToggle = (searchTerm, route) => ({ payload: { searchTerm, route }, }); +export const filterDataWithZeroStockToggle = (searchTerm, route) => ({ + type: ACTIONS.FILTER_DATA_WITH_ZERO_STOCK_TOGGLE, + payload: { searchTerm, route }, +}); + export const TableActionsLookup = { sortData, filterData, @@ -331,4 +336,5 @@ export const TableActionsLookup = { filterDataWithFinalisedToggle, filterDataWithOverStockToggle, toggleColumnSet, + filterDataWithZeroStockToggle, }; diff --git a/src/pages/dataTableUtilities/getPageInitialiser.js b/src/pages/dataTableUtilities/getPageInitialiser.js index 637de5734..95af1e87a 100644 --- a/src/pages/dataTableUtilities/getPageInitialiser.js +++ b/src/pages/dataTableUtilities/getPageInitialiser.js @@ -185,6 +185,7 @@ const stockInitialiser = () => { filterDataKeys: ['name', 'code'], sortKey: 'name', isAscending: true, + showAll: true, selectedRow: null, route: ROUTES.STOCK, columns: getColumns(ROUTES.STOCK), diff --git a/src/pages/dataTableUtilities/reducer/tableReducers.js b/src/pages/dataTableUtilities/reducer/tableReducers.js index 23ff61eca..d7cbf2929 100644 --- a/src/pages/dataTableUtilities/reducer/tableReducers.js +++ b/src/pages/dataTableUtilities/reducer/tableReducers.js @@ -136,6 +136,33 @@ export const filterDataWithOverStockToggle = (state, action) => { return { ...state, data: sortedData, searchTerm }; }; +/** + * Filters the backingData with REALM - first applying show/hide zero stock filtering + * toggle. Sorting is held stable. + * + */ +export const filterDataWithZeroStockToggle = (state, action) => { + const { backingData, filterDataKeys, sortKey, isAscending, showAll } = state; + const { payload } = action; + const { searchTerm } = payload; + + // Apply query filtering + const queryString = filterDataKeys.map(filterTerm => `${filterTerm} CONTAINS[c] $0`).join(' OR '); + const queryFilteredData = backingData.filtered(queryString, searchTerm.trim()).slice(); + + // Filter by toggle status - showing or not showing zero stocked records. + const stockFilteredData = !showAll + ? queryFilteredData.slice().filter(item => item.hasStock) + : queryFilteredData.slice(); + + // Sort the data by the current sorting parameters. + const sortedData = sortKey + ? sortDataBy(stockFilteredData, sortKey, isAscending) + : stockFilteredData; + + return { ...state, data: sortedData, searchTerm }; +}; + /** * Simply refresh's the data object in state to correctly match the * backingData. Used for when side effects such as finalizing manipulate @@ -232,7 +259,6 @@ export const hideOverStocked = state => { return { ...state, data: newData, showAll: false }; }; - /** * Filters by backingData elements `hasStock` field. */ @@ -285,4 +311,5 @@ export const TableReducerLookup = { filterDataWithFinalisedToggle, filterDataWithOverStockToggle, toggleColumnSet, + filterDataWithZeroStockToggle, }; From ad01b196d3dab63d6ad5d257b5a4587670831b01 Mon Sep 17 00:00:00 2001 From: Arjun Sah <arjunsah@sussol.net> Date: Tue, 9 Aug 2022 14:35:10 +0545 Subject: [PATCH 090/131] added toggle button resources added toggle button resources --- src/localization/buttonStrings.json | 24 ++++++++++++++++-------- src/pages/StockPage.js | 7 +------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/localization/buttonStrings.json b/src/localization/buttonStrings.json index 0e66a1efd..eff342006 100644 --- a/src/localization/buttonStrings.json +++ b/src/localization/buttonStrings.json @@ -38,7 +38,8 @@ "customer_data": "Customer Data", "factory_reset": "Factory Reset", "check_connection": "Check connection", - "connection_status": "Connection successful" + "connection_status": "Connection successful", + "hide_zero_stocks": "Hide zero stocks" }, "fr": { "back": "Précédent", @@ -77,7 +78,8 @@ "customer_data": "Données Clients", "factory_reset": "Réinitialiser", "check_connection": "Vérifiez la connexion", - "connection_status": "Connexion réussie" + "connection_status": "Connexion réussie", + "hide_zero_stocks": "Masquer les stocks zéro" }, "gil": { "add_master_list_items": "Rinea am list", @@ -96,7 +98,8 @@ "new_supplier_invoice": "Bebwan am oota\nman pharmacy", "past": "Are I mwaina", "use_requested_quantities": "Kabongana maitina\nae kainnanoaki", - "use_suggested_quantities": "Kabongana te\nmwaiti are e katauaki" + "use_suggested_quantities": "Kabongana te\nmwaiti are e katauaki", + "hide_zero_stocks": "Hide zero stocks" }, "tl": { "add_master_list_items": "Utiliza Lista Master", @@ -115,7 +118,8 @@ "new_supplier_invoice": "Distribuidor nia Invoice Foun", "past": "Pasadu", "use_suggested_quantities": "Kuantidade Sujere", - "use_requested_quantities": "Kuantidade Ezije" + "use_requested_quantities": "Kuantidade Ezije", + "hide_zero_stocks": "Hide zero stocks" }, "la": { "add_batch": "​ໃສ​່​ເລກ​ຊຸດ​ຜະ​ລິດ", @@ -135,7 +139,8 @@ "new_supplier_invoice": "ໃບ​ຮັບ​ສິນ​ຄ້າໃໝ່", "past": "ໃນທີ່​ຜ່ານ​ມາ", "use_suggested_quantities": "ນຳໃຊ້ຈຳ​ນວນທີ່​ຖືກແນະ​ນຳ", - "use_requested_quantities": "ນຳໃຊ້ຈຳ​ນວນທີ່​ສະ​ເໜີ​ຂໍ" + "use_requested_quantities": "ນຳໃຊ້ຈຳ​ນວນທີ່​ສະ​ເໜີ​ຂໍ", + "hide_zero_stocks": "Hide zero stocks" }, "my": { "add_fridge": "အအေးပေးစက်အတွင်း သိမ်းဆည်းသည်", @@ -163,7 +168,8 @@ "past": "ယခင်", "save_changes": "ပြောင်းလဲမှုအားသိမ်းဆည်းသည်", "use_requested_quantities": "တောင်းခံထားသော အ‌ရေအတွက်ကို သုံးသည်", - "use_suggested_quantities": "အကြံပြုထားသော အရေအတွက်ကို သုံးသည်" + "use_suggested_quantities": "အကြံပြုထားသော အရေအတွက်ကို သုံးသည်", + "hide_zero_stocks": "Hide zero stocks" }, "pt": { "back": "Voltar", @@ -202,7 +208,8 @@ "customer_data": "Dados do cliente", "factory_reset": "Configuração inicial", "check_connection": "Check connection", - "connection_status": "Conexão ativa" + "connection_status": "Conexão ativa", + "hide_zero_stocks": "Hide zero stocks" }, "es": { "next": "Siguiente", @@ -231,6 +238,7 @@ "use_requested_quantities": "Usar Cantidades Solicitadas", "use_suggested_quantities": "Usar Cantidades Sugeridas", "view_regimen_data": "Ver Datos del Régimen", - "check_connection": "Check connection" + "check_connection": "Check connection", + "hide_zero_stocks": "Ocultar existencias cero" } } diff --git a/src/pages/StockPage.js b/src/pages/StockPage.js index a7a5fd028..ae9a24026 100644 --- a/src/pages/StockPage.js +++ b/src/pages/StockPage.js @@ -58,9 +58,6 @@ export const Stock = ({ const refundCallback = React.useCallback(() => itemId => refund(itemId), []); - console.log(data.length); - console.log(searchTerm); - const renderRow = useCallback( listItem => { const { item, index } = listItem; @@ -90,12 +87,10 @@ export const Stock = ({ ); const toggles = useMemo( - () => [{ text: buttonStrings.hide_stockouts, onPress: toggleStockOut, isOn: !showAll }], + () => [{ text: buttonStrings.hide_zero_stocks, onPress: toggleStockOut, isOn: !showAll }], [showAll] ); - console.log(showAll); - const { pageTopSectionContainer, pageTopRightSectionContainer, From a0362dc63a06366e2ffc008f0bcde84762394217 Mon Sep 17 00:00:00 2001 From: Arjun Sah <arjunsah@sussol.net> Date: Tue, 9 Aug 2022 17:18:46 +0545 Subject: [PATCH 091/131] #4847 Fixed chopping of label Done Fixed chopping of label Done --- src/widgets/modalChildren/MultiSelectList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/modalChildren/MultiSelectList.js b/src/widgets/modalChildren/MultiSelectList.js index 7e317d8b3..ab0a787b7 100644 --- a/src/widgets/modalChildren/MultiSelectList.js +++ b/src/widgets/modalChildren/MultiSelectList.js @@ -135,5 +135,5 @@ const localStyles = StyleSheet.create({ buttonContainer: { alignItems: 'flex-end' }, resultList: { marginHorizontal: 5, backgroundColor: 'white' }, confirmButton: { ...globalStyles.button, backgroundColor: SUSSOL_ORANGE, height: 50 }, - confirmButtonText: { color: 'white', fontSize: 20, fontFamily: APP_FONT_FAMILY }, + confirmButtonText: { color: 'white', fontSize: 20, fontFamily: APP_FONT_FAMILY, paddingTop: 5 }, }); From 3fe783b3f6c187eca8c0321427b4e7bac8a137d3 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Mon, 15 Aug 2022 16:12:56 +1200 Subject: [PATCH 092/131] disable confirm button after click --- src/widgets/Tabs/VaccineSelect.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/widgets/Tabs/VaccineSelect.js b/src/widgets/Tabs/VaccineSelect.js index 4e5c85964..94862dce2 100644 --- a/src/widgets/Tabs/VaccineSelect.js +++ b/src/widgets/Tabs/VaccineSelect.js @@ -118,9 +118,10 @@ const VaccineSelectComponent = ({ }, 2000); }, []); - const confirmPrescription = React.useCallback(() => runWithLoadingIndicator(onConfirm), [ - onConfirm, - ]); + const confirmPrescription = React.useCallback(() => { + runWithLoadingIndicator(onConfirm); + setOkConfirmButtonEnabled(false); + }, [onConfirm]); const confirmAndRepeatPrescription = React.useCallback( () => runWithLoadingIndicator(okAndRepeat), From cacbcafa6639294ec7a67e1ed41908760224af00 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Mon, 15 Aug 2022 13:54:26 +0545 Subject: [PATCH 093/131] Fix Deleted patients again shows up --- src/actions/PatientActions.js | 2 +- src/globalStyles/dataTableStyles.js | 7 +++++++ src/hooks/useLocalAndRemotePatients.js | 2 +- src/localization/generalStrings.json | 3 ++- src/reducers/PatientReducer.js | 15 ++++++++++++++- src/sync/lookupApiUtils.js | 12 +++++++++++- src/widgets/SimpleTable.js | 17 +++++++++++++++-- src/widgets/modals/SearchForm.js | 10 ++++++++++ 8 files changed, 61 insertions(+), 7 deletions(-) diff --git a/src/actions/PatientActions.js b/src/actions/PatientActions.js index 398dae28a..c2c67f4ef 100644 --- a/src/actions/PatientActions.js +++ b/src/actions/PatientActions.js @@ -46,7 +46,7 @@ const patientDelete = () => (dispatch, getState) => { ToastAndroid.show(dispensingStrings.patient_deleted, ToastAndroid.LONG); dispatch(closeModal()); - return { type: PATIENT_ACTIONS.PATIENT_DELETE }; + return { type: PATIENT_ACTIONS.PATIENT_DELETE, payload: { patient } }; }; const patientUpdate = patientDetails => async (dispatch, getState) => { diff --git a/src/globalStyles/dataTableStyles.js b/src/globalStyles/dataTableStyles.js index 164bf9a06..00a209d0d 100644 --- a/src/globalStyles/dataTableStyles.js +++ b/src/globalStyles/dataTableStyles.js @@ -188,6 +188,13 @@ export const dataTableStyles = { backgroundColor: 'white', flexDirection: 'row', }, + emptyRow: { + backgroundColor: 'white', + height: 250, + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + }, headerCells: { left: { height: 40, diff --git a/src/hooks/useLocalAndRemotePatients.js b/src/hooks/useLocalAndRemotePatients.js index c4e7503c8..40c826c6a 100644 --- a/src/hooks/useLocalAndRemotePatients.js +++ b/src/hooks/useLocalAndRemotePatients.js @@ -151,7 +151,7 @@ export const useLocalAndRemotePatients = (initialValue = []) => { if (!response || noMore) return; dispatch({ type: 'getting_more_patients' }); - const paramsWithLimits = { ...searchParams, limit, offset }; + const paramsWithLimits = { ...searchParams, limit, offset, isDeleted: false }; // Use RNFetch as the fetch returned from `useFetch` is coupled with it's state in a specific // implementation, which we want to change by appending to the result, rather than replacing. diff --git a/src/localization/generalStrings.json b/src/localization/generalStrings.json index efe88ae35..329303c44 100644 --- a/src/localization/generalStrings.json +++ b/src/localization/generalStrings.json @@ -99,7 +99,8 @@ "no_locations": "You don't have any locations to assign! Contact your administrator.", "no_options": "No options available. Please contact your administrator!", "no_vvm_status": "No vaccine vial monitor statuses available. Please contact your administrator!", - "no_reasons": "No requisition line variance reasons available. Please contact your administrator!" + "no_reasons": "No requisition line variance reasons available. Please contact your administrator!", + "no_records": "No record(s) found." }, "fr": { "oh_no": "Oh non!", diff --git a/src/reducers/PatientReducer.js b/src/reducers/PatientReducer.js index 4552f1312..0f4820097 100644 --- a/src/reducers/PatientReducer.js +++ b/src/reducers/PatientReducer.js @@ -107,7 +107,20 @@ export const PatientReducer = (state = patientInitialState(), action) => { return { ...state, currentPatient: null, creatingADR: false }; } - case PATIENT_ACTIONS.PATIENT_DELETE: + case PATIENT_ACTIONS.PATIENT_DELETE: { + const { payload } = action; + const { patient } = payload; + + return { + ...state, + isADRModalOpen: false, + currentPatient: patient, + creatingADR: false, + viewingHistory: false, + isEditing: false, + isCreating: false, + }; + } case PATIENT_ACTIONS.REFRESH: { return { ...patientInitialState() }; } diff --git a/src/sync/lookupApiUtils.js b/src/sync/lookupApiUtils.js index 1bfb9d70a..c6d94e86f 100644 --- a/src/sync/lookupApiUtils.js +++ b/src/sync/lookupApiUtils.js @@ -34,6 +34,7 @@ const TYPES = { STRING: 'string', DATE: 'date', NUMBER: 'number', + BOOLEAN: 'boolean', }; const PARAMETERS = { @@ -45,6 +46,7 @@ const PARAMETERS = { registrationCode: { key: 'code', type: TYPES.STRING }, limit: { key: 'limit', type: TYPES.NUMBER }, offset: { key: 'offset', type: TYPES.NUMBER }, + isDeleted: { key: 'is_deleted', type: TYPES.BOOLEAN }, }; class BugsnagError extends Error { @@ -83,12 +85,15 @@ export const createPolicyRecord = policy => { const getQueryString = params => { const query = params.reduce((queryObject, param) => { const [[key, value], [, type]] = Object.entries(param); - if (!value) return queryObject; + + // We still want to allow false to pass through + if (value === '' || value === null || value === undefined) return queryObject; const formatter = { [TYPES.STRING]: string => `@${string}@`, [TYPES.DATE]: date => moment(date).format('DDMMYYYY'), [TYPES.NUMBER]: number => Number(number), + [TYPES.BOOLEAN]: boolean => Boolean(boolean), }; return { ...queryObject, [key]: formatter[type](value) }; @@ -111,6 +116,7 @@ const getPatientQueryString = ({ barcode = '', dateOfBirth = '', policyNumber = '', + isDeleted = false, limit = null, offset = null, } = {}) => { @@ -118,11 +124,15 @@ const getPatientQueryString = ({ { [PARAMETERS.firstName.key]: firstName, type: PARAMETERS.firstName.type }, { [PARAMETERS.lastName.key]: lastName, type: PARAMETERS.lastName.type }, { [PARAMETERS.barcode.key]: barcode, type: PARAMETERS.barcode.type }, + { [PARAMETERS.isDeleted.key]: isDeleted, type: PARAMETERS.isDeleted.type }, { [PARAMETERS.dateOfBirth.key]: dateOfBirth, type: PARAMETERS.dateOfBirth.type }, { [PARAMETERS.policyNumber.key]: policyNumber, type: PARAMETERS.policyNumber.type }, { [PARAMETERS.offset.key]: offset, type: PARAMETERS.offset.type }, { [PARAMETERS.limit.key]: limit, type: PARAMETERS.limit.type }, ]; + + console.log('queryParams', queryParams); + return getQueryString(queryParams); }; diff --git a/src/widgets/SimpleTable.js b/src/widgets/SimpleTable.js index f6a14c9e9..bb376c2cd 100644 --- a/src/widgets/SimpleTable.js +++ b/src/widgets/SimpleTable.js @@ -3,7 +3,7 @@ import React, { useCallback } from 'react'; import PropTypes from 'prop-types'; import * as Animatable from 'react-native-animatable'; -import { FlatList, TouchableOpacity } from 'react-native'; +import { FlatList, TouchableOpacity, View, Text } from 'react-native'; import Cell from './DataTable/Cell'; import { dataTableStyles, GREY } from '../globalStyles/index'; import { HeaderCell, HeaderRow, TouchableNoFeedback } from './DataTable/index'; @@ -38,8 +38,10 @@ export const SimpleTable = React.memo( alternateRow: alternateRowStyle, row: basicRowStyle, headerRow, + emptyRow, headerCells, } = dataTableStyles; + const renderCells = useCallback( rowData => { const { id } = rowData; @@ -91,6 +93,15 @@ export const SimpleTable = React.memo( [selectedRows, disabledRows, selectRow, isDisabled] ); + const renderEmpty = useCallback( + () => ( + <View style={emptyRow}> + <Text style={{ textAlign: 'center' }}>{generalStrings.no_records}</Text> + </View> + ), + [] + ); + const renderHeaderCells = useCallback( () => columns.map(({ title, width, alignText }, index) => ( @@ -120,6 +131,7 @@ export const SimpleTable = React.memo( renderItem={renderRow} stickyHeaderIndices={[0]} ListHeaderComponent={renderHeaders} + ListEmptyComponent={renderEmpty} extraData={selectedRows} style={style} {...flatListProps} @@ -135,10 +147,11 @@ SimpleTable.defaultProps = { selectRow: null, isDisabled: false, style: null, + data: null, }; SimpleTable.propTypes = { - data: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired, + data: PropTypes.oneOfType([PropTypes.array, PropTypes.object]), columns: PropTypes.array.isRequired, selectRow: PropTypes.func, selectedRows: PropTypes.object, diff --git a/src/widgets/modals/SearchForm.js b/src/widgets/modals/SearchForm.js index b851e7481..8ef55ba86 100644 --- a/src/widgets/modals/SearchForm.js +++ b/src/widgets/modals/SearchForm.js @@ -169,6 +169,16 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => ({ close: () => dispatch(DispensaryActions.closeLookupModal()), selectPatient: async patient => { + const localPatient = UIDatabase.get('Name', patient.id); + + if (localPatient.isDeleted) { + ToastAndroid.show( + 'The patient has been deleted. You cannot sync this patient back.', + ToastAndroid.LONG + ); + return; + } + const result = await PatientActions.makePatientVisibility(patient); if (result) { From 019fae01f34e43fd895ccfe1bc70143fa38d5d90 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Mon, 15 Aug 2022 14:06:49 +0545 Subject: [PATCH 094/131] Code cleanup --- src/localization/dispensingStrings.json | 3 ++- src/sync/lookupApiUtils.js | 2 -- src/widgets/modals/SearchForm.js | 7 ++----- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/localization/dispensingStrings.json b/src/localization/dispensingStrings.json index 613369d07..7ab08c61f 100644 --- a/src/localization/dispensingStrings.json +++ b/src/localization/dispensingStrings.json @@ -72,7 +72,8 @@ "edit_supplemental_data": "Edit additional details", "refused_vaccine": "Refused vaccine", "validation_failed": "Some required fields are not completed.", - "patient_deleted": "Patient successfully deleted" + "patient_deleted": "Patient successfully deleted", + "patient_already_deleted": "'The patient has already been deleted. You cannot sync this patient back." }, "fr": { "vaccination_history": "Historique de vaccination pour", diff --git a/src/sync/lookupApiUtils.js b/src/sync/lookupApiUtils.js index c6d94e86f..cb2e20a1f 100644 --- a/src/sync/lookupApiUtils.js +++ b/src/sync/lookupApiUtils.js @@ -131,8 +131,6 @@ const getPatientQueryString = ({ { [PARAMETERS.limit.key]: limit, type: PARAMETERS.limit.type }, ]; - console.log('queryParams', queryParams); - return getQueryString(queryParams); }; diff --git a/src/widgets/modals/SearchForm.js b/src/widgets/modals/SearchForm.js index 8ef55ba86..884e03ad1 100644 --- a/src/widgets/modals/SearchForm.js +++ b/src/widgets/modals/SearchForm.js @@ -28,7 +28,7 @@ import { PrescriberActions } from '../../actions/PrescriberActions'; import { getColumns } from '../../pages/dataTableUtilities'; import { SimpleTable } from '../SimpleTable'; -import { modalStrings, generalStrings } from '../../localization'; +import { modalStrings, generalStrings, dispensingStrings } from '../../localization'; import { APP_FONT_FAMILY, DARK_GREY, ROW_BLUE, WHITE, SUSSOL_ORANGE } from '../../globalStyles'; @@ -172,10 +172,7 @@ const mapDispatchToProps = dispatch => ({ const localPatient = UIDatabase.get('Name', patient.id); if (localPatient.isDeleted) { - ToastAndroid.show( - 'The patient has been deleted. You cannot sync this patient back.', - ToastAndroid.LONG - ); + ToastAndroid.show(dispensingStrings.patient_already_deleted, ToastAndroid.LONG); return; } From b338d928942382112c2fb8e411397ed5cd45ed15 Mon Sep 17 00:00:00 2001 From: Arjun Sah <arjunsah@sussol.net> Date: Mon, 15 Aug 2022 14:36:51 +0545 Subject: [PATCH 095/131] button resources improved --- src/localization/buttonStrings.json | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/localization/buttonStrings.json b/src/localization/buttonStrings.json index eff342006..eda4d0c3c 100644 --- a/src/localization/buttonStrings.json +++ b/src/localization/buttonStrings.json @@ -78,8 +78,7 @@ "customer_data": "Données Clients", "factory_reset": "Réinitialiser", "check_connection": "Vérifiez la connexion", - "connection_status": "Connexion réussie", - "hide_zero_stocks": "Masquer les stocks zéro" + "connection_status": "Connexion réussie" }, "gil": { "add_master_list_items": "Rinea am list", @@ -98,8 +97,7 @@ "new_supplier_invoice": "Bebwan am oota\nman pharmacy", "past": "Are I mwaina", "use_requested_quantities": "Kabongana maitina\nae kainnanoaki", - "use_suggested_quantities": "Kabongana te\nmwaiti are e katauaki", - "hide_zero_stocks": "Hide zero stocks" + "use_suggested_quantities": "Kabongana te\nmwaiti are e katauaki" }, "tl": { "add_master_list_items": "Utiliza Lista Master", @@ -118,8 +116,7 @@ "new_supplier_invoice": "Distribuidor nia Invoice Foun", "past": "Pasadu", "use_suggested_quantities": "Kuantidade Sujere", - "use_requested_quantities": "Kuantidade Ezije", - "hide_zero_stocks": "Hide zero stocks" + "use_requested_quantities": "Kuantidade Ezije" }, "la": { "add_batch": "​ໃສ​່​ເລກ​ຊຸດ​ຜະ​ລິດ", @@ -139,8 +136,7 @@ "new_supplier_invoice": "ໃບ​ຮັບ​ສິນ​ຄ້າໃໝ່", "past": "ໃນທີ່​ຜ່ານ​ມາ", "use_suggested_quantities": "ນຳໃຊ້ຈຳ​ນວນທີ່​ຖືກແນະ​ນຳ", - "use_requested_quantities": "ນຳໃຊ້ຈຳ​ນວນທີ່​ສະ​ເໜີ​ຂໍ", - "hide_zero_stocks": "Hide zero stocks" + "use_requested_quantities": "ນຳໃຊ້ຈຳ​ນວນທີ່​ສະ​ເໜີ​ຂໍ" }, "my": { "add_fridge": "အအေးပေးစက်အတွင်း သိမ်းဆည်းသည်", @@ -168,8 +164,7 @@ "past": "ယခင်", "save_changes": "ပြောင်းလဲမှုအားသိမ်းဆည်းသည်", "use_requested_quantities": "တောင်းခံထားသော အ‌ရေအတွက်ကို သုံးသည်", - "use_suggested_quantities": "အကြံပြုထားသော အရေအတွက်ကို သုံးသည်", - "hide_zero_stocks": "Hide zero stocks" + "use_suggested_quantities": "အကြံပြုထားသော အရေအတွက်ကို သုံးသည်" }, "pt": { "back": "Voltar", @@ -208,8 +203,7 @@ "customer_data": "Dados do cliente", "factory_reset": "Configuração inicial", "check_connection": "Check connection", - "connection_status": "Conexão ativa", - "hide_zero_stocks": "Hide zero stocks" + "connection_status": "Conexão ativa" }, "es": { "next": "Siguiente", @@ -238,7 +232,6 @@ "use_requested_quantities": "Usar Cantidades Solicitadas", "use_suggested_quantities": "Usar Cantidades Sugeridas", "view_regimen_data": "Ver Datos del Régimen", - "check_connection": "Check connection", - "hide_zero_stocks": "Ocultar existencias cero" + "check_connection": "Check connection" } } From d78b9a6170ce0d6d97e1fa687a78ec7bd58ad5e6 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Mon, 15 Aug 2022 17:14:59 +0545 Subject: [PATCH 096/131] Fix lookup patient overriding current changes - Fix lookup patient overriding current changes by ignoring syning patients that already exist in local database --- src/localization/dispensingStrings.json | 5 ++++- src/widgets/modals/SearchForm.js | 13 ++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/localization/dispensingStrings.json b/src/localization/dispensingStrings.json index df0b12ed0..a36b66446 100644 --- a/src/localization/dispensingStrings.json +++ b/src/localization/dispensingStrings.json @@ -71,7 +71,10 @@ "edit_the_patient": "Edit patient details", "edit_supplemental_data": "Edit additional details", "refused_vaccine": "Refused vaccine", - "validation_failed": "Some required fields are not completed." + "validation_failed": "Some required fields are not completed.", + "patient_deleted": "Patient successfully deleted", + "patient_already_exists": "'The patient already exists. Can only sync existing patient from tablet to server.", + "patient_already_deleted": "'The patient has already been deleted. You cannot sync this patient back." }, "fr": { "vaccination_history": "Historique de vaccination pour", diff --git a/src/widgets/modals/SearchForm.js b/src/widgets/modals/SearchForm.js index b851e7481..29be6b2d6 100644 --- a/src/widgets/modals/SearchForm.js +++ b/src/widgets/modals/SearchForm.js @@ -28,7 +28,7 @@ import { PrescriberActions } from '../../actions/PrescriberActions'; import { getColumns } from '../../pages/dataTableUtilities'; import { SimpleTable } from '../SimpleTable'; -import { modalStrings, generalStrings } from '../../localization'; +import { modalStrings, generalStrings, dispensingStrings } from '../../localization'; import { APP_FONT_FAMILY, DARK_GREY, ROW_BLUE, WHITE, SUSSOL_ORANGE } from '../../globalStyles'; @@ -169,6 +169,17 @@ const mapStateToProps = state => { const mapDispatchToProps = dispatch => ({ close: () => dispatch(DispensaryActions.closeLookupModal()), selectPatient: async patient => { + const localPatient = UIDatabase.get('Name', patient.id); + + if (localPatient) { + const toastMessage = localPatient.isDeleted + ? dispensingStrings.patient_already_deleted + : dispensingStrings.patient_already_exists; + + ToastAndroid.show(toastMessage, ToastAndroid.LONG); + return; + } + const result = await PatientActions.makePatientVisibility(patient); if (result) { From 000293286768210f5b4249d34663f0892601aa01 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 16 Aug 2022 09:56:59 +0545 Subject: [PATCH 097/131] Remove typo @chastichno https://github.com/openmsupply/mobile/pull/4881#discussion_r946226424 --- src/localization/dispensingStrings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/localization/dispensingStrings.json b/src/localization/dispensingStrings.json index a36b66446..6f5bd0743 100644 --- a/src/localization/dispensingStrings.json +++ b/src/localization/dispensingStrings.json @@ -73,8 +73,8 @@ "refused_vaccine": "Refused vaccine", "validation_failed": "Some required fields are not completed.", "patient_deleted": "Patient successfully deleted", - "patient_already_exists": "'The patient already exists. Can only sync existing patient from tablet to server.", - "patient_already_deleted": "'The patient has already been deleted. You cannot sync this patient back." + "patient_already_exists": "The patient already exists. Can only sync existing patient from tablet to server.", + "patient_already_deleted": "The patient has already been deleted. You cannot sync this patient back." }, "fr": { "vaccination_history": "Historique de vaccination pour", From be6d5430003bfcbc8769f36e87b2de22823d5e81 Mon Sep 17 00:00:00 2001 From: Arjun Sah <arjunsah@sussol.net> Date: Tue, 9 Aug 2022 13:49:04 +0545 Subject: [PATCH 098/131] #4647 added toggle to hide zero stock Added toggle to hide zero stock on Current stock. Creating new reducer and action to handle it --- src/pages/StockPage.js | 50 ++++++++++++++----- .../dataTableUtilities/actions/constants.js | 1 + .../actions/tableActions.js | 6 +++ .../dataTableUtilities/getPageInitialiser.js | 1 + .../reducer/tableReducers.js | 29 ++++++++++- 5 files changed, 74 insertions(+), 13 deletions(-) diff --git a/src/pages/StockPage.js b/src/pages/StockPage.js index 02edf0620..a7a5fd028 100644 --- a/src/pages/StockPage.js +++ b/src/pages/StockPage.js @@ -4,22 +4,23 @@ * Sustainable Solutions (NZ) Ltd. 2019 */ -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import PropTypes from 'prop-types'; import { View } from 'react-native'; import { connect } from 'react-redux'; -import { getItemLayout, getPageDispatchers } from './dataTableUtilities'; +import { getItemLayout, getPageDispatchers, PageActions } from './dataTableUtilities'; import { DataTable, DataTableHeaderRow, DataTableRow } from '../widgets/DataTable'; -import { DataTablePageView, SearchBar } from '../widgets'; +import { DataTablePageView, SearchBar, ToggleBar } from '../widgets'; import globalStyles from '../globalStyles'; import { useSyncListener } from '../hooks'; -import { generalStrings } from '../localization'; +import { generalStrings, buttonStrings } from '../localization'; import { SupplierCreditActions } from '../actions/SupplierCreditActions'; import { RowDetailActions } from '../actions/RowDetailActions'; +import { ROUTES } from '../navigation'; /** * Renders a mSupply mobile page with Items and their stock levels. @@ -49,12 +50,17 @@ export const Stock = ({ onFilterData, onSortColumn, refund, + showAll, + toggleStockOut, }) => { // Refresh data on retrieving item or itembatch records from sync. useSyncListener(refreshData, ['Item', 'ItemBatch']); const refundCallback = React.useCallback(() => itemId => refund(itemId), []); + console.log(data.length); + console.log(searchTerm); + const renderRow = useCallback( listItem => { const { item, index } = listItem; @@ -83,17 +89,33 @@ export const Stock = ({ /> ); - const { pageTopSectionContainer } = globalStyles; + const toggles = useMemo( + () => [{ text: buttonStrings.hide_stockouts, onPress: toggleStockOut, isOn: !showAll }], + [showAll] + ); + + console.log(showAll); + + const { + pageTopSectionContainer, + pageTopRightSectionContainer, + pageTopLeftSectionContainer, + } = globalStyles; return ( <DataTablePageView captureUncaughtGestures={false}> <View style={pageTopSectionContainer}> - <SearchBar - onChangeText={onFilterData} - value={searchTerm} - onFocusOrBlur={selectedRow && onDeselectRow} - // eslint-disable-next-line max-len - placeholder={`${generalStrings.search_by} ${generalStrings.code} ${generalStrings.or} ${generalStrings.name}`} - /> + <View style={pageTopLeftSectionContainer}> + <SearchBar + onChangeText={onFilterData} + value={searchTerm} + onFocusOrBlur={selectedRow && onDeselectRow} + // eslint-disable-next-line max-len + placeholder={`${generalStrings.search_by} ${generalStrings.code} ${generalStrings.or} ${generalStrings.name}`} + /> + </View> + <View style={pageTopRightSectionContainer}> + <ToggleBar toggles={toggles} /> + </View> </View> <DataTable @@ -113,6 +135,7 @@ const mapDispatchToProps = dispatch => ({ ...getPageDispatchers(dispatch, '', 'stock'), onSelectRow: rowKey => dispatch(RowDetailActions.openItemDetail(rowKey)), refund: rowKey => dispatch(SupplierCreditActions.createFromItem(rowKey)), + onFilterData: value => dispatch(PageActions.filterDataWithZeroStockToggle(value, ROUTES.STOCK)), }); const mapStateToProps = state => { @@ -126,6 +149,7 @@ export const StockPage = connect(mapStateToProps, mapDispatchToProps)(Stock); Stock.defaultProps = { selectedRow: null, + showAll: true, }; Stock.propTypes = { @@ -143,4 +167,6 @@ Stock.propTypes = { onFilterData: PropTypes.func.isRequired, onSortColumn: PropTypes.func.isRequired, refund: PropTypes.func.isRequired, + showAll: PropTypes.bool, + toggleStockOut: PropTypes.func.isRequired, }; diff --git a/src/pages/dataTableUtilities/actions/constants.js b/src/pages/dataTableUtilities/actions/constants.js index 587551206..b95e7c514 100644 --- a/src/pages/dataTableUtilities/actions/constants.js +++ b/src/pages/dataTableUtilities/actions/constants.js @@ -21,6 +21,7 @@ export const ACTIONS = { FILTER_DATA_WITH_FINALISED_TOGGLE: 'filterDataWithFinalisedToggle', FILTER_DATA_WITH_OVER_STOCK_TOGGLE: 'filterDataWithOverStockToggle', TOGGLE_COLUMN_SET: 'toggleColumnSet', + FILTER_DATA_WITH_ZERO_STOCK_TOGGLE: 'filterDataWithZeroStockToggle', // pageAction constants OPEN_MODAL: 'openModal', diff --git a/src/pages/dataTableUtilities/actions/tableActions.js b/src/pages/dataTableUtilities/actions/tableActions.js index 28d191eea..8475a0297 100644 --- a/src/pages/dataTableUtilities/actions/tableActions.js +++ b/src/pages/dataTableUtilities/actions/tableActions.js @@ -305,6 +305,11 @@ export const filterDataWithOverStockToggle = (searchTerm, route) => ({ payload: { searchTerm, route }, }); +export const filterDataWithZeroStockToggle = (searchTerm, route) => ({ + type: ACTIONS.FILTER_DATA_WITH_ZERO_STOCK_TOGGLE, + payload: { searchTerm, route }, +}); + export const TableActionsLookup = { sortData, filterData, @@ -331,4 +336,5 @@ export const TableActionsLookup = { filterDataWithFinalisedToggle, filterDataWithOverStockToggle, toggleColumnSet, + filterDataWithZeroStockToggle, }; diff --git a/src/pages/dataTableUtilities/getPageInitialiser.js b/src/pages/dataTableUtilities/getPageInitialiser.js index 637de5734..95af1e87a 100644 --- a/src/pages/dataTableUtilities/getPageInitialiser.js +++ b/src/pages/dataTableUtilities/getPageInitialiser.js @@ -185,6 +185,7 @@ const stockInitialiser = () => { filterDataKeys: ['name', 'code'], sortKey: 'name', isAscending: true, + showAll: true, selectedRow: null, route: ROUTES.STOCK, columns: getColumns(ROUTES.STOCK), diff --git a/src/pages/dataTableUtilities/reducer/tableReducers.js b/src/pages/dataTableUtilities/reducer/tableReducers.js index 23ff61eca..d7cbf2929 100644 --- a/src/pages/dataTableUtilities/reducer/tableReducers.js +++ b/src/pages/dataTableUtilities/reducer/tableReducers.js @@ -136,6 +136,33 @@ export const filterDataWithOverStockToggle = (state, action) => { return { ...state, data: sortedData, searchTerm }; }; +/** + * Filters the backingData with REALM - first applying show/hide zero stock filtering + * toggle. Sorting is held stable. + * + */ +export const filterDataWithZeroStockToggle = (state, action) => { + const { backingData, filterDataKeys, sortKey, isAscending, showAll } = state; + const { payload } = action; + const { searchTerm } = payload; + + // Apply query filtering + const queryString = filterDataKeys.map(filterTerm => `${filterTerm} CONTAINS[c] $0`).join(' OR '); + const queryFilteredData = backingData.filtered(queryString, searchTerm.trim()).slice(); + + // Filter by toggle status - showing or not showing zero stocked records. + const stockFilteredData = !showAll + ? queryFilteredData.slice().filter(item => item.hasStock) + : queryFilteredData.slice(); + + // Sort the data by the current sorting parameters. + const sortedData = sortKey + ? sortDataBy(stockFilteredData, sortKey, isAscending) + : stockFilteredData; + + return { ...state, data: sortedData, searchTerm }; +}; + /** * Simply refresh's the data object in state to correctly match the * backingData. Used for when side effects such as finalizing manipulate @@ -232,7 +259,6 @@ export const hideOverStocked = state => { return { ...state, data: newData, showAll: false }; }; - /** * Filters by backingData elements `hasStock` field. */ @@ -285,4 +311,5 @@ export const TableReducerLookup = { filterDataWithFinalisedToggle, filterDataWithOverStockToggle, toggleColumnSet, + filterDataWithZeroStockToggle, }; From ebc159b4272f0a4b4904ebaa95019a6b3fab515b Mon Sep 17 00:00:00 2001 From: Arjun Sah <arjunsah@sussol.net> Date: Tue, 9 Aug 2022 14:35:10 +0545 Subject: [PATCH 099/131] added toggle button resources added toggle button resources --- src/localization/buttonStrings.json | 24 ++++++++++++++++-------- src/pages/StockPage.js | 7 +------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/localization/buttonStrings.json b/src/localization/buttonStrings.json index 0e66a1efd..eff342006 100644 --- a/src/localization/buttonStrings.json +++ b/src/localization/buttonStrings.json @@ -38,7 +38,8 @@ "customer_data": "Customer Data", "factory_reset": "Factory Reset", "check_connection": "Check connection", - "connection_status": "Connection successful" + "connection_status": "Connection successful", + "hide_zero_stocks": "Hide zero stocks" }, "fr": { "back": "Précédent", @@ -77,7 +78,8 @@ "customer_data": "Données Clients", "factory_reset": "Réinitialiser", "check_connection": "Vérifiez la connexion", - "connection_status": "Connexion réussie" + "connection_status": "Connexion réussie", + "hide_zero_stocks": "Masquer les stocks zéro" }, "gil": { "add_master_list_items": "Rinea am list", @@ -96,7 +98,8 @@ "new_supplier_invoice": "Bebwan am oota\nman pharmacy", "past": "Are I mwaina", "use_requested_quantities": "Kabongana maitina\nae kainnanoaki", - "use_suggested_quantities": "Kabongana te\nmwaiti are e katauaki" + "use_suggested_quantities": "Kabongana te\nmwaiti are e katauaki", + "hide_zero_stocks": "Hide zero stocks" }, "tl": { "add_master_list_items": "Utiliza Lista Master", @@ -115,7 +118,8 @@ "new_supplier_invoice": "Distribuidor nia Invoice Foun", "past": "Pasadu", "use_suggested_quantities": "Kuantidade Sujere", - "use_requested_quantities": "Kuantidade Ezije" + "use_requested_quantities": "Kuantidade Ezije", + "hide_zero_stocks": "Hide zero stocks" }, "la": { "add_batch": "​ໃສ​່​ເລກ​ຊຸດ​ຜະ​ລິດ", @@ -135,7 +139,8 @@ "new_supplier_invoice": "ໃບ​ຮັບ​ສິນ​ຄ້າໃໝ່", "past": "ໃນທີ່​ຜ່ານ​ມາ", "use_suggested_quantities": "ນຳໃຊ້ຈຳ​ນວນທີ່​ຖືກແນະ​ນຳ", - "use_requested_quantities": "ນຳໃຊ້ຈຳ​ນວນທີ່​ສະ​ເໜີ​ຂໍ" + "use_requested_quantities": "ນຳໃຊ້ຈຳ​ນວນທີ່​ສະ​ເໜີ​ຂໍ", + "hide_zero_stocks": "Hide zero stocks" }, "my": { "add_fridge": "အအေးပေးစက်အတွင်း သိမ်းဆည်းသည်", @@ -163,7 +168,8 @@ "past": "ယခင်", "save_changes": "ပြောင်းလဲမှုအားသိမ်းဆည်းသည်", "use_requested_quantities": "တောင်းခံထားသော အ‌ရေအတွက်ကို သုံးသည်", - "use_suggested_quantities": "အကြံပြုထားသော အရေအတွက်ကို သုံးသည်" + "use_suggested_quantities": "အကြံပြုထားသော အရေအတွက်ကို သုံးသည်", + "hide_zero_stocks": "Hide zero stocks" }, "pt": { "back": "Voltar", @@ -202,7 +208,8 @@ "customer_data": "Dados do cliente", "factory_reset": "Configuração inicial", "check_connection": "Check connection", - "connection_status": "Conexão ativa" + "connection_status": "Conexão ativa", + "hide_zero_stocks": "Hide zero stocks" }, "es": { "next": "Siguiente", @@ -231,6 +238,7 @@ "use_requested_quantities": "Usar Cantidades Solicitadas", "use_suggested_quantities": "Usar Cantidades Sugeridas", "view_regimen_data": "Ver Datos del Régimen", - "check_connection": "Check connection" + "check_connection": "Check connection", + "hide_zero_stocks": "Ocultar existencias cero" } } diff --git a/src/pages/StockPage.js b/src/pages/StockPage.js index a7a5fd028..ae9a24026 100644 --- a/src/pages/StockPage.js +++ b/src/pages/StockPage.js @@ -58,9 +58,6 @@ export const Stock = ({ const refundCallback = React.useCallback(() => itemId => refund(itemId), []); - console.log(data.length); - console.log(searchTerm); - const renderRow = useCallback( listItem => { const { item, index } = listItem; @@ -90,12 +87,10 @@ export const Stock = ({ ); const toggles = useMemo( - () => [{ text: buttonStrings.hide_stockouts, onPress: toggleStockOut, isOn: !showAll }], + () => [{ text: buttonStrings.hide_zero_stocks, onPress: toggleStockOut, isOn: !showAll }], [showAll] ); - console.log(showAll); - const { pageTopSectionContainer, pageTopRightSectionContainer, From be96fb01d8785bf603b1ffd238aa4c6ec5906c0a Mon Sep 17 00:00:00 2001 From: Arjun Sah <arjunsah@sussol.net> Date: Mon, 15 Aug 2022 14:36:51 +0545 Subject: [PATCH 100/131] button resources improved --- src/localization/buttonStrings.json | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/localization/buttonStrings.json b/src/localization/buttonStrings.json index eff342006..eda4d0c3c 100644 --- a/src/localization/buttonStrings.json +++ b/src/localization/buttonStrings.json @@ -78,8 +78,7 @@ "customer_data": "Données Clients", "factory_reset": "Réinitialiser", "check_connection": "Vérifiez la connexion", - "connection_status": "Connexion réussie", - "hide_zero_stocks": "Masquer les stocks zéro" + "connection_status": "Connexion réussie" }, "gil": { "add_master_list_items": "Rinea am list", @@ -98,8 +97,7 @@ "new_supplier_invoice": "Bebwan am oota\nman pharmacy", "past": "Are I mwaina", "use_requested_quantities": "Kabongana maitina\nae kainnanoaki", - "use_suggested_quantities": "Kabongana te\nmwaiti are e katauaki", - "hide_zero_stocks": "Hide zero stocks" + "use_suggested_quantities": "Kabongana te\nmwaiti are e katauaki" }, "tl": { "add_master_list_items": "Utiliza Lista Master", @@ -118,8 +116,7 @@ "new_supplier_invoice": "Distribuidor nia Invoice Foun", "past": "Pasadu", "use_suggested_quantities": "Kuantidade Sujere", - "use_requested_quantities": "Kuantidade Ezije", - "hide_zero_stocks": "Hide zero stocks" + "use_requested_quantities": "Kuantidade Ezije" }, "la": { "add_batch": "​ໃສ​່​ເລກ​ຊຸດ​ຜະ​ລິດ", @@ -139,8 +136,7 @@ "new_supplier_invoice": "ໃບ​ຮັບ​ສິນ​ຄ້າໃໝ່", "past": "ໃນທີ່​ຜ່ານ​ມາ", "use_suggested_quantities": "ນຳໃຊ້ຈຳ​ນວນທີ່​ຖືກແນະ​ນຳ", - "use_requested_quantities": "ນຳໃຊ້ຈຳ​ນວນທີ່​ສະ​ເໜີ​ຂໍ", - "hide_zero_stocks": "Hide zero stocks" + "use_requested_quantities": "ນຳໃຊ້ຈຳ​ນວນທີ່​ສະ​ເໜີ​ຂໍ" }, "my": { "add_fridge": "အအေးပေးစက်အတွင်း သိမ်းဆည်းသည်", @@ -168,8 +164,7 @@ "past": "ယခင်", "save_changes": "ပြောင်းလဲမှုအားသိမ်းဆည်းသည်", "use_requested_quantities": "တောင်းခံထားသော အ‌ရေအတွက်ကို သုံးသည်", - "use_suggested_quantities": "အကြံပြုထားသော အရေအတွက်ကို သုံးသည်", - "hide_zero_stocks": "Hide zero stocks" + "use_suggested_quantities": "အကြံပြုထားသော အရေအတွက်ကို သုံးသည်" }, "pt": { "back": "Voltar", @@ -208,8 +203,7 @@ "customer_data": "Dados do cliente", "factory_reset": "Configuração inicial", "check_connection": "Check connection", - "connection_status": "Conexão ativa", - "hide_zero_stocks": "Hide zero stocks" + "connection_status": "Conexão ativa" }, "es": { "next": "Siguiente", @@ -238,7 +232,6 @@ "use_requested_quantities": "Usar Cantidades Solicitadas", "use_suggested_quantities": "Usar Cantidades Sugeridas", "view_regimen_data": "Ver Datos del Régimen", - "check_connection": "Check connection", - "hide_zero_stocks": "Ocultar existencias cero" + "check_connection": "Check connection" } } From 52f364cae620757b3e82476a278f2a8c7d9c35a6 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 16 Aug 2022 13:11:42 +0545 Subject: [PATCH 101/131] Refactor toggleStockOut reducer --- src/pages/dataTableUtilities/reducer/tableReducers.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/pages/dataTableUtilities/reducer/tableReducers.js b/src/pages/dataTableUtilities/reducer/tableReducers.js index d7cbf2929..805de339e 100644 --- a/src/pages/dataTableUtilities/reducer/tableReducers.js +++ b/src/pages/dataTableUtilities/reducer/tableReducers.js @@ -263,13 +263,11 @@ export const hideOverStocked = state => { * Filters by backingData elements `hasStock` field. */ export const toggleStockOut = state => { - const { backingData, showAll } = state; - - const newToggleState = !showAll; + const { backingData } = state; - const newData = newToggleState ? backingData.slice() : backingData.filter(item => item.hasStock); + const newData = backingData.filter(item => item.hasStock); - return { ...state, data: newData, showAll: newToggleState, searchTerm: '' }; + return { ...state, data: newData, showAll: false, searchTerm: '' }; }; /** From 28581841ae71581b2351677efd297f0ef6f35639 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 16 Aug 2022 16:18:30 +0545 Subject: [PATCH 102/131] Disable deleted patient from being given vaccine --- src/widgets/Tabs/PatientSelect.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/widgets/Tabs/PatientSelect.js b/src/widgets/Tabs/PatientSelect.js index b1eaf64ed..47d4e3616 100644 --- a/src/widgets/Tabs/PatientSelect.js +++ b/src/widgets/Tabs/PatientSelect.js @@ -256,16 +256,22 @@ const PatientSelectComponent = ({ rowKey={keyExtractor(item)} columns={columns} onPress={name => { - const selectedPatient = UIDatabase.get('Name', name?.id); - if (selectedPatient.isDeceased) { - toggleIsDeceasedAlert(); - return; - } + const localPatient = UIDatabase.get('Name', name?.id); // Only show a spinner when the name doesn't exist in the database, as we need to // send a request to the server to add a name store join. - if (selectedPatient) { - selectPatient(name); + if (localPatient) { + if (localPatient.isDeceased) { + toggleIsDeceasedAlert(); + return; + } + + if (localPatient.isDeleted) { + ToastAndroid.show(dispensingStrings.patient_already_deleted, ToastAndroid.LONG); + return; + } + + selectPatient(localPatient); } else { withLoadingIndicator(() => selectPatient(name)); } From 2e90610f52aa774de5c6e4f4d261ae0d867c39be Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 16 Aug 2022 16:53:50 +0545 Subject: [PATCH 103/131] New Patient button should only go to tab 2 --- src/widgets/Tabs/PatientSelect.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/Tabs/PatientSelect.js b/src/widgets/Tabs/PatientSelect.js index b1eaf64ed..56e3dbf36 100644 --- a/src/widgets/Tabs/PatientSelect.js +++ b/src/widgets/Tabs/PatientSelect.js @@ -384,7 +384,7 @@ const mapDispatchToProps = dispatch => { const patient = createDefaultName('patient', id); dispatch(NameActions.create(patient)); dispatch(NameNoteActions.createSurveyNameNote(patient)); - dispatch(WizardActions.nextTab()); + dispatch(WizardActions.switchTab(1)); }); const updateForm = (key, value) => dispatch(FormActions.updateForm(key, value)); From f9c11f354134781d366211144a881f06223f7f67 Mon Sep 17 00:00:00 2001 From: Nastia <41636693+chastichno@users.noreply.github.com> Date: Wed, 17 Aug 2022 12:15:43 +1200 Subject: [PATCH 104/131] disable ok and repeat after clicking --- src/widgets/Tabs/VaccineSelect.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/widgets/Tabs/VaccineSelect.js b/src/widgets/Tabs/VaccineSelect.js index 94862dce2..11fd8d80f 100644 --- a/src/widgets/Tabs/VaccineSelect.js +++ b/src/widgets/Tabs/VaccineSelect.js @@ -123,10 +123,10 @@ const VaccineSelectComponent = ({ setOkConfirmButtonEnabled(false); }, [onConfirm]); - const confirmAndRepeatPrescription = React.useCallback( - () => runWithLoadingIndicator(okAndRepeat), - [okAndRepeat] - ); + const confirmAndRepeatPrescription = React.useCallback(() => { + runWithLoadingIndicator(okAndRepeat); + setOkConfirmButtonEnabled(false); + }, [okAndRepeat]); const onModalClose = confirmDoubleDoseModalOpen ? toggleConfirmDoubleDoseModal From 5e785f1f5f39166bf3f35dbc650120272b6e25ff Mon Sep 17 00:00:00 2001 From: Arjun Sah <arjunsah@sussol.net> Date: Wed, 17 Aug 2022 10:49:01 +0545 Subject: [PATCH 105/131] #4894 fixed toggle failure Fixed toggle failure on Current stock and Stocktake by undo the code changes on toggleStockOut reducer function --- src/pages/dataTableUtilities/reducer/tableReducers.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/pages/dataTableUtilities/reducer/tableReducers.js b/src/pages/dataTableUtilities/reducer/tableReducers.js index 805de339e..fad5d2860 100644 --- a/src/pages/dataTableUtilities/reducer/tableReducers.js +++ b/src/pages/dataTableUtilities/reducer/tableReducers.js @@ -263,11 +263,12 @@ export const hideOverStocked = state => { * Filters by backingData elements `hasStock` field. */ export const toggleStockOut = state => { - const { backingData } = state; + const { backingData, showAll } = state; + const newToggleState = !showAll; - const newData = backingData.filter(item => item.hasStock); + const newData = newToggleState ? backingData.slice() : backingData.filter(item => item.hasStock); - return { ...state, data: newData, showAll: false, searchTerm: '' }; + return { ...state, data: newData, showAll: newToggleState, searchTerm: '' }; }; /** From 9be4aedbd40b2bac402f50a14964f74fe930dc6f Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Fri, 19 Aug 2022 15:01:34 +0545 Subject: [PATCH 106/131] Fix App crashes on tapping Save changes button multiple times --- src/widgets/modals/VaccinationEvent.js | 204 +++++++++++++++++-------- 1 file changed, 143 insertions(+), 61 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 1b1933b9f..29bdf20ce 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -1,6 +1,6 @@ /* eslint-disable react/jsx-curly-newline */ /* eslint-disable react/forbid-prop-types */ -import React, { useCallback, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { View, StyleSheet, Text, ToastAndroid } from 'react-native'; import PropTypes from 'prop-types'; import { batch, connect } from 'react-redux'; @@ -66,42 +66,131 @@ export const VaccinationEventComponent = ({ const pcdFormRef = useRef(null); const supplementalFormRef = useRef(null); const vaccinationFormRef = useRef(null); + const [vaccinationEventNameNote, setVaccinationEventNameNote] = useState(); + const [vaccinationEvent, setVaccinationEvent] = useState(); + const [transaction, setTransaction] = useState(); + const [transactionBatch, setTransactionBatch] = useState(); + const [alertText, setAlertText] = useState('Something went wrong'); - const vaccinationEventNameNote = UIDatabase.get('NameNote', vaccinationEventId); - const vaccinationEvent = vaccinationEventNameNote.data; - const transaction = UIDatabase.get('Transaction', vaccinationEvent?.extra?.prescription?.id); - const nameNoteStoreName = vaccinationEvent?.storeName; - const alertText = nameNoteStoreName - ? modalStrings.formatString(modalStrings.vaccine_event_not_editable_store, nameNoteStoreName) - : modalStrings.vaccine_event_not_editable; - const transactionBatch = UIDatabase.objects('TransactionBatch').filtered( - 'transaction.id == $0', - transaction?.id - )[0]; + const [{ isDeletedVaccinationEvent }, setIsDeletedVaccinationEvent] = useState({ + isDeletedVaccinationEvent: false, + }); + + const [surveyForm, setSurveyForm] = useState(); + const [customDataObject, setCustomDataObject] = useState(); + const [parsedVaccinationEvent, setParsedVaccinationEvent] = useState(); + const [vaccinator, setVaccinator] = useState(); + const [vaccine, setVaccine] = useState(); const [isEditingTransaction, toggleEditTransaction] = useToggle(false); const [isModalOpen, toggleModal] = useToggle(false); const [isDeleteModalOpen, toggleDeleteModal] = useToggle(false); - const [vaccinator, setVaccinator] = useState(transactionBatch?.medicineAdministrator); - const [vaccine, setVaccine] = useState( - vaccines.filter(item => item.id === transactionBatch?.itemId)[0] - ); - const vaccineDropDownValues = vaccines - .filter(v => v.totalQuantity !== 0) - .map(({ code, name }) => `${code}: ${name}`); + const [vaccineDropDownValues, setVaccineDropDownValues] = useToggle(false); const [{ updatedPcdForm, isPCDValid }, setPCDForm] = useState({ updatedPcdForm: null, isPCDValid: true, }); + const [{ updatedSupplementalDataForm, isSupplementalDataValid }, setSupplementalData] = useState({ updatedSupplementalDataForm: null, isSupplementalDataValid: true, }); - const [{ isDeletedVaccinationEvent }, setIsDeletedVaccinationEvent] = useState({ - isDeletedVaccinationEvent: vaccinationEventNameNote.isDeleted, - }); + useEffect(() => { + const result = vaccines + .filter(v => v.totalQuantity !== 0) + .map(({ code, name }) => `${code}: ${name}`); + setVaccineDropDownValues(result); + }, [vaccines]); + + useEffect(() => { + const result = UIDatabase.get('NameNote', vaccinationEventId); + setVaccinationEventNameNote(result); + }, [vaccinationEventId]); + + useEffect(() => { + if (vaccinationEventNameNote?.data) { + setVaccinationEvent(vaccinationEventNameNote?.data); + } + }, [vaccinationEventNameNote]); + + useEffect(() => { + if (customDataObject) { + setSupplementalData({ + updatedSupplementalDataForm: customDataObject, + isSupplementalDataValid: true, + }); + } + }, [customDataObject]); + + useEffect(() => { + if (vaccinationEvent) { + const result = vaccinationEvent?.extra?.prescription?.customData + ? JSON.parse(vaccinationEvent.extra.prescription.customData) + : {}; + + setCustomDataObject(result); + + const result2 = { + ...vaccinationEvent, + extra: { + prescription: { ...vaccinationEvent.extra.prescription, customData: result }, + }, + }; + setParsedVaccinationEvent(result2); + } + }, [vaccinationEvent]); + + useEffect(() => { + if (vaccinationEvent) { + const { pcdNameNoteId } = vaccinationEvent; + const result = pcdNameNoteId + ? UIDatabase.get('NameNote', pcdNameNoteId) + : selectMostRecentNameNote(patient, 'PCD', vaccinationEvent.entryDate); + setSurveyForm(result); + } + }, [vaccinationEvent]); + + useEffect(() => { + if (vaccinationEventNameNote?.isDeleted) { + setIsDeletedVaccinationEvent({ + isDeletedVaccinationEvent: !!vaccinationEventNameNote?.isDeleted, + }); + } + }, [vaccinationEventNameNote]); + + useEffect(() => { + if (vaccinationEvent) { + setTransaction(UIDatabase.get('Transaction', vaccinationEvent?.extra?.prescription?.id)); + const nameNoteStoreName = vaccinationEvent?.storeName; + + const localString = modalStrings.vaccine_event_not_editable_store; + + const alert = nameNoteStoreName + ? modalStrings.formatString(localString, nameNoteStoreName) + : modalStrings.vaccine_event_not_editable; + setAlertText(alert); + } + }, [vaccinationEvent]); + + useEffect(() => { + if (transaction?.id) { + const result = UIDatabase.objects('TransactionBatch').filtered( + 'transaction.id == $0', + transaction?.id + )[0]; + + setTransactionBatch(result); + } + }, [transaction]); + + useEffect(() => { + if (transactionBatch) { + setVaccinator(transactionBatch?.medicineAdministrator); + setVaccine(vaccines.filter(item => item.id === transactionBatch?.itemId)[0]); + } + }, [transactionBatch]); // User cannot edit 'Vaccination Event' panel if vaccination was done on a different tablet/store const tryEdit = useCallback(() => { @@ -112,22 +201,6 @@ export const VaccinationEventComponent = ({ } }, [transaction]); - const { pcdNameNoteId } = vaccinationEvent; - const surveyForm = pcdNameNoteId - ? UIDatabase.get('NameNote', pcdNameNoteId) - : selectMostRecentNameNote(patient, 'PCD', vaccinationEvent.entryDate); - - const customDataObject = vaccinationEvent?.extra?.prescription?.customData - ? JSON.parse(vaccinationEvent.extra.prescription.customData) - : {}; - - const parsedVaccinationEvent = { - ...vaccinationEvent, - extra: { - prescription: { ...vaccinationEvent.extra.prescription, customData: customDataObject }, - }, - }; - const trySave = useCallback(() => { setIsDeletedVaccinationEvent({ isDeletedVaccinationEvent: true, @@ -194,32 +267,41 @@ export const VaccinationEventComponent = ({ </FlexRow> <FlexRow flex={1}> <View style={localStyles.formContainer}> - <Paper - Header={ - <Title title={vaccineStrings.vaccine_event_supplemental_data_title} size="large" /> - } - contentContainerStyle={{ flex: 1 }} - style={{ flex: 1 }} - headerContainerStyle={localStyles.title} - > - <JSONForm - ref={supplementalFormRef} - formData={customDataObject ?? null} - surveySchema={supplementalDataSchema} - onChange={(changed, validator) => { - setSupplementalData({ - updatedSupplementalDataForm: changed.formData, - isSupplementalDataValid: validator(changed.formData), - }); - }} - disabled={isDeletedVaccinationEvent} + {!!supplementalDataSchema && !!customDataObject ? ( + <Paper + // eslint-disable-next-line prettier/prettier + Header={( + <Title + title={vaccineStrings.vaccine_event_supplemental_data_title} + size="large" + /> + // eslint-disable-next-line prettier/prettier + )} + contentContainerStyle={{ flex: 1 }} + style={{ flex: 1 }} + headerContainerStyle={localStyles.title} > - <></> - </JSONForm> - </Paper> + <JSONForm + ref={supplementalFormRef} + formData={customDataObject} + surveySchema={supplementalDataSchema} + onChange={(changed, validator) => { + setSupplementalData({ + updatedSupplementalDataForm: changed.formData, + isSupplementalDataValid: validator(changed.formData), + }); + }} + disabled={isDeletedVaccinationEvent} + > + <></> + </JSONForm> + </Paper> + ) : ( + <NoPCDForm /> + )} </View> </FlexRow> - {!!vaccinationEventSchema && !!vaccinationEvent && ( + {!!vaccinationEventSchema && !!vaccinationEvent && !!parsedVaccinationEvent && ( <FlexRow flex={1}> <View style={localStyles.formContainer}> <Paper From 4aebf03f186420f981696459b4bd0ec3928d4f28 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Fri, 19 Aug 2022 15:55:35 +0545 Subject: [PATCH 107/131] Fix vaccination event edit logic --- src/widgets/VaccinatorDropDown.js | 1 + src/widgets/modals/VaccinationEvent.js | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/widgets/VaccinatorDropDown.js b/src/widgets/VaccinatorDropDown.js index 8679b4b11..50e528e05 100644 --- a/src/widgets/VaccinatorDropDown.js +++ b/src/widgets/VaccinatorDropDown.js @@ -11,6 +11,7 @@ export const VaccinatorDropDown = ({ value, onChange, style }) => { return ( <DropDown style={{ ...style, ...styles.dropdown }} + isDisabled={values.length <= 0} values={values} onValueChange={(_, i) => onChange(medicineAdmins[i])} selectedValue={value?.displayString} diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 29bdf20ce..cffaecb3b 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -85,7 +85,7 @@ export const VaccinationEventComponent = ({ const [isEditingTransaction, toggleEditTransaction] = useToggle(false); const [isModalOpen, toggleModal] = useToggle(false); const [isDeleteModalOpen, toggleDeleteModal] = useToggle(false); - const [vaccineDropDownValues, setVaccineDropDownValues] = useToggle(false); + const [vaccineDropDownValues, setVaccineDropDownValues] = useState([]); const [{ updatedPcdForm, isPCDValid }, setPCDForm] = useState({ updatedPcdForm: null, @@ -335,6 +335,7 @@ export const VaccinationEventComponent = ({ /> <Title title={vaccineStrings.vaccines} size="medium" /> <DropDown + isDisabled={vaccineDropDownValues.length === 0} style={(localStyles.dropdown, { width: null, flex: 1 })} values={vaccineDropDownValues} onValueChange={(_, i) => setVaccine(vaccines[i])} @@ -365,6 +366,7 @@ export const VaccinationEventComponent = ({ </FlexRow> <FlexRow flex={0} justifyContent="center"> <PageButton + disabled={!(!!surveySchema && !!surveyForm)} text={buttonStrings.save_changes} onPress={() => savePCDForm(surveyForm, updatedPcdForm)} style={localStyles.saveButton} @@ -372,6 +374,7 @@ export const VaccinationEventComponent = ({ isDisabled={!isPCDValid || isDeletedVaccinationEvent} /> <PageButton + disabled={!(!!vaccinationEventSchema && !!vaccinationEvent && !!parsedVaccinationEvent)} text={buttonStrings.save_changes} onPress={() => saveSupplementalData(vaccinationEventNameNote, updatedSupplementalDataForm) From 8e7d7cb4cacad1683e5d2d63352762cc46479d23 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Fri, 19 Aug 2022 16:21:55 +0545 Subject: [PATCH 108/131] Minor refactor --- src/widgets/modals/VaccinationEvent.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index cffaecb3b..81e334e99 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -71,21 +71,19 @@ export const VaccinationEventComponent = ({ const [transaction, setTransaction] = useState(); const [transactionBatch, setTransactionBatch] = useState(); const [alertText, setAlertText] = useState('Something went wrong'); - const [{ isDeletedVaccinationEvent }, setIsDeletedVaccinationEvent] = useState({ isDeletedVaccinationEvent: false, }); - const [surveyForm, setSurveyForm] = useState(); const [customDataObject, setCustomDataObject] = useState(); const [parsedVaccinationEvent, setParsedVaccinationEvent] = useState(); - + const [vaccineDropDownValues, setVaccineDropDownValues] = useState([]); const [vaccinator, setVaccinator] = useState(); const [vaccine, setVaccine] = useState(); + const [isEditingTransaction, toggleEditTransaction] = useToggle(false); const [isModalOpen, toggleModal] = useToggle(false); const [isDeleteModalOpen, toggleDeleteModal] = useToggle(false); - const [vaccineDropDownValues, setVaccineDropDownValues] = useState([]); const [{ updatedPcdForm, isPCDValid }, setPCDForm] = useState({ updatedPcdForm: null, From 9f370b85a2d54d3d9d7ac97759011bd05a3bd281 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Fri, 19 Aug 2022 17:20:27 +0545 Subject: [PATCH 109/131] Fix bug where vaccine selection during edit was having indexing discrepency - Dropdown code filtered out vaccines with no stock - In dropdown, we were selecting the selected vaccine based on index from the vaccines list but not considering that dropdown has no stock vaccines filted. - This causes an issue where when there is a vaccine in the list without stock, the dropdown selection would select a completely different vaccine. - Changed the code to query for id so that current vaccine is selected. --- src/widgets/modals/VaccinationEvent.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 81e334e99..a08efb91e 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -98,7 +98,10 @@ export const VaccinationEventComponent = ({ useEffect(() => { const result = vaccines .filter(v => v.totalQuantity !== 0) - .map(({ code, name }) => `${code}: ${name}`); + .map(({ id, code, name }) => ({ + id, + name: `${code}: ${name}`, + })); setVaccineDropDownValues(result); }, [vaccines]); @@ -335,8 +338,13 @@ export const VaccinationEventComponent = ({ <DropDown isDisabled={vaccineDropDownValues.length === 0} style={(localStyles.dropdown, { width: null, flex: 1 })} - values={vaccineDropDownValues} - onValueChange={(_, i) => setVaccine(vaccines[i])} + values={vaccineDropDownValues.map(item => item.name)} + onValueChange={(_, i) => { + const selectedVaccine = vaccines.find( + item => item.id === vaccineDropDownValues[i].id + ); + return setVaccine(selectedVaccine); + }} selectedValue={`${vaccine?.code}: ${vaccine?.name}`} /> </FlexColumn> From ca05931be1eaee2fc6518ac72d9a66691989b014 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Fri, 19 Aug 2022 17:58:09 +0545 Subject: [PATCH 110/131] Fix vaccinator dropdown's actual data was not updating --- src/widgets/modals/VaccinationEvent.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index a08efb91e..25ea09a9e 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -226,7 +226,7 @@ export const VaccinationEventComponent = ({ }); ToastAndroid.show(vaccineStrings.vaccination_not_updated, ToastAndroid.LONG); } - }, [patient, transactionBatch, vaccine]); + }, [patient, transactionBatch, vaccine, vaccinator, customDataObject, vaccinationEventNameNote]); const tryDelete = useCallback(() => { deleteVaccinationEvent(patient, transactionBatch, vaccinationEventNameNote); @@ -235,7 +235,7 @@ export const VaccinationEventComponent = ({ setIsDeletedVaccinationEvent({ isDeletedVaccinationEvent: true, }); - }, [patient]); + }, [patient, transactionBatch, vaccinationEventNameNote]); return ( <FlexView> From e82adf2bd3cdff1418a5f5b1e6e50a70cf1f1cfc Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Mon, 22 Aug 2022 14:04:20 +0545 Subject: [PATCH 111/131] UPdate disabled logic --- src/widgets/modals/VaccinationEvent.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 25ea09a9e..cd3a6cbd8 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -372,22 +372,24 @@ export const VaccinationEventComponent = ({ </FlexRow> <FlexRow flex={0} justifyContent="center"> <PageButton - disabled={!(!!surveySchema && !!surveyForm)} text={buttonStrings.save_changes} onPress={() => savePCDForm(surveyForm, updatedPcdForm)} style={localStyles.saveButton} textStyle={localStyles.saveButtonTextStyle} - isDisabled={!isPCDValid || isDeletedVaccinationEvent} + isDisabled={!isPCDValid || isDeletedVaccinationEvent || !(!!surveySchema && !!surveyForm)} /> <PageButton - disabled={!(!!vaccinationEventSchema && !!vaccinationEvent && !!parsedVaccinationEvent)} text={buttonStrings.save_changes} onPress={() => saveSupplementalData(vaccinationEventNameNote, updatedSupplementalDataForm) } style={localStyles.saveButton} textStyle={localStyles.saveButtonTextStyle} - isDisabled={!isSupplementalDataValid || isDeletedVaccinationEvent} + isDisabled={ + !isSupplementalDataValid || + isDeletedVaccinationEvent || + !(!!vaccinationEventSchema && !!vaccinationEvent && !!parsedVaccinationEvent) + } /> <PageButton text={isEditingTransaction ? buttonStrings.save_changes : buttonStrings.edit} From a7e318379bd3e5a3c484927f55d5f01b209f234e Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Mon, 22 Aug 2022 14:28:53 +0545 Subject: [PATCH 112/131] Update useeffects to cut down load time --- src/widgets/modals/VaccinationEvent.js | 64 +++++++++++--------------- 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index cd3a6cbd8..4cbb9c71d 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -96,34 +96,29 @@ export const VaccinationEventComponent = ({ }); useEffect(() => { - const result = vaccines - .filter(v => v.totalQuantity !== 0) - .map(({ id, code, name }) => ({ - id, - name: `${code}: ${name}`, - })); - setVaccineDropDownValues(result); - }, [vaccines]); - - useEffect(() => { - const result = UIDatabase.get('NameNote', vaccinationEventId); - setVaccinationEventNameNote(result); - }, [vaccinationEventId]); + setVaccineDropDownValues( + vaccines + .filter(v => v.totalQuantity !== 0) + .map(({ id, code, name }) => ({ + id, + name: `${code}: ${name}`, + })) + ); + + setVaccinationEventNameNote(UIDatabase.get('NameNote', vaccinationEventId)); + }, [vaccines, vaccinationEventId]); useEffect(() => { if (vaccinationEventNameNote?.data) { setVaccinationEvent(vaccinationEventNameNote?.data); } - }, [vaccinationEventNameNote]); - useEffect(() => { - if (customDataObject) { - setSupplementalData({ - updatedSupplementalDataForm: customDataObject, - isSupplementalDataValid: true, + if (vaccinationEventNameNote?.isDeleted) { + setIsDeletedVaccinationEvent({ + isDeletedVaccinationEvent: !!vaccinationEventNameNote?.isDeleted, }); } - }, [customDataObject]); + }, [vaccinationEventNameNote]); useEffect(() => { if (vaccinationEvent) { @@ -140,29 +135,13 @@ export const VaccinationEventComponent = ({ }, }; setParsedVaccinationEvent(result2); - } - }, [vaccinationEvent]); - useEffect(() => { - if (vaccinationEvent) { const { pcdNameNoteId } = vaccinationEvent; - const result = pcdNameNoteId + const surveyFormData = pcdNameNoteId ? UIDatabase.get('NameNote', pcdNameNoteId) : selectMostRecentNameNote(patient, 'PCD', vaccinationEvent.entryDate); - setSurveyForm(result); - } - }, [vaccinationEvent]); + setSurveyForm(surveyFormData); - useEffect(() => { - if (vaccinationEventNameNote?.isDeleted) { - setIsDeletedVaccinationEvent({ - isDeletedVaccinationEvent: !!vaccinationEventNameNote?.isDeleted, - }); - } - }, [vaccinationEventNameNote]); - - useEffect(() => { - if (vaccinationEvent) { setTransaction(UIDatabase.get('Transaction', vaccinationEvent?.extra?.prescription?.id)); const nameNoteStoreName = vaccinationEvent?.storeName; @@ -175,6 +154,15 @@ export const VaccinationEventComponent = ({ } }, [vaccinationEvent]); + useEffect(() => { + if (customDataObject) { + setSupplementalData({ + updatedSupplementalDataForm: customDataObject, + isSupplementalDataValid: true, + }); + } + }, [customDataObject]); + useEffect(() => { if (transaction?.id) { const result = UIDatabase.objects('TransactionBatch').filtered( From e2580eb974fd1496454ccadae285fdaf55acc34b Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 23 Aug 2022 14:49:08 +0545 Subject: [PATCH 113/131] Add loading indicator --- src/widgets/modals/VaccinationEvent.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 4cbb9c71d..f32cc3d89 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -23,6 +23,7 @@ import globalStyles, { SUSSOL_ORANGE, DANGER_RED, } from '../../globalStyles'; +import { Spinner } from '..'; import { buttonStrings, generalStrings, modalStrings, vaccineStrings } from '../../localization'; import { PageButton } from '../PageButton'; import { NameActions, NameNoteActions, VaccinePrescriptionActions } from '../../actions'; @@ -66,6 +67,7 @@ export const VaccinationEventComponent = ({ const pcdFormRef = useRef(null); const supplementalFormRef = useRef(null); const vaccinationFormRef = useRef(null); + const [loading, setLoading] = useState(true); const [vaccinationEventNameNote, setVaccinationEventNameNote] = useState(); const [vaccinationEvent, setVaccinationEvent] = useState(); const [transaction, setTransaction] = useState(); @@ -160,6 +162,7 @@ export const VaccinationEventComponent = ({ updatedSupplementalDataForm: customDataObject, isSupplementalDataValid: true, }); + setLoading(false); } }, [customDataObject]); @@ -225,6 +228,14 @@ export const VaccinationEventComponent = ({ }); }, [patient, transactionBatch, vaccinationEventNameNote]); + if (loading) { + return ( + <View style={globalStyles.loadingIndicatorContainer}> + <Spinner isSpinning={loading} color={SUSSOL_ORANGE} /> + </View> + ); + } + return ( <FlexView> <FlexRow flex={1} style={localStyles.formContainer}> From 139794596539dbe6f9d5000366c534db4a616ad8 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Fri, 26 Aug 2022 12:12:31 +0545 Subject: [PATCH 114/131] Lock vaccination event details to home store --- src/widgets/modals/VaccinationEvent.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index f32cc3d89..72bcea021 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -254,7 +254,7 @@ export const VaccinationEventComponent = ({ isPCDValid: validator(changed.formData), }); }} - disabled={isDeletedVaccinationEvent} + disabled={isDeletedVaccinationEvent || !transaction} > <></> </JSONForm> @@ -291,7 +291,7 @@ export const VaccinationEventComponent = ({ isSupplementalDataValid: validator(changed.formData), }); }} - disabled={isDeletedVaccinationEvent} + disabled={isDeletedVaccinationEvent || !transaction} > <></> </JSONForm> @@ -372,7 +372,7 @@ export const VaccinationEventComponent = ({ <FlexRow flex={0} justifyContent="center"> <PageButton text={buttonStrings.save_changes} - onPress={() => savePCDForm(surveyForm, updatedPcdForm)} + onPress={() => (!transaction ? toggleModal() : savePCDForm(surveyForm, updatedPcdForm))} style={localStyles.saveButton} textStyle={localStyles.saveButtonTextStyle} isDisabled={!isPCDValid || isDeletedVaccinationEvent || !(!!surveySchema && !!surveyForm)} @@ -380,7 +380,9 @@ export const VaccinationEventComponent = ({ <PageButton text={buttonStrings.save_changes} onPress={() => - saveSupplementalData(vaccinationEventNameNote, updatedSupplementalDataForm) + !transaction + ? toggleModal() + : saveSupplementalData(vaccinationEventNameNote, updatedSupplementalDataForm) } style={localStyles.saveButton} textStyle={localStyles.saveButtonTextStyle} From 3d10ce7d8460b5cd5e483ca3b399a3447ad15302 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Mon, 29 Aug 2022 13:50:13 +0545 Subject: [PATCH 115/131] Update transaction check with a boolean --- src/widgets/modals/VaccinationEvent.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 72bcea021..a3432d669 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -71,6 +71,7 @@ export const VaccinationEventComponent = ({ const [vaccinationEventNameNote, setVaccinationEventNameNote] = useState(); const [vaccinationEvent, setVaccinationEvent] = useState(); const [transaction, setTransaction] = useState(); + const [isCurrentStoreVaccineEvent, setIsCurrentStoreVaccineEvent] = useState(false); const [transactionBatch, setTransactionBatch] = useState(); const [alertText, setAlertText] = useState('Something went wrong'); const [{ isDeletedVaccinationEvent }, setIsDeletedVaccinationEvent] = useState({ @@ -168,6 +169,7 @@ export const VaccinationEventComponent = ({ useEffect(() => { if (transaction?.id) { + setIsCurrentStoreVaccineEvent(true); const result = UIDatabase.objects('TransactionBatch').filtered( 'transaction.id == $0', transaction?.id @@ -186,12 +188,12 @@ export const VaccinationEventComponent = ({ // User cannot edit 'Vaccination Event' panel if vaccination was done on a different tablet/store const tryEdit = useCallback(() => { - if (!transaction) { + if (!isCurrentStoreVaccineEvent) { toggleModal(); } else { toggleEditTransaction(); } - }, [transaction]); + }, [isCurrentStoreVaccineEvent]); const trySave = useCallback(() => { setIsDeletedVaccinationEvent({ @@ -254,7 +256,7 @@ export const VaccinationEventComponent = ({ isPCDValid: validator(changed.formData), }); }} - disabled={isDeletedVaccinationEvent || !transaction} + disabled={isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent} > <></> </JSONForm> @@ -291,7 +293,7 @@ export const VaccinationEventComponent = ({ isSupplementalDataValid: validator(changed.formData), }); }} - disabled={isDeletedVaccinationEvent || !transaction} + disabled={isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent} > <></> </JSONForm> @@ -372,7 +374,9 @@ export const VaccinationEventComponent = ({ <FlexRow flex={0} justifyContent="center"> <PageButton text={buttonStrings.save_changes} - onPress={() => (!transaction ? toggleModal() : savePCDForm(surveyForm, updatedPcdForm))} + onPress={() => + !isCurrentStoreVaccineEvent ? toggleModal() : savePCDForm(surveyForm, updatedPcdForm) + } style={localStyles.saveButton} textStyle={localStyles.saveButtonTextStyle} isDisabled={!isPCDValid || isDeletedVaccinationEvent || !(!!surveySchema && !!surveyForm)} @@ -380,7 +384,7 @@ export const VaccinationEventComponent = ({ <PageButton text={buttonStrings.save_changes} onPress={() => - !transaction + !isCurrentStoreVaccineEvent ? toggleModal() : saveSupplementalData(vaccinationEventNameNote, updatedSupplementalDataForm) } From d6aab9197cd3c716ea96465924ea836ccf81fa1c Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Thu, 1 Sep 2022 17:53:19 +0545 Subject: [PATCH 116/131] Fix the issue where a blank NameNote is created on every edit and it syncs too --- src/actions/Entities/NameNoteActions.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/actions/Entities/NameNoteActions.js b/src/actions/Entities/NameNoteActions.js index a4f03d020..fa8416f52 100644 --- a/src/actions/Entities/NameNoteActions.js +++ b/src/actions/Entities/NameNoteActions.js @@ -63,7 +63,12 @@ const updateForm = (data, validator) => ({ const saveEditing = () => (dispatch, getState) => { const nameNote = selectCreatingNameNote(getState()) ?? {}; const patient = UIDatabase.get('Name', nameNote?.nameID); - const isDirty = JSON.stringify(patient?.mostRecentPCD?.data) !== JSON.stringify(nameNote?.data); + const isDirty = + !( + nameNote?.data && + Object.keys(nameNote?.data).length === 0 && + Object.getPrototypeOf(nameNote?.data) === Object.prototype + ) && JSON.stringify(patient?.mostRecentPCD?.data) !== JSON.stringify(nameNote?.data); if (isDirty) { UIDatabase.write(() => createRecord(UIDatabase, 'NameNote', nameNote)); } From 823b9396f4be73ac4830623267f7454715a15de8 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Thu, 1 Sep 2022 17:54:10 +0545 Subject: [PATCH 117/131] Fix all nameNotes of Patient being updated every time VaccineEvent is created --- src/actions/Entities/NameActions.js | 2 +- src/actions/PatientActions.js | 2 +- src/database/utilities/createRecord.js | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/actions/Entities/NameActions.js b/src/actions/Entities/NameActions.js index 7182896b0..6764eccdf 100644 --- a/src/actions/Entities/NameActions.js +++ b/src/actions/Entities/NameActions.js @@ -73,7 +73,7 @@ const select = name => async dispatch => { const result = await createPatientVisibility(name); if (result) { UIDatabase.write(() => { - selectedName = createRecord(UIDatabase, 'Patient', name); + selectedName = createRecord(UIDatabase, 'Patient', name, true); }); } else { ToastAndroid.show(generalStrings.problem_connecting_please_try_again, ToastAndroid.LONG); diff --git a/src/actions/PatientActions.js b/src/actions/PatientActions.js index c2c67f4ef..8d88e14f5 100644 --- a/src/actions/PatientActions.js +++ b/src/actions/PatientActions.js @@ -150,7 +150,7 @@ const patientUpdate = patientDetails => async (dispatch, getState) => { createdDate, }; - UIDatabase.write(() => createRecord(UIDatabase, 'Patient', patientRecord)); + UIDatabase.write(() => createRecord(UIDatabase, 'Patient', patientRecord, false)); batch(() => { dispatch(closeModal()); diff --git a/src/database/utilities/createRecord.js b/src/database/utilities/createRecord.js index 827bc6850..99bb70ae3 100644 --- a/src/database/utilities/createRecord.js +++ b/src/database/utilities/createRecord.js @@ -247,7 +247,7 @@ const createNameNote = ( * country, female, supplyingStoreId, isActive * } */ -const createPatient = (database, patientDetails) => { +const createPatient = (database, patientDetails, createNameNotes) => { const { id: patientId, barcode: patientBarcode, @@ -344,7 +344,9 @@ const createPatient = (database, patientDetails) => { createdDate, }); - nameNotes?.forEach(nameNote => createNameNote(database, nameNote)); + if (createNameNotes) { + nameNotes?.forEach(nameNote => createNameNote(database, nameNote)); + } return patient; }; From a6ce0587b7bad968f3284a7d070eb96d67abb3ea Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Fri, 2 Sep 2022 11:11:40 +0545 Subject: [PATCH 118/131] Move is object empty check to a separate utility function --- src/actions/Entities/NameNoteActions.js | 8 +++----- src/utilities/checkIsObject.js | 3 +++ src/utilities/index.js | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/actions/Entities/NameNoteActions.js b/src/actions/Entities/NameNoteActions.js index fa8416f52..c4fc36bc3 100644 --- a/src/actions/Entities/NameNoteActions.js +++ b/src/actions/Entities/NameNoteActions.js @@ -9,6 +9,7 @@ import { import { selectSurveySchemas } from '../../selectors/formSchema'; import { validateJsonSchemaData } from '../../utilities/ajvValidator'; import { vaccineStrings } from '../../localization'; +import { checkIsObjectEmpty } from '../../utilities'; export const NAME_NOTE_ACTIONS = { SELECT: 'NAME_NOTE/select', @@ -64,11 +65,8 @@ const saveEditing = () => (dispatch, getState) => { const nameNote = selectCreatingNameNote(getState()) ?? {}; const patient = UIDatabase.get('Name', nameNote?.nameID); const isDirty = - !( - nameNote?.data && - Object.keys(nameNote?.data).length === 0 && - Object.getPrototypeOf(nameNote?.data) === Object.prototype - ) && JSON.stringify(patient?.mostRecentPCD?.data) !== JSON.stringify(nameNote?.data); + !checkIsObjectEmpty(nameNote?.data) && + JSON.stringify(patient?.mostRecentPCD?.data) !== JSON.stringify(nameNote?.data); if (isDirty) { UIDatabase.write(() => createRecord(UIDatabase, 'NameNote', nameNote)); } diff --git a/src/utilities/checkIsObject.js b/src/utilities/checkIsObject.js index a80cf91b6..e5619fe0b 100644 --- a/src/utilities/checkIsObject.js +++ b/src/utilities/checkIsObject.js @@ -8,3 +8,6 @@ export const checkIsObject = object => !!(object && typeof object === 'object' && object.constructor === Object); + +export const checkIsObjectEmpty = object => + checkIsObject(object) && Object.keys(object).length === 0; diff --git a/src/utilities/index.js b/src/utilities/index.js index 86899dcfc..f63879af4 100644 --- a/src/utilities/index.js +++ b/src/utilities/index.js @@ -17,7 +17,7 @@ export { requestPermission } from './requestPermission'; export { backupValidation, selectDocument } from './fileSystem'; export { debounce } from './underscoreMethods'; export { getModalTitle, MODAL_KEYS } from './getModalTitle'; -export { checkIsObject } from './checkIsObject'; +export { checkIsObject, checkIsObjectEmpty } from './checkIsObject'; export { validateReport } from './validateReport'; export { chunk } from './chunk'; export { parsePositiveIntegerInterfaceInput } from './parsers'; From 564a263c145340bc84414cade16660c6a5aeb67c Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Fri, 2 Sep 2022 15:57:12 +0545 Subject: [PATCH 119/131] Update patient create --- src/actions/Entities/NameActions.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/actions/Entities/NameActions.js b/src/actions/Entities/NameActions.js index 6764eccdf..2a9ff3ab1 100644 --- a/src/actions/Entities/NameActions.js +++ b/src/actions/Entities/NameActions.js @@ -104,7 +104,7 @@ const saveEditing = () => (dispatch, getState) => { const patientRecord = { ...currentPatient, dateOfBirth, createdDate, name }; - UIDatabase.write(() => createRecord(UIDatabase, 'Patient', patientRecord)); + UIDatabase.write(() => createRecord(UIDatabase, 'Patient', patientRecord, true)); dispatch(reset()); }; From d4e46ce2ba8f8c2c7a7054fda0b3931382e4664e Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Mon, 5 Sep 2022 13:25:06 +0545 Subject: [PATCH 120/131] #4913 Vax event creation and editing should not trigger patient update if the current store is not patient's home store --- src/actions/Entities/VaccinePrescriptionActions.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/actions/Entities/VaccinePrescriptionActions.js b/src/actions/Entities/VaccinePrescriptionActions.js index 30d20991d..26c782328 100644 --- a/src/actions/Entities/VaccinePrescriptionActions.js +++ b/src/actions/Entities/VaccinePrescriptionActions.js @@ -11,7 +11,7 @@ import { selectSelectedSupplementalData, selectSelectedVaccinator, } from '../../selectors/Entities/vaccinePrescription'; -import { selectEditingNameId } from '../../selectors/Entities/name'; +import { selectEditingNameId, selectEditingName } from '../../selectors/Entities/name'; import { NameActions } from './NameActions'; import { NameNoteActions } from './NameNoteActions'; import { goBack, gotoVaccineDispensingPage } from '../../navigation/actions'; @@ -230,6 +230,7 @@ const getPcdNameNoteID = patientId => { .sorted('entryDate', true); return patientNameNotes.length > 0 ? patientNameNotes[0].id : ''; }; + const createSupplementaryData = () => (dispatch, getState) => { // Create a supplementaryData object which is seeded with the data that was last // entered against a prescription @@ -281,7 +282,14 @@ const confirm = () => (dispatch, getState) => { }); } batch(() => { - dispatch(NameActions.saveEditing()); + const { isEditable = true } = selectEditingName(getState()); + + // We are already not allowing patient update for patient that do not belong + // to the current store. This check will stop unnecessary updates. + if (isEditable) { + dispatch(NameActions.saveEditing()); + } + dispatch(NameNoteActions.saveEditing()); dispatch(reset()); }); From dbe90296406e8405c9f1d0c07da4938341c9605f Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Wed, 7 Sep 2022 15:19:51 +0545 Subject: [PATCH 121/131] Fix Subpage 2 Additional details' changes are overridden by Subpage 3 Vax Event changes --- src/widgets/modals/VaccinationEvent.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index a3432d669..367b7b91c 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -159,10 +159,10 @@ export const VaccinationEventComponent = ({ useEffect(() => { if (customDataObject) { - setSupplementalData({ + setSupplementalData(prevState => ({ + ...prevState, updatedSupplementalDataForm: customDataObject, - isSupplementalDataValid: true, - }); + })); setLoading(false); } }, [customDataObject]); @@ -209,7 +209,7 @@ export const VaccinationEventComponent = ({ transactionBatch, vaccine, vaccinator, - customDataObject, + updatedSupplementalDataForm, vaccinationEventNameNote ); toggleEditTransaction(); @@ -219,7 +219,14 @@ export const VaccinationEventComponent = ({ }); ToastAndroid.show(vaccineStrings.vaccination_not_updated, ToastAndroid.LONG); } - }, [patient, transactionBatch, vaccine, vaccinator, customDataObject, vaccinationEventNameNote]); + }, [ + patient, + transactionBatch, + vaccine, + vaccinator, + updatedSupplementalDataForm, + vaccinationEventNameNote, + ]); const tryDelete = useCallback(() => { deleteVaccinationEvent(patient, transactionBatch, vaccinationEventNameNote); @@ -269,7 +276,7 @@ export const VaccinationEventComponent = ({ </FlexRow> <FlexRow flex={1}> <View style={localStyles.formContainer}> - {!!supplementalDataSchema && !!customDataObject ? ( + {!!supplementalDataSchema && !!updatedSupplementalDataForm ? ( <Paper // eslint-disable-next-line prettier/prettier Header={( @@ -285,7 +292,7 @@ export const VaccinationEventComponent = ({ > <JSONForm ref={supplementalFormRef} - formData={customDataObject} + formData={updatedSupplementalDataForm} surveySchema={supplementalDataSchema} onChange={(changed, validator) => { setSupplementalData({ From ca92dabf4372d4eaa7a48c58d6be3de578ad099f Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Wed, 7 Sep 2022 17:03:16 +0545 Subject: [PATCH 122/131] Fix patient homes store vax event reupdate error --- src/actions/Entities/NameActions.js | 4 ++-- src/actions/Entities/VaccinePrescriptionActions.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/actions/Entities/NameActions.js b/src/actions/Entities/NameActions.js index 2a9ff3ab1..0a402827a 100644 --- a/src/actions/Entities/NameActions.js +++ b/src/actions/Entities/NameActions.js @@ -96,7 +96,7 @@ const filter = (key, value) => ({ type: NAME_ACTIONS.FILTER, payload: { key, val const sort = sortKey => ({ type: NAME_ACTIONS.SORT, payload: { sortKey } }); -const saveEditing = () => (dispatch, getState) => { +const saveEditing = (shouldCreateNameNames = true) => (dispatch, getState) => { const currentPatient = selectEditingName(getState()); const createdDate = currentPatient?.createdDate ? new Date(currentPatient.createdDate) : null; const dateOfBirth = new Date(currentPatient.dateOfBirth); @@ -104,7 +104,7 @@ const saveEditing = () => (dispatch, getState) => { const patientRecord = { ...currentPatient, dateOfBirth, createdDate, name }; - UIDatabase.write(() => createRecord(UIDatabase, 'Patient', patientRecord, true)); + UIDatabase.write(() => createRecord(UIDatabase, 'Patient', patientRecord, shouldCreateNameNames)); dispatch(reset()); }; diff --git a/src/actions/Entities/VaccinePrescriptionActions.js b/src/actions/Entities/VaccinePrescriptionActions.js index 26c782328..8338be23e 100644 --- a/src/actions/Entities/VaccinePrescriptionActions.js +++ b/src/actions/Entities/VaccinePrescriptionActions.js @@ -287,7 +287,7 @@ const confirm = () => (dispatch, getState) => { // We are already not allowing patient update for patient that do not belong // to the current store. This check will stop unnecessary updates. if (isEditable) { - dispatch(NameActions.saveEditing()); + dispatch(NameActions.saveEditing(false)); } dispatch(NameNoteActions.saveEditing()); From 5565998089626469dcfc4f8377eb177ada4328af Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 13 Sep 2022 14:03:38 +0545 Subject: [PATCH 123/131] Make form controls color gray when disabled --- src/widgets/JSONForm/widgets/Checkboxes.js | 7 ++++--- src/widgets/JSONForm/widgets/Select.js | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/widgets/JSONForm/widgets/Checkboxes.js b/src/widgets/JSONForm/widgets/Checkboxes.js index 21bdfb1bc..005c9a335 100644 --- a/src/widgets/JSONForm/widgets/Checkboxes.js +++ b/src/widgets/JSONForm/widgets/Checkboxes.js @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; import CheckBox from '@react-native-community/checkbox'; import { APP_FONT_FAMILY } from '../../../globalStyles/fonts'; -import { SUSSOL_ORANGE } from '../../../globalStyles/colors'; +import { SUSSOL_ORANGE, WEARY_TARMAC } from '../../../globalStyles/colors'; import { FlexColumn, FlexRow, FlexView } from '../../index'; const selectValue = (value, selected, all) => { @@ -41,15 +41,16 @@ export const Checkboxes = ({ const checkboxes = enumOptions.map(option => { const itemDisabled = enumDisabled && enumDisabled.indexOf(option.value) !== -1; const checked = checkboxesValue.indexOf(option.value) !== -1; + const isDisabled = disabled || itemDisabled || readonly; return ( <FlexRow key={`checkbox_row_${option.value}`}> <FlexColumn> <CheckBox value={checked} - disabled={disabled || itemDisabled || readonly} + disabled={isDisabled} onValueChange={_onChange(option)} - tintColors={{ true: SUSSOL_ORANGE }} + tintColors={{ true: isDisabled ? WEARY_TARMAC : SUSSOL_ORANGE }} /> </FlexColumn> <FlexColumn> diff --git a/src/widgets/JSONForm/widgets/Select.js b/src/widgets/JSONForm/widgets/Select.js index b59930bd9..7a5318a8e 100644 --- a/src/widgets/JSONForm/widgets/Select.js +++ b/src/widgets/JSONForm/widgets/Select.js @@ -3,7 +3,7 @@ import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; import { Picker } from '@react-native-community/picker'; -import { SUSSOL_ORANGE } from '../../../globalStyles/colors'; +import { SUSSOL_ORANGE, LIGHT_GREY } from '../../../globalStyles/colors'; export const Select = ({ disabled, @@ -17,11 +17,21 @@ export const Select = ({ id, }) => { let pickers = options.enumOptions.map(({ label, value: enumValue }) => ( - <Picker.Item key={label} label={label} value={enumValue} color={SUSSOL_ORANGE} /> + <Picker.Item + key={label} + label={label} + value={enumValue} + color={disabled ? LIGHT_GREY : SUSSOL_ORANGE} + /> )); const placeholderItem = ( - <Picker.Item key={placeholder} label={placeholder} value={placeholder} color={SUSSOL_ORANGE} /> + <Picker.Item + key={placeholder} + label={placeholder} + value={placeholder} + color={disabled ? LIGHT_GREY : SUSSOL_ORANGE} + /> ); pickers = [placeholderItem, ...pickers]; From bbc6ed41b2c5793cfcfae4495326fe6417822518 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Tue, 13 Sep 2022 16:23:41 +0545 Subject: [PATCH 124/131] Make uneditable form's button gray background --- src/widgets/JSONForm/widgets/Select.js | 2 +- src/widgets/modals/VaccinationEvent.js | 25 ++++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/widgets/JSONForm/widgets/Select.js b/src/widgets/JSONForm/widgets/Select.js index 7a5318a8e..c3c83eaef 100644 --- a/src/widgets/JSONForm/widgets/Select.js +++ b/src/widgets/JSONForm/widgets/Select.js @@ -41,7 +41,7 @@ export const Select = ({ const { default: defaultValue } = schema; onChange(defaultValue); } - }); + }, [value, options, schema, onChange]); return ( <Picker diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 367b7b91c..5877deaad 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -22,6 +22,7 @@ import globalStyles, { GREY, SUSSOL_ORANGE, DANGER_RED, + WARMER_GREY, } from '../../globalStyles'; import { Spinner } from '..'; import { buttonStrings, generalStrings, modalStrings, vaccineStrings } from '../../localization'; @@ -384,7 +385,12 @@ export const VaccinationEventComponent = ({ onPress={() => !isCurrentStoreVaccineEvent ? toggleModal() : savePCDForm(surveyForm, updatedPcdForm) } - style={localStyles.saveButton} + style={[ + localStyles.saveButton, + isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent + ? localStyles.saveButtonCantEdit + : '', + ]} textStyle={localStyles.saveButtonTextStyle} isDisabled={!isPCDValid || isDeletedVaccinationEvent || !(!!surveySchema && !!surveyForm)} /> @@ -395,7 +401,12 @@ export const VaccinationEventComponent = ({ ? toggleModal() : saveSupplementalData(vaccinationEventNameNote, updatedSupplementalDataForm) } - style={localStyles.saveButton} + style={[ + localStyles.saveButton, + isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent + ? localStyles.saveButtonCantEdit + : '', + ]} textStyle={localStyles.saveButtonTextStyle} isDisabled={ !isSupplementalDataValid || @@ -405,7 +416,12 @@ export const VaccinationEventComponent = ({ /> <PageButton text={isEditingTransaction ? buttonStrings.save_changes : buttonStrings.edit} - style={localStyles.saveButton} + style={[ + localStyles.saveButton, + isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent + ? localStyles.saveButtonCantEdit + : '', + ]} textStyle={localStyles.saveButtonTextStyle} onPress={isEditingTransaction ? trySave : tryEdit} isDisabled={isDeletedVaccinationEvent} @@ -515,6 +531,9 @@ const localStyles = StyleSheet.create({ backgroundColor: SUSSOL_ORANGE, alignSelf: 'center', }, + saveButtonCantEdit: { + backgroundColor: WARMER_GREY, + }, saveButtonTextStyle: { ...globalStyles.buttonText, color: 'white', From afc57441b5ec9eabbcc9c9c4562294f9ea6d7f1e Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Wed, 14 Sep 2022 11:31:13 +0545 Subject: [PATCH 125/131] Rename saveButtonCantEdit to saveButtonCannotEdit --- src/widgets/modals/VaccinationEvent.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 5877deaad..53fc929ad 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -388,7 +388,7 @@ export const VaccinationEventComponent = ({ style={[ localStyles.saveButton, isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent - ? localStyles.saveButtonCantEdit + ? localStyles.saveButtonCannotEdit : '', ]} textStyle={localStyles.saveButtonTextStyle} @@ -404,7 +404,7 @@ export const VaccinationEventComponent = ({ style={[ localStyles.saveButton, isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent - ? localStyles.saveButtonCantEdit + ? localStyles.saveButtonCannotEdit : '', ]} textStyle={localStyles.saveButtonTextStyle} @@ -419,7 +419,7 @@ export const VaccinationEventComponent = ({ style={[ localStyles.saveButton, isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent - ? localStyles.saveButtonCantEdit + ? localStyles.saveButtonCannotEdit : '', ]} textStyle={localStyles.saveButtonTextStyle} @@ -531,7 +531,7 @@ const localStyles = StyleSheet.create({ backgroundColor: SUSSOL_ORANGE, alignSelf: 'center', }, - saveButtonCantEdit: { + saveButtonCannotEdit: { backgroundColor: WARMER_GREY, }, saveButtonTextStyle: { From 1412a45977a09ab97b213777f68f86c00ac928c6 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Wed, 14 Sep 2022 12:30:39 +0545 Subject: [PATCH 126/131] Make styles better --- src/widgets/modals/VaccinationEvent.js | 87 +++++++++++--------------- 1 file changed, 38 insertions(+), 49 deletions(-) diff --git a/src/widgets/modals/VaccinationEvent.js b/src/widgets/modals/VaccinationEvent.js index 53fc929ad..de22dfa0e 100644 --- a/src/widgets/modals/VaccinationEvent.js +++ b/src/widgets/modals/VaccinationEvent.js @@ -1,7 +1,7 @@ /* eslint-disable react/jsx-curly-newline */ /* eslint-disable react/forbid-prop-types */ import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { View, StyleSheet, Text, ToastAndroid } from 'react-native'; +import { View, Text, ToastAndroid } from 'react-native'; import PropTypes from 'prop-types'; import { batch, connect } from 'react-redux'; import { FlexRow } from '../FlexRow'; @@ -246,14 +246,18 @@ export const VaccinationEventComponent = ({ ); } + const isDisabled = isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent; + + const styles = getStyles({ isDisabled }); + return ( <FlexView> - <FlexRow flex={1} style={localStyles.formContainer}> + <FlexRow flex={1} style={styles.formContainer}> <FlexRow flex={1}> - <View style={localStyles.formContainer}> + <View style={styles.formContainer}> {!!surveySchema && !!surveyForm ? ( <FlexRow flex={1}> - <View style={localStyles.formContainer}> + <View style={styles.formContainer}> <JSONForm ref={pcdFormRef} formData={surveyForm.data ?? null} @@ -264,7 +268,7 @@ export const VaccinationEventComponent = ({ isPCDValid: validator(changed.formData), }); }} - disabled={isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent} + disabled={isDisabled} > <></> </JSONForm> @@ -276,7 +280,7 @@ export const VaccinationEventComponent = ({ </View> </FlexRow> <FlexRow flex={1}> - <View style={localStyles.formContainer}> + <View style={styles.formContainer}> {!!supplementalDataSchema && !!updatedSupplementalDataForm ? ( <Paper // eslint-disable-next-line prettier/prettier @@ -289,7 +293,7 @@ export const VaccinationEventComponent = ({ )} contentContainerStyle={{ flex: 1 }} style={{ flex: 1 }} - headerContainerStyle={localStyles.title} + headerContainerStyle={styles.title} > <JSONForm ref={supplementalFormRef} @@ -301,7 +305,7 @@ export const VaccinationEventComponent = ({ isSupplementalDataValid: validator(changed.formData), }); }} - disabled={isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent} + disabled={isDisabled} > <></> </JSONForm> @@ -313,14 +317,14 @@ export const VaccinationEventComponent = ({ </FlexRow> {!!vaccinationEventSchema && !!vaccinationEvent && !!parsedVaccinationEvent && ( <FlexRow flex={1}> - <View style={localStyles.formContainer}> + <View style={styles.formContainer}> <Paper Header={ <Title title={vaccineStrings.vaccine_event_transact_data_title} size="large" /> } contentContainerStyle={{ flex: 1 }} style={{ flex: 1 }} - headerContainerStyle={localStyles.title} + headerContainerStyle={styles.title} > {!isEditingTransaction ? ( <JSONForm @@ -346,7 +350,7 @@ export const VaccinationEventComponent = ({ <Title title={vaccineStrings.vaccines} size="medium" /> <DropDown isDisabled={vaccineDropDownValues.length === 0} - style={(localStyles.dropdown, { width: null, flex: 1 })} + style={(styles.dropdown, { width: null, flex: 1 })} values={vaccineDropDownValues.map(item => item.name)} onValueChange={(_, i) => { const selectedVaccine = vaccines.find( @@ -361,8 +365,8 @@ export const VaccinationEventComponent = ({ <PageButton text={buttonStrings.delete_vaccination_event} onPress={toggleDeleteModal} - style={localStyles.deleteButton} - textStyle={localStyles.saveButtonTextStyle} + style={styles.deleteButton} + textStyle={styles.saveButtonTextStyle} /> </FlexRow> <FlexRow flex={1} style={{ marginBottom: 10 }}> @@ -385,13 +389,8 @@ export const VaccinationEventComponent = ({ onPress={() => !isCurrentStoreVaccineEvent ? toggleModal() : savePCDForm(surveyForm, updatedPcdForm) } - style={[ - localStyles.saveButton, - isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent - ? localStyles.saveButtonCannotEdit - : '', - ]} - textStyle={localStyles.saveButtonTextStyle} + style={styles.saveButton} + textStyle={styles.saveButtonTextStyle} isDisabled={!isPCDValid || isDeletedVaccinationEvent || !(!!surveySchema && !!surveyForm)} /> <PageButton @@ -401,13 +400,8 @@ export const VaccinationEventComponent = ({ ? toggleModal() : saveSupplementalData(vaccinationEventNameNote, updatedSupplementalDataForm) } - style={[ - localStyles.saveButton, - isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent - ? localStyles.saveButtonCannotEdit - : '', - ]} - textStyle={localStyles.saveButtonTextStyle} + style={styles.saveButton} + textStyle={styles.saveButtonTextStyle} isDisabled={ !isSupplementalDataValid || isDeletedVaccinationEvent || @@ -416,13 +410,8 @@ export const VaccinationEventComponent = ({ /> <PageButton text={isEditingTransaction ? buttonStrings.save_changes : buttonStrings.edit} - style={[ - localStyles.saveButton, - isDeletedVaccinationEvent || !isCurrentStoreVaccineEvent - ? localStyles.saveButtonCannotEdit - : '', - ]} - textStyle={localStyles.saveButtonTextStyle} + style={styles.saveButton} + textStyle={styles.saveButtonTextStyle} onPress={isEditingTransaction ? trySave : tryEdit} isDisabled={isDeletedVaccinationEvent} /> @@ -517,7 +506,7 @@ const mapDispatchToProps = dispatch => { return { editTransaction, savePCDForm, saveSupplementalData, deleteVaccinationEvent }; }; -const localStyles = StyleSheet.create({ +const getStyles = context => ({ dropdown: { height: 35, marginTop: 0, marginBottom: 0, marginLeft: 0 }, formContainer: { flex: 1, @@ -525,20 +514,6 @@ const localStyles = StyleSheet.create({ backgroundColor: 'white', alignItems: 'stretch', }, - saveButton: { - ...globalStyles.button, - flex: 1, - backgroundColor: SUSSOL_ORANGE, - alignSelf: 'center', - }, - saveButtonCannotEdit: { - backgroundColor: WARMER_GREY, - }, - saveButtonTextStyle: { - ...globalStyles.buttonText, - color: 'white', - fontSize: 14, - }, title: { textAlignVertical: 'center', fontWeight: 'bold', @@ -552,6 +527,20 @@ const localStyles = StyleSheet.create({ backgroundColor: DANGER_RED, alignSelf: 'center', }, + saveButton: [ + { + ...globalStyles.button, + flex: 1, + backgroundColor: SUSSOL_ORANGE, + alignSelf: 'center', + }, + context.isDisabled && { backgroundColor: WARMER_GREY }, + ], + saveButtonTextStyle: { + ...globalStyles.buttonText, + color: 'white', + fontSize: 14, + }, }); VaccinationEventComponent.defaultProps = { From ed35ca45958abbd07c49b2f96ea64ceb8be3ce91 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Thu, 15 Sep 2022 15:36:50 +0545 Subject: [PATCH 127/131] Bump version up --- android/app/build.gradle | 4 ++-- package.json | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 965902532..5ea122444 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -116,13 +116,13 @@ def jscFlavor = 'org.webkit:android-jsc:+' def enableHermes = project.ext.react.get("enableHermes", false); def getAppVersion() { - def inputFile = new File("../package.json") + def inputFile = new File("${project.projectDir}/../../package.json") def packageJson = new JsonSlurper().parseText(inputFile.text) return packageJson["version"] } def getAppName() { - def inputFile = new File("../package.json") + def inputFile = new File("${project.projectDir}/../../package.json") def packageJson = new JsonSlurper().parseText(inputFile.text) return packageJson["name"] } diff --git a/package.json b/package.json index ef0d72a3e..d09cfbd5d 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ }, "name": "mSupplyMobile", "//": "version must be in the format ${majorNumber}.${minorNumber}.${patchNumber}-rc${releaseCandidateNumber}", - "version": "8.5.0", + "version": "8.6.0", "private": false, "license": "MIT", "description": "Mobile app for use with the mSupply medical inventory control software", @@ -16,7 +16,8 @@ "url": "http://github.com/sussol/mobile", "homepage": "http://msupply.org.nz", "scripts": { - "start": "react-native run-android", + "start": "react-native start", + "android": "react-native run-android", "log": "react-native log-android", "shake": "adb shell input keyevent KEYCODE_MENU", "rr": "adb shell input text \"RR\"", From 6224577cc0405b0759cf20dffeb06e646e2cee61 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Thu, 15 Sep 2022 12:40:22 +0545 Subject: [PATCH 128/131] Fix datepicker issues --- src/widgets/JSONForm/widgets/DatePicker.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/widgets/JSONForm/widgets/DatePicker.js b/src/widgets/JSONForm/widgets/DatePicker.js index 7f760e605..154632274 100644 --- a/src/widgets/JSONForm/widgets/DatePicker.js +++ b/src/widgets/JSONForm/widgets/DatePicker.js @@ -22,7 +22,7 @@ export const DatePicker = ({ const { focusController } = useJSONFormOptions(); const ref = focusController.useRegisteredRef(); - const [selectedDate, setSelectedDate] = useState(''); + const [selectedTextValue, setSelectedTextValue] = useState(''); const handleChange = dateString => { onChange(dateString); @@ -35,7 +35,9 @@ export const DatePicker = ({ ? alternateFormatDate : moment(value, DATE_FORMAT.DD_MM_YYYY, true); - setSelectedDate(expectedFormatDate.isValid() ? expectedFormatDate.toDate() : value); + setSelectedTextValue( + expectedFormatDate.isValid() ? expectedFormatDate.format(DATE_FORMAT.DD_MM_YYYY) : value + ); }, [value]); return ( @@ -46,11 +48,7 @@ export const DatePicker = ({ underlineColorAndroid={DARKER_GREY} placeholder={placeholder} editable={!(readonly || disabled)} - value={ - moment(selectedDate, DATE_FORMAT.DD_MM_YYYY, true).isValid() - ? moment(selectedDate).format(DATE_FORMAT.DD_MM_YYYY) - : selectedDate - } + value={selectedTextValue} ref={ref} onSubmitEditing={() => focusController.next(ref)} onChangeText={handleChange} @@ -61,7 +59,11 @@ export const DatePicker = ({ /> <DatePickerButton isDisabled={readonly || disabled} - initialValue={selectedDate || moment().toDate()} + initialValue={ + moment(selectedTextValue, DATE_FORMAT.DD_MM_YYYY, true).isValid() + ? moment(selectedTextValue, DATE_FORMAT.DD_MM_YYYY, true) + : moment().toDate() + } minimumDate={options.dateRange === 'future' ? new Date() : null} maximumDate={options.dateRange === 'past' ? new Date() : null} onDateChanged={date => handleChange(moment(date).format(DATE_FORMAT.DD_MM_YYYY))} From e1fdd29c37f25379523503c2a12af60d38e81916 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Thu, 15 Sep 2022 12:57:22 +0545 Subject: [PATCH 129/131] Need date as type not moment --- src/widgets/JSONForm/widgets/DatePicker.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/widgets/JSONForm/widgets/DatePicker.js b/src/widgets/JSONForm/widgets/DatePicker.js index 154632274..dfb539a48 100644 --- a/src/widgets/JSONForm/widgets/DatePicker.js +++ b/src/widgets/JSONForm/widgets/DatePicker.js @@ -61,7 +61,7 @@ export const DatePicker = ({ isDisabled={readonly || disabled} initialValue={ moment(selectedTextValue, DATE_FORMAT.DD_MM_YYYY, true).isValid() - ? moment(selectedTextValue, DATE_FORMAT.DD_MM_YYYY, true) + ? moment(selectedTextValue, DATE_FORMAT.DD_MM_YYYY, true).toDate() : moment().toDate() } minimumDate={options.dateRange === 'future' ? new Date() : null} From 3924ad6a657eecf66474935a70d529edb521d28f Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Thu, 15 Sep 2022 14:52:50 +0545 Subject: [PATCH 130/131] Disallow non DD/MM/YYYY format for DOB --- src/widgets/JSONForm/widgets/DatePicker.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/widgets/JSONForm/widgets/DatePicker.js b/src/widgets/JSONForm/widgets/DatePicker.js index dfb539a48..056a3bdce 100644 --- a/src/widgets/JSONForm/widgets/DatePicker.js +++ b/src/widgets/JSONForm/widgets/DatePicker.js @@ -1,3 +1,4 @@ +/* eslint-disable prettier/prettier */ import React, { useState } from 'react'; import { TextInput } from 'react-native'; import moment from 'moment'; @@ -60,7 +61,10 @@ export const DatePicker = ({ <DatePickerButton isDisabled={readonly || disabled} initialValue={ - moment(selectedTextValue, DATE_FORMAT.DD_MM_YYYY, true).isValid() + moment(selectedTextValue, DATE_FORMAT.DD_MM_YYYY, true).isValid() || + moment(selectedTextValue, 'D/M/YYYY', true).isValid() || + moment(selectedTextValue, 'D/MM/YYYY', true).isValid() || + moment(selectedTextValue, 'DD/M/YYYY', true).isValid() ? moment(selectedTextValue, DATE_FORMAT.DD_MM_YYYY, true).toDate() : moment().toDate() } From 4f1c30371f6d3c51cee0dc4ddd6bdc0810128fd8 Mon Sep 17 00:00:00 2001 From: Sworup Shakya <sworup@users.noreply.github.com> Date: Thu, 15 Sep 2022 15:35:24 +0545 Subject: [PATCH 131/131] Fix #4892 DOB field of Patient edit is showing invalid if we let dob update with age field --- src/widgets/FormInputs/FormDOBInput.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/widgets/FormInputs/FormDOBInput.js b/src/widgets/FormInputs/FormDOBInput.js index d3a4b0c84..b30968fed 100644 --- a/src/widgets/FormInputs/FormDOBInput.js +++ b/src/widgets/FormInputs/FormDOBInput.js @@ -132,6 +132,7 @@ const useDobInput = (initialValue, onChangeDate, onValidate) => { const onTypeAge = newAge => { const asNumber = Number(newAge); const isValid = !Number.isNaN(asNumber) && asNumber < 500; + onValidate(newAge); typedAge(newAge, isValid); onChangeDate(moment().subtract(asNumber, 'years').toDate()); };