From 19e955c08dfdb0841787fc379c9f69ea02c695a4 Mon Sep 17 00:00:00 2001 From: Lache Melvin Date: Fri, 9 Feb 2024 08:44:44 +1300 Subject: [PATCH 1/6] update helper types --- .../src/Admin/EditEntity/helpers.test.ts | 69 ++++++++++++++----- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/frontend/system/src/Admin/EditEntity/helpers.test.ts b/frontend/system/src/Admin/EditEntity/helpers.test.ts index 12c6e3e5d..518b44604 100644 --- a/frontend/system/src/Admin/EditEntity/helpers.test.ts +++ b/frontend/system/src/Admin/EditEntity/helpers.test.ts @@ -9,19 +9,25 @@ import { isValidDrugInput, isValidVaccineInput, } from './helpers'; -import { ConsumableInput, DrugInput, VaccineInput } from './types'; +import { + ConsumableInput, + DrugInput, + EntityDetails, + VaccineInput, +} from './types'; describe('getAllEntityCodes', () => { it('returns empty array when entity is not defined', () => { expect(getAllEntityCodes(undefined)).toMatchObject([]); }); it('returns array of codes with empty strings filtered out', () => { - const entityDetails = { + const entityDetails: EntityDetails = { code: '7c8c2b5b', name: 'Acetic Acid', type: 'drug', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '6e5f7a00', @@ -29,6 +35,7 @@ describe('getAllEntityCodes', () => { type: 'Route', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '', @@ -37,6 +44,7 @@ describe('getAllEntityCodes', () => { alternativeNames: [], properties: [], children: [], + barcodes: [], }, ], }, @@ -48,7 +56,7 @@ describe('getAllEntityCodes', () => { ]); }); it('returns codes of entity and all its children', () => { - const entityDetails = { + const entityDetails: EntityDetails = { code: '7c8c2b5b', name: 'Acetic Acid', type: 'drug', @@ -59,6 +67,7 @@ describe('getAllEntityCodes', () => { }, ], properties: [], + barcodes: [], children: [ { code: '6e5f7a00', @@ -66,6 +75,7 @@ describe('getAllEntityCodes', () => { type: 'Route', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '66e85500', @@ -73,6 +83,7 @@ describe('getAllEntityCodes', () => { type: 'Form', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '36e874bf', @@ -80,6 +91,7 @@ describe('getAllEntityCodes', () => { type: 'DoseStrength', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: 'e4edcb00', @@ -87,6 +99,7 @@ describe('getAllEntityCodes', () => { type: 'Unit', alternativeNames: [], properties: [], + barcodes: [], }, ], }, @@ -96,6 +109,7 @@ describe('getAllEntityCodes', () => { type: 'DoseStrength', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: 'e4edcb01', @@ -103,6 +117,7 @@ describe('getAllEntityCodes', () => { type: 'Unit', alternativeNames: [], properties: [], + barcodes: [], }, ], }, @@ -130,7 +145,7 @@ describe('getAllEntityCodes', () => { describe('buildDrugInputFromEntity', () => { it('builds input from entity details', () => { - const entityDetails = { + const entityDetails: EntityDetails = { code: '7c8c2b5b', name: 'Acetic Acid', type: 'drug', @@ -141,6 +156,7 @@ describe('buildDrugInputFromEntity', () => { }, ], properties: [], + barcodes: [], children: [ { code: '6e5f7a00', @@ -148,6 +164,7 @@ describe('buildDrugInputFromEntity', () => { type: 'Route', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '66e85500', @@ -155,6 +172,7 @@ describe('buildDrugInputFromEntity', () => { type: 'Form', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '36e874bf', @@ -162,6 +180,7 @@ describe('buildDrugInputFromEntity', () => { type: 'DoseStrength', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: 'e4edcb00', @@ -169,6 +188,7 @@ describe('buildDrugInputFromEntity', () => { type: 'Unit', alternativeNames: [], properties: [], + barcodes: [], }, ], }, @@ -230,36 +250,33 @@ describe('buildDrugInputFromEntity', () => { }); it('includes properties', () => { - const entityDetails = { - id: '7c8c2b5b', + const entityDetails: EntityDetails = { code: '7c8c2b5b', name: 'Acetic Acid', type: 'drug', alternativeNames: [], properties: [ { - id: '7c8c2b5b_who_eml', code: '7c8c2b5b_who_eml', type: 'who_eml', value: '28', }, { - id: '7c8c2b5b_code_unspsc', code: '7c8c2b5b_code_unspsc', type: 'code_unspsc', value: '51471602', }, ], + barcodes: [], children: [ { - id: '6e5f7a00', code: '6e5f7a00', name: 'Topical', type: 'Route', alternativeNames: [], + barcodes: [], properties: [ { - id: '6e5f7a00_code_rxnav', code: '6e5f7a00_code_rxnav', type: 'code_rxnav', value: '168', @@ -316,12 +333,13 @@ describe('buildDrugInputFromEntity', () => { // for now we'll just ignore nodes that show up at strange levels, until we decide whether/where we // support breaking the hierarchy structure it('only includes children in the result if they are for the correct level of the hierarchy', () => { - const entityDetails = { + const entityDetails: EntityDetails = { code: '7c8c2b5b', name: 'Acetic Acid', type: 'drug', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '6e5f7a00', @@ -329,6 +347,7 @@ describe('buildDrugInputFromEntity', () => { type: 'Route', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '66e85500', @@ -337,6 +356,7 @@ describe('buildDrugInputFromEntity', () => { alternativeNames: [], properties: [], children: [], + barcodes: [], }, { code: 'e4edcb00', @@ -345,6 +365,7 @@ describe('buildDrugInputFromEntity', () => { alternativeNames: [], properties: [], children: [], + barcodes: [], }, ], }, @@ -382,12 +403,13 @@ describe('buildDrugInputFromEntity', () => { describe('buildVaccineInputFromEntity', () => { it('builds input from entity details', () => { - const entityDetails = { + const entityDetails: EntityDetails = { code: '7c8c2b5b', name: 'Some Vaccine', type: 'Vaccine', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '6e5f7a00', @@ -395,15 +417,16 @@ describe('buildVaccineInputFromEntity', () => { type: 'Route', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '66e85500', name: 'Injection: suspension', type: 'Form', alternativeNames: [], + barcodes: [], properties: [ { - id: '6e5f7a00_code_rxnav', code: '6e5f7a00_code_rxnav', type: 'code_rxnav', value: '168', @@ -416,6 +439,7 @@ describe('buildVaccineInputFromEntity', () => { type: EntityType.VaccineNameDetails, alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '7e5f7a02', @@ -423,6 +447,7 @@ describe('buildVaccineInputFromEntity', () => { type: EntityType.ActiveIngredients, alternativeNames: [], properties: [], + barcodes: [], children: [ { code: 'a6e85500', @@ -430,6 +455,7 @@ describe('buildVaccineInputFromEntity', () => { type: 'Brand', alternativeNames: [], properties: [], + barcodes: [], children: [], }, ], @@ -442,12 +468,14 @@ describe('buildVaccineInputFromEntity', () => { type: EntityType.ActiveIngredients, alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '86e85500', name: 'Brand 1', type: 'Brand', alternativeNames: [], + barcodes: [], properties: [], children: [ { @@ -456,6 +484,7 @@ describe('buildVaccineInputFromEntity', () => { type: 'DoseStrength', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: 'e4edcb00', @@ -463,6 +492,7 @@ describe('buildVaccineInputFromEntity', () => { type: 'Unit', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: 'x4edcb00', @@ -470,6 +500,7 @@ describe('buildVaccineInputFromEntity', () => { type: 'PackImmediate', alternativeNames: [], properties: [], + barcodes: [], }, ], }, @@ -591,12 +622,13 @@ describe('buildVaccineInputFromEntity', () => { // for now we'll just ignore nodes that show up at strange levels, until we decide whether/where we // support breaking the hierarchy structure it('only includes children in the result if they are for the correct level of the hierarchy', () => { - const entityDetails = { + const entityDetails: EntityDetails = { code: '7c8c2b5b', name: 'Some Vaccine', type: 'Vaccine', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '6e5f7a00', @@ -604,6 +636,7 @@ describe('buildVaccineInputFromEntity', () => { type: 'Route', // include: valid child alternativeNames: [], properties: [], + barcodes: [], }, { code: 'e4edcb00', @@ -611,6 +644,7 @@ describe('buildVaccineInputFromEntity', () => { type: 'Unit', // exclude: invalid (should be deeper in the tree) alternativeNames: [], properties: [], + barcodes: [], }, ], }; @@ -638,21 +672,22 @@ describe('buildVaccineInputFromEntity', () => { describe('buildConsumableInputFromEntity', () => { it('builds input from entity details', () => { - const entityDetails = { + const entityDetails: EntityDetails = { code: '7c8c2b5b', name: 'Examination Glove', type: 'Consumable', alternativeNames: [], properties: [], + barcodes: [], children: [ { code: '7e5f7a00', name: 'Large', type: 'Presentation', alternativeNames: [], + barcodes: [], properties: [ { - id: '6e5f7a00_code_rxnav', code: '6e5f7a00_code_rxnav', type: 'code_rxnav', value: '168', @@ -666,6 +701,7 @@ describe('buildConsumableInputFromEntity', () => { alternativeNames: [], properties: [], children: [], + barcodes: [], }, { code: '76e85501', @@ -674,6 +710,7 @@ describe('buildConsumableInputFromEntity', () => { alternativeNames: [], properties: [], children: [], + barcodes: [], }, ], }, From a66e7bbe81a9f6387c5890231788ebfc30cd175e Mon Sep 17 00:00:00 2001 From: Lache Melvin Date: Fri, 9 Feb 2024 09:02:02 +1300 Subject: [PATCH 2/6] add packsizes to consumable input --- .../components/ConsumableFormTree.tsx | 8 +++- .../src/Admin/EditEntity/helpers.test.ts | 37 +++++++++++++++++-- .../system/src/Admin/EditEntity/helpers.ts | 14 +++++++ frontend/system/src/Admin/EditEntity/types.ts | 5 ++- 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/frontend/system/src/Admin/EditEntity/components/ConsumableFormTree.tsx b/frontend/system/src/Admin/EditEntity/components/ConsumableFormTree.tsx index ccb42920a..60805c195 100644 --- a/frontend/system/src/Admin/EditEntity/components/ConsumableFormTree.tsx +++ b/frontend/system/src/Admin/EditEntity/components/ConsumableFormTree.tsx @@ -189,6 +189,7 @@ export const ConsumableFormTree = ({ { id: uuid(), name: '', + packSizes: [], }, pres.extraDescriptions ) @@ -225,7 +226,7 @@ export const ConsumableFormTree = ({ label={t('label.add-presentation')} onClick={() => onUpdate( - { id: uuid(), name: '', extraDescriptions: [] }, + { id: uuid(), name: '', extraDescriptions: [], packSizes: [] }, draft.presentations ) } @@ -233,7 +234,10 @@ export const ConsumableFormTree = ({ - onUpdate({ id: uuid(), name: '' }, draft.extraDescriptions) + onUpdate( + { id: uuid(), name: '', packSizes: [] }, + draft.extraDescriptions + ) } /> diff --git a/frontend/system/src/Admin/EditEntity/helpers.test.ts b/frontend/system/src/Admin/EditEntity/helpers.test.ts index 518b44604..29884c3cd 100644 --- a/frontend/system/src/Admin/EditEntity/helpers.test.ts +++ b/frontend/system/src/Admin/EditEntity/helpers.test.ts @@ -700,8 +700,18 @@ describe('buildConsumableInputFromEntity', () => { type: 'ExtraDescription', alternativeNames: [], properties: [], - children: [], barcodes: [], + children: [ + { + code: 'b1234500', + name: '50 pack', + type: EntityType.PackSize, + alternativeNames: [], + properties: [], + children: [], + barcodes: [], + }, + ], }, { code: '76e85501', @@ -738,18 +748,28 @@ describe('buildConsumableInputFromEntity', () => { value: '168', }, ], + packSizes: [], extraDescriptions: [ { id: '86e85500', code: '86e85500', name: 'Pink', properties: [], + packSizes: [ + { + id: 'b1234500', + code: 'b1234500', + name: '50 pack', + properties: [], + }, + ], }, { id: '76e85501', code: '76e85501', name: 'Black', properties: [], + packSizes: [], }, ], }, @@ -772,6 +792,7 @@ describe('buildEntityFromConsumableInput', () => { id: '7e5f7a00', code: '7e5f7a00', name: 'Large', + packSizes: [], properties: [ { id: '6e5f7a00_code_rxnav', @@ -786,12 +807,14 @@ describe('buildEntityFromConsumableInput', () => { code: '86e85500', name: 'Pink', properties: [], + packSizes: [], }, { id: '76e85501', code: '76e85501', name: 'Black', properties: [], + packSizes: [], }, ], }, @@ -802,6 +825,7 @@ describe('buildEntityFromConsumableInput', () => { code: '36e85501', name: 'Bundled', properties: [], + packSizes: [], }, ], }; @@ -1127,6 +1151,7 @@ describe('isValidConsumableInput', () => { id: '7e5f7a00', code: '7e5f7a00', name: 'Large', + packSizes: [], properties: [ { id: '6e5f7a00_code_rxnav', @@ -1141,12 +1166,14 @@ describe('isValidConsumableInput', () => { code: '86e85500', name: 'Pink', properties: [], + packSizes: [], }, { id: '76e85501', code: '76e85501', name: 'Black', properties: [], + packSizes: [], }, ], }, @@ -1157,6 +1184,7 @@ describe('isValidConsumableInput', () => { code: '36e85501', name: 'Bundled', properties: [], + packSizes: [], }, ], }; @@ -1167,7 +1195,7 @@ describe('isValidConsumableInput', () => { }); it('returns false when a field is missing a name', () => { - const consumableInput = { + const consumableInput: ConsumableInput = { id: '7c8c2b5b', name: 'Examination Glove', properties: [], @@ -1179,6 +1207,7 @@ describe('isValidConsumableInput', () => { name: '', properties: [], extraDescriptions: [], + packSizes: [], }, ], }; @@ -1189,7 +1218,7 @@ describe('isValidConsumableInput', () => { }); it('returns false when there is a duplicate name', () => { - const consumableInput = { + const consumableInput: ConsumableInput = { id: '7c8c2b5b', name: 'Examination Glove', properties: [], @@ -1201,12 +1230,14 @@ describe('isValidConsumableInput', () => { name: 'SAME', properties: [], extraDescriptions: [], + packSizes: [], }, { id: '7e5f7a00', name: 'SAME', properties: [], extraDescriptions: [], + packSizes: [], }, ], }; diff --git a/frontend/system/src/Admin/EditEntity/helpers.ts b/frontend/system/src/Admin/EditEntity/helpers.ts index e85908812..35914b5f7 100644 --- a/frontend/system/src/Admin/EditEntity/helpers.ts +++ b/frontend/system/src/Admin/EditEntity/helpers.ts @@ -319,6 +319,12 @@ export const buildConsumableInputFromEntity = ( ?.filter(pres => pres.type === EntityType.Presentation) .map(pres => ({ ...getDetails(pres), + packSizes: + pres.children + ?.filter(packSize => packSize.type === EntityType.PackSize) + .map(packSize => ({ + ...getDetails(packSize), + })) || [], extraDescriptions: pres.children ?.filter( @@ -326,6 +332,10 @@ export const buildConsumableInputFromEntity = ( ) .map(description => ({ ...getDetails(description), + packSizes: + description.children + ?.filter(packSize => packSize.type === EntityType.PackSize) + .map(packSize => getDetails(packSize)) || [], })) || [], })) || [], extraDescriptions: @@ -335,6 +345,10 @@ export const buildConsumableInputFromEntity = ( ) .map(description => ({ ...getDetails(description), + packSizes: + description.children + ?.filter(packSize => packSize.type === EntityType.PackSize) + .map(packSize => getDetails(packSize)) || [], })) || [], alternativeNames: entity.alternativeNames.map(mapAltName), }; diff --git a/frontend/system/src/Admin/EditEntity/types.ts b/frontend/system/src/Admin/EditEntity/types.ts index 15c56abc7..1efbead0a 100644 --- a/frontend/system/src/Admin/EditEntity/types.ts +++ b/frontend/system/src/Admin/EditEntity/types.ts @@ -47,9 +47,12 @@ export interface DrugInput extends Entity { routes: Route[]; } -export interface ExtraDescription extends Entity {} +export interface ExtraDescription extends Entity { + packSizes: PackSize[]; +} export interface Presentation extends Entity { extraDescriptions: ExtraDescription[]; + packSizes: PackSize[]; } export interface ConsumableInput extends Entity { alternativeNames: AlternativeName[]; From 4592aa231f4ed25630c198424f04deed80e737de Mon Sep 17 00:00:00 2001 From: Lache Melvin Date: Fri, 9 Feb 2024 09:25:59 +1300 Subject: [PATCH 3/6] update consumable form tree to include pack sizes --- .../components/ConsumableFormTree.tsx | 196 ++++++++++++------ 1 file changed, 130 insertions(+), 66 deletions(-) diff --git a/frontend/system/src/Admin/EditEntity/components/ConsumableFormTree.tsx b/frontend/system/src/Admin/EditEntity/components/ConsumableFormTree.tsx index 60805c195..a77112e65 100644 --- a/frontend/system/src/Admin/EditEntity/components/ConsumableFormTree.tsx +++ b/frontend/system/src/Admin/EditEntity/components/ConsumableFormTree.tsx @@ -4,7 +4,7 @@ import React, { useState } from 'react'; import { useUuid } from '../../../hooks'; import { PropertiesModal } from './PropertiesModal'; import { useEditModal } from '@common/hooks'; -import { ConsumableInput, Entity, Property } from '../types'; +import { ConsumableInput, Entity, Presentation, Property } from '../types'; import { TreeFormBox } from './TreeFormBox'; import { AddFieldButton } from './AddFieldButton'; import { EditPropertiesButton } from './EditPropertiesButton'; @@ -156,26 +156,33 @@ export const ConsumableFormTree = ({ /> - {!!pres.extraDescriptions.length && ( - - {t('label.extra-descriptions')} - + + + {!!pres.packSizes.length && ( + {t('label.pack-sizes')} )} - {pres.extraDescriptions.map(description => ( - + {pres.packSizes.map(packSize => ( + @@ -183,64 +190,29 @@ export const ConsumableFormTree = ({ ))} - onUpdate( - { - id: uuid(), - name: '', - packSizes: [], - }, - pres.extraDescriptions - ) - } + label={t('label.add-pack-size')} + onClick={() => onUpdate({ id: uuid(), name: '' }, pres.packSizes)} /> ))} - {draft.extraDescriptions.map(description => ( - - - - - - - ))} - - - onUpdate( - { id: uuid(), name: '', extraDescriptions: [], packSizes: [] }, - draft.presentations - ) - } - /> - - onUpdate( - { id: uuid(), name: '', packSizes: [] }, - draft.extraDescriptions - ) - } - /> - + + onUpdate( + { id: uuid(), name: '', extraDescriptions: [], packSizes: [] }, + draft.presentations + ) + } + /> + + {!!draft.alternativeNames.length && ( @@ -272,3 +244,95 @@ export const ConsumableFormTree = ({ ); }; + +const ExtraDescriptions = ({ + parent, + grandparents, + isDisabled, + onUpdate, + onDelete, + onOpenPropertiesModal, +}: { + parent: Presentation | ConsumableInput; + grandparents: Entity[]; + isDisabled: (id: string) => boolean; + onUpdate: (updated: T, list: T[]) => void; + onDelete: (toDelete: T, list: T[]) => void; + onOpenPropertiesModal: (title: string, entityToUpdate: Entity) => void; +}) => { + const uuid = useUuid(); + const t = useTranslation('system'); + + return ( + + {!!parent.extraDescriptions.length && ( + {t('label.extra-descriptions')} + )} + + {parent.extraDescriptions.map(description => ( + + + + + + + {!!description.packSizes.length && ( + {t('label.pack-sizes')} + )} + + {description.packSizes.map(packSize => ( + + + + + + + ))} + + + onUpdate({ id: uuid(), name: '' }, description.packSizes) + } + /> + + ))} + + + onUpdate( + { + id: uuid(), + name: '', + packSizes: [], + }, + parent.extraDescriptions + ) + } + /> + + ); +}; From 635ca10d23e626e492e18df5858c646339c98d56 Mon Sep 17 00:00:00 2001 From: Lache Melvin Date: Fri, 9 Feb 2024 11:04:26 +1300 Subject: [PATCH 4/6] map pack sizes in buildEntityFromConsumableInput --- .../src/Admin/EditEntity/helpers.test.ts | 56 ++++++++++++++++++- .../system/src/Admin/EditEntity/helpers.ts | 42 +++++++++++--- 2 files changed, 87 insertions(+), 11 deletions(-) diff --git a/frontend/system/src/Admin/EditEntity/helpers.test.ts b/frontend/system/src/Admin/EditEntity/helpers.test.ts index 29884c3cd..281db7b81 100644 --- a/frontend/system/src/Admin/EditEntity/helpers.test.ts +++ b/frontend/system/src/Admin/EditEntity/helpers.test.ts @@ -792,7 +792,14 @@ describe('buildEntityFromConsumableInput', () => { id: '7e5f7a00', code: '7e5f7a00', name: 'Large', - packSizes: [], + packSizes: [ + { + id: 'cba12345', + code: 'cba12345', + name: '10pack', + properties: [], + }, + ], properties: [ { id: '6e5f7a00_code_rxnav', @@ -807,7 +814,14 @@ describe('buildEntityFromConsumableInput', () => { code: '86e85500', name: 'Pink', properties: [], - packSizes: [], + packSizes: [ + { + id: 'abc12345', + code: 'abc12345', + name: '10pack', + properties: [], + }, + ], }, { id: '76e85501', @@ -825,7 +839,14 @@ describe('buildEntityFromConsumableInput', () => { code: '36e85501', name: 'Bundled', properties: [], - packSizes: [], + packSizes: [ + { + id: '2305945', + code: '2305945', + name: '15pack', + properties: [], + }, + ], }, ], }; @@ -863,6 +884,16 @@ describe('buildEntityFromConsumableInput', () => { type: 'ExtraDescription', category: 'Consumable', properties: [], + children: [ + { + code: 'abc12345', + name: '10pack', + description: 'Examination Glove Large Pink 10pack', + type: 'PackSize', + category: 'Consumable', + properties: [], + }, + ], }, { code: '76e85501', @@ -871,6 +902,15 @@ describe('buildEntityFromConsumableInput', () => { type: 'ExtraDescription', category: 'Consumable', properties: [], + children: [], + }, + { + code: 'cba12345', + name: '10pack', + description: 'Examination Glove Large 10pack', + type: 'PackSize', + category: 'Consumable', + properties: [], }, ], }, @@ -881,6 +921,16 @@ describe('buildEntityFromConsumableInput', () => { type: 'ExtraDescription', category: 'Consumable', properties: [], + children: [ + { + code: '2305945', + name: '15pack', + description: 'Examination Glove Bundled 15pack', + type: 'PackSize', + category: 'Consumable', + properties: [], + }, + ], }, ], }); diff --git a/frontend/system/src/Admin/EditEntity/helpers.ts b/frontend/system/src/Admin/EditEntity/helpers.ts index 35914b5f7..cf10f3d09 100644 --- a/frontend/system/src/Admin/EditEntity/helpers.ts +++ b/frontend/system/src/Admin/EditEntity/helpers.ts @@ -378,14 +378,32 @@ export const buildEntityFromConsumableInput = ( type: EntityType.Presentation, category: EntityCategory.Consumable, properties: pres.properties?.map(mapProperty), - children: pres.extraDescriptions?.map(description => ({ - code: description.code, - name: description.name, - description: `${consumable.name} ${pres.name} ${description.name}`, - type: EntityType.ExtraDescription, - category: EntityCategory.Consumable, - properties: description.properties?.map(mapProperty), - })), + children: [ + ...pres.extraDescriptions?.map(description => ({ + code: description.code, + name: description.name, + description: `${consumable.name} ${pres.name} ${description.name}`, + type: EntityType.ExtraDescription, + category: EntityCategory.Consumable, + properties: description.properties?.map(mapProperty), + children: description.packSizes?.map(packSize => ({ + code: packSize.code, + name: packSize.name, + description: `${consumable.name} ${pres.name} ${description.name} ${packSize.name}`, + type: EntityType.PackSize, + category: EntityCategory.Consumable, + properties: packSize.properties?.map(mapProperty), + })), + })), + ...pres.packSizes?.map(packSize => ({ + code: packSize.code, + name: packSize.name, + description: `${consumable.name} ${pres.name} ${packSize.name}`, + type: EntityType.PackSize, + category: EntityCategory.Consumable, + properties: packSize.properties?.map(mapProperty), + })), + ], })), // Extra Descriptions ...consumable.extraDescriptions.map(description => ({ @@ -395,6 +413,14 @@ export const buildEntityFromConsumableInput = ( type: EntityType.ExtraDescription, category: EntityCategory.Consumable, properties: description.properties?.map(mapProperty), + children: description.packSizes?.map(packSize => ({ + code: packSize.code, + name: packSize.name, + description: `${consumable.name} ${description.name} ${packSize.name}`, + type: EntityType.PackSize, + category: EntityCategory.Consumable, + properties: packSize.properties?.map(mapProperty), + })), })), ], }; From ff70f0adc1cd91ec8f25e713eb8ed6b74db750be Mon Sep 17 00:00:00 2001 From: Lache Melvin Date: Fri, 9 Feb 2024 11:31:30 +1300 Subject: [PATCH 5/6] ensure query captures all barcodes for an entity --- .../Barcodes/api/operations.generated.ts | 23 ++++++++++++++++++- .../Entities/Barcodes/api/operations.graphql | 21 +++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/frontend/system/src/Entities/Barcodes/api/operations.generated.ts b/frontend/system/src/Entities/Barcodes/api/operations.generated.ts index fc2c73640..1675e2d86 100644 --- a/frontend/system/src/Entities/Barcodes/api/operations.generated.ts +++ b/frontend/system/src/Entities/Barcodes/api/operations.generated.ts @@ -34,7 +34,7 @@ export type EntityWithBarcodesQueryVariables = Types.Exact<{ }>; -export type EntityWithBarcodesQuery = { __typename?: 'FullQuery', entity?: { __typename?: 'EntityType', code: string, name: string, description: string, type: string, children: Array<{ __typename?: 'EntityType', code: string, name: string, description: string, type: string, children: Array<{ __typename?: 'EntityType', code: string, name: string, description: string, type: string, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> }>, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> }>, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> } | null }; +export type EntityWithBarcodesQuery = { __typename?: 'FullQuery', entity?: { __typename?: 'EntityType', code: string, name: string, description: string, type: string, children: Array<{ __typename?: 'EntityType', code: string, name: string, description: string, type: string, children: Array<{ __typename?: 'EntityType', code: string, name: string, description: string, type: string, children: Array<{ __typename?: 'EntityType', code: string, name: string, description: string, type: string, children: Array<{ __typename?: 'EntityType', code: string, name: string, description: string, type: string, children: Array<{ __typename?: 'EntityType', code: string, name: string, description: string, type: string, children: Array<{ __typename?: 'EntityType', code: string, name: string, description: string, type: string, children: Array<{ __typename?: 'EntityType', code: string, name: string, description: string, type: string, children: Array<{ __typename?: 'EntityType', code: string, name: string, description: string, type: string, children: Array<{ __typename?: 'EntityType', code: string, name: string, description: string, type: string, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> }>, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> }>, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> }>, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> }>, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> }>, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> }>, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> }>, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> }>, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> }>, barcodes: Array<{ __typename?: 'BarcodeType', id: string, gtin: string, manufacturer: string }> } | null }; export const BarcodeFragmentDoc = gql` fragment Barcode on BarcodeNode { @@ -93,6 +93,27 @@ export const EntityWithBarcodesDocument = gql` ...EntityWithBarcodes children { ...EntityWithBarcodes + children { + ...EntityWithBarcodes + children { + ...EntityWithBarcodes + children { + ...EntityWithBarcodes + children { + ...EntityWithBarcodes + children { + ...EntityWithBarcodes + children { + ...EntityWithBarcodes + children { + ...EntityWithBarcodes + } + } + } + } + } + } + } } } } diff --git a/frontend/system/src/Entities/Barcodes/api/operations.graphql b/frontend/system/src/Entities/Barcodes/api/operations.graphql index ee06b0a44..9f2db0154 100644 --- a/frontend/system/src/Entities/Barcodes/api/operations.graphql +++ b/frontend/system/src/Entities/Barcodes/api/operations.graphql @@ -49,6 +49,27 @@ query entityWithBarcodes($code: String!) { ...EntityWithBarcodes children { ...EntityWithBarcodes + children { + ...EntityWithBarcodes + children { + ...EntityWithBarcodes + children { + ...EntityWithBarcodes + children { + ...EntityWithBarcodes + children { + ...EntityWithBarcodes + children { + ...EntityWithBarcodes + children { + ...EntityWithBarcodes + } + } + } + } + } + } + } } } } From a1eabbabede56c3f7fa27b8fc928fda98b491d8b Mon Sep 17 00:00:00 2001 From: Lache Melvin Date: Fri, 9 Feb 2024 11:44:24 +1300 Subject: [PATCH 6/6] improve consumable entity display with pack sizes --- .../system/src/Admin/EditEntity/helpers.ts | 12 ++++----- .../system/src/Entities/EntityDetails.tsx | 7 +++++- .../system/src/Entities/EntityTreeItem.tsx | 25 +++++++++++-------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/frontend/system/src/Admin/EditEntity/helpers.ts b/frontend/system/src/Admin/EditEntity/helpers.ts index cf10f3d09..8c1c2b636 100644 --- a/frontend/system/src/Admin/EditEntity/helpers.ts +++ b/frontend/system/src/Admin/EditEntity/helpers.ts @@ -319,12 +319,6 @@ export const buildConsumableInputFromEntity = ( ?.filter(pres => pres.type === EntityType.Presentation) .map(pres => ({ ...getDetails(pres), - packSizes: - pres.children - ?.filter(packSize => packSize.type === EntityType.PackSize) - .map(packSize => ({ - ...getDetails(packSize), - })) || [], extraDescriptions: pres.children ?.filter( @@ -337,6 +331,12 @@ export const buildConsumableInputFromEntity = ( ?.filter(packSize => packSize.type === EntityType.PackSize) .map(packSize => getDetails(packSize)) || [], })) || [], + packSizes: + pres.children + ?.filter(packSize => packSize.type === EntityType.PackSize) + .map(packSize => ({ + ...getDetails(packSize), + })) || [], })) || [], extraDescriptions: entity.children diff --git a/frontend/system/src/Entities/EntityDetails.tsx b/frontend/system/src/Entities/EntityDetails.tsx index c486ec74e..6e2ae650b 100644 --- a/frontend/system/src/Entities/EntityDetails.tsx +++ b/frontend/system/src/Entities/EntityDetails.tsx @@ -47,7 +47,12 @@ export const EntityDetails = () => { const expandedIds: string[] = []; const addToExpandedIds = (ent?: EntityData | null) => { - if (ent && !TYPES_TO_COLLAPSE.includes(ent.type as EntityType)) { + if ( + ent && + !TYPES_TO_COLLAPSE.includes(ent.type as EntityType) && + // don't expand if only have children that are pack sizes + !ent.children?.every(c => c.type === EntityType.PackSize) + ) { expandedIds.push(ent.code); ent.children?.forEach(addToExpandedIds); } diff --git a/frontend/system/src/Entities/EntityTreeItem.tsx b/frontend/system/src/Entities/EntityTreeItem.tsx index 98dde094f..e0f286dca 100644 --- a/frontend/system/src/Entities/EntityTreeItem.tsx +++ b/frontend/system/src/Entities/EntityTreeItem.tsx @@ -40,11 +40,13 @@ export const EntityTreeItem = ({ const isLeaf = !entity.children?.length; const showCode = showAllCodes || - isLeaf || + (isLeaf && entity.type !== EntityType.PackSize) || // PackSizes are leaf nodes but we don't care about their codes by default, just their link to the barcodes page highlightCode === entity.code || // mSupply users will usually want these codes: entity.type === EntityType.Strength || - entity.type === EntityType.Unit; + entity.type === EntityType.Unit || + entity.type === EntityType.ExtraDescription || + entity.type === EntityType.Presentation; // use default chevron icons, unless we're looking at a leaf node with no properties const customIcons = @@ -95,14 +97,17 @@ export const EntityTreeItem = ({ } > - {entity.children?.map(c => ( - - ))} + {entity.children + // we have some entities that have multiple types of children - we want to group the same types together! + ?.sort((a, b) => (a.type > b.type ? 1 : a.type < b.type ? -1 : 0)) + .map(c => ( + + ))} {!!entity.properties.length && (