@palladium-ethiopia/esm-clinical-workflow-app 5.4.2-pre.34 → 5.4.2-pre.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/dist/164.js +1 -1
  3. package/dist/164.js.map +1 -1
  4. package/dist/{160.js → 693.js} +1 -1
  5. package/dist/693.js.map +1 -0
  6. package/dist/ethiopia-esm-clinical-workflow-app.js +1 -1
  7. package/dist/ethiopia-esm-clinical-workflow-app.js.buildmanifest.json +28 -28
  8. package/dist/main.js +4 -4
  9. package/dist/main.js.map +1 -1
  10. package/dist/routes.json +1 -1
  11. package/package.json +1 -1
  12. package/src/config-schema.ts +2 -0
  13. package/src/mru/billing-information/billing-information.resource.ts +60 -59
  14. package/src/mru/billing-information/billing-information.scss +21 -3
  15. package/src/mru/billing-information/billing-information.workspace.tsx +106 -252
  16. package/src/mru/billing-information/components/BillingTypeAttributes.tsx +106 -0
  17. package/src/mru/billing-information/components/CreditSubTypeFields.tsx +145 -0
  18. package/src/mru/billing-information/components/CreditSubTypeSelection.tsx +66 -0
  19. package/src/mru/billing-information/components/FreeSubTypeFields.tsx +7 -0
  20. package/src/mru/billing-information/components/FreeSubTypeSelection.tsx +61 -0
  21. package/src/mru/billing-information/components/index.ts +5 -0
  22. package/src/mru/billing-information/hooks/index.ts +4 -0
  23. package/src/mru/billing-information/hooks/useBillingForm.ts +22 -0
  24. package/src/mru/billing-information/hooks/useBillingType.ts +18 -0
  25. package/src/mru/billing-information/hooks/useCreditCompanies.ts +17 -0
  26. package/src/mru/billing-information/hooks/usePaymentModes.ts +17 -0
  27. package/dist/160.js.map +0 -1
@@ -0,0 +1,106 @@
1
+ import React from 'react';
2
+ import { Controller, Control, FieldErrors } from 'react-hook-form';
3
+ import { TextInput, FormGroup } from '@carbon/react';
4
+ import { OpenmrsDatePicker } from '@openmrs/esm-framework';
5
+ import type { TFunction } from 'i18next';
6
+ import type { BillingFormData } from '../billing-information.resource';
7
+ import styles from '../billing-information.scss';
8
+
9
+ type BillingTypeAttributesProps = {
10
+ control: Control<BillingFormData>;
11
+ errors: FieldErrors<BillingFormData>;
12
+ t: TFunction;
13
+ attributeTypes: Array<{
14
+ uuid: string;
15
+ name: string;
16
+ description?: string;
17
+ format?: string;
18
+ required?: boolean;
19
+ }>;
20
+ attributes: Record<string, any>;
21
+ setValue: (name: string, value: any, options?: { shouldDirty?: boolean }) => void;
22
+ };
23
+
24
+ export const BillingTypeAttributes: React.FC<BillingTypeAttributesProps> = ({
25
+ control,
26
+ errors,
27
+ t,
28
+ attributeTypes,
29
+ attributes,
30
+ setValue,
31
+ }) => {
32
+ const renderAttributeField = (attrType: {
33
+ uuid: string;
34
+ name: string;
35
+ description?: string;
36
+ format?: string;
37
+ required?: boolean;
38
+ }) => {
39
+ const fieldName = `attributes.${attrType.uuid}` as const;
40
+ const fieldError = errors.attributes?.[attrType.uuid];
41
+ const errorMessage =
42
+ fieldError && typeof fieldError === 'object' && 'message' in fieldError
43
+ ? String(fieldError.message)
44
+ : fieldError
45
+ ? String(fieldError)
46
+ : undefined;
47
+
48
+ // Determine field type based on format
49
+ const isDateField =
50
+ attrType.format?.toLowerCase().includes('date') || attrType.format === 'org.openmrs.util.AttributableDate';
51
+
52
+ if (isDateField) {
53
+ return (
54
+ <Controller
55
+ key={attrType.uuid}
56
+ name={fieldName}
57
+ control={control}
58
+ render={({ field: { onChange, value } }) => (
59
+ <OpenmrsDatePicker
60
+ id={`attribute-${attrType.uuid}`}
61
+ labelText={attrType.name}
62
+ value={value || ''}
63
+ onChange={(date) => {
64
+ const dateValue = typeof date === 'string' ? date : date.toISOString().split('T')[0];
65
+ onChange(dateValue);
66
+ setValue(fieldName, dateValue, { shouldDirty: true });
67
+ }}
68
+ invalid={!!fieldError}
69
+ invalidText={errorMessage}
70
+ />
71
+ )}
72
+ />
73
+ );
74
+ }
75
+
76
+ // Default to text input
77
+ return (
78
+ <Controller
79
+ key={attrType.uuid}
80
+ name={fieldName}
81
+ control={control}
82
+ render={({ field: { onChange, value } }) => (
83
+ <TextInput
84
+ id={`attribute-${attrType.uuid}`}
85
+ labelText={attrType.name}
86
+ value={value || ''}
87
+ onChange={(e) => {
88
+ onChange(e.target.value);
89
+ setValue(fieldName, e.target.value, { shouldDirty: true });
90
+ }}
91
+ placeholder={attrType.description || t('enterValue', 'Enter {{name}}', { name: attrType.name })}
92
+ invalid={!!fieldError}
93
+ invalidText={errorMessage}
94
+ required={attrType.required}
95
+ />
96
+ )}
97
+ />
98
+ );
99
+ };
100
+
101
+ return (
102
+ <FormGroup className={styles.billingTypeAttributesContainer} legendText={t('billingDetails', 'Billing Details')}>
103
+ {attributeTypes.map((attrType) => renderAttributeField(attrType))}
104
+ </FormGroup>
105
+ );
106
+ };
@@ -0,0 +1,145 @@
1
+ import React from 'react';
2
+ import { Control, FieldErrors } from 'react-hook-form';
3
+ import { TextInput, Dropdown, FormGroup } from '@carbon/react';
4
+ import { OpenmrsDatePicker, useConfig } from '@openmrs/esm-framework';
5
+ import type { TFunction } from 'i18next';
6
+ import type { BillingFormData } from '../billing-information.resource';
7
+ import type { ClinicalWorkflowConfig } from '../../../config-schema';
8
+ import styles from '../billing-information.scss';
9
+
10
+ type CreditSubTypeFieldsProps = {
11
+ control: Control<BillingFormData>;
12
+ errors: FieldErrors<BillingFormData>;
13
+ t: TFunction;
14
+ creditSubType: string;
15
+ creditCompanies: Array<{ uuid: string; name: string }>;
16
+ attributes: Record<string, any>;
17
+ setValue: (name: string, value: any, options?: { shouldDirty?: boolean }) => void;
18
+ };
19
+
20
+ export const CreditSubTypeFields: React.FC<CreditSubTypeFieldsProps> = ({
21
+ control,
22
+ errors,
23
+ t,
24
+ creditSubType,
25
+ creditCompanies,
26
+ attributes,
27
+ setValue,
28
+ }) => {
29
+ const { billingVisitAttributeTypes } = useConfig<ClinicalWorkflowConfig>();
30
+
31
+ // Use creditTypeDetails UUID to store all credit details as a JSON object
32
+ const creditDetailsKey = billingVisitAttributeTypes.creditTypeDetails || 'creditTypeDetails';
33
+ const creditDetails = attributes[creditDetailsKey]
34
+ ? typeof attributes[creditDetailsKey] === 'string'
35
+ ? JSON.parse(attributes[creditDetailsKey])
36
+ : attributes[creditDetailsKey]
37
+ : {};
38
+
39
+ const updateCreditDetails = (fieldName: string, value: any) => {
40
+ const updatedDetails = { ...creditDetails, [fieldName]: value };
41
+ setValue(`attributes.${creditDetailsKey}`, JSON.stringify(updatedDetails), { shouldDirty: true });
42
+ };
43
+
44
+ const renderField = (fieldName: string, label: string, isDate = false, isDropdown = false, items: any[] = []) => {
45
+ const fieldValue = creditDetails[fieldName] || '';
46
+ const creditDetailsError = errors.attributes?.[creditDetailsKey];
47
+ const errorMessage =
48
+ creditDetailsError && typeof creditDetailsError === 'object' && 'message' in creditDetailsError
49
+ ? String(creditDetailsError.message)
50
+ : creditDetailsError
51
+ ? String(creditDetailsError)
52
+ : undefined;
53
+
54
+ if (isDropdown) {
55
+ return (
56
+ <Dropdown
57
+ key={fieldName}
58
+ id={`attribute-${fieldName}`}
59
+ titleText={label}
60
+ label={label}
61
+ items={items}
62
+ itemToString={(item) => (item ? (typeof item === 'string' ? item : item.name || item.uuid) : '')}
63
+ selectedItem={
64
+ items.find((item) => (typeof item === 'string' ? item === fieldValue : item.uuid === fieldValue)) || null
65
+ }
66
+ onChange={({ selectedItem }) => {
67
+ const selectedValue = typeof selectedItem === 'string' ? selectedItem : selectedItem?.uuid || '';
68
+ updateCreditDetails(fieldName, selectedValue);
69
+ }}
70
+ invalid={!!creditDetailsError}
71
+ invalidText={errorMessage}
72
+ />
73
+ );
74
+ }
75
+
76
+ if (isDate) {
77
+ return (
78
+ <OpenmrsDatePicker
79
+ key={fieldName}
80
+ id={`attribute-${fieldName}`}
81
+ labelText={label}
82
+ value={fieldValue || ''}
83
+ onChange={(date) => {
84
+ const dateValue = typeof date === 'string' ? date : date.toISOString().split('T')[0];
85
+ updateCreditDetails(fieldName, dateValue);
86
+ }}
87
+ invalid={!!creditDetailsError}
88
+ invalidText={errorMessage}
89
+ />
90
+ );
91
+ }
92
+
93
+ return (
94
+ <TextInput
95
+ key={fieldName}
96
+ id={`attribute-${fieldName}`}
97
+ labelText={label}
98
+ value={fieldValue || ''}
99
+ onChange={(e) => {
100
+ updateCreditDetails(fieldName, e.target.value);
101
+ }}
102
+ placeholder={t('enterValue', 'Enter {{name}}', { name: label })}
103
+ invalid={!!creditDetailsError}
104
+ invalidText={errorMessage}
105
+ />
106
+ );
107
+ };
108
+
109
+ return (
110
+ <FormGroup className={styles.creditTypeDetailsContainer} legendText={t('creditDetails', 'Credit Details')}>
111
+ {creditSubType === 'cbhi' && (
112
+ <>
113
+ {renderField('cbhiId', t('cbhiId', 'CBHI ID'))}
114
+ {renderField('cbhiExpiryDate', t('expiryDate', 'Expiry Date'), true)}
115
+ </>
116
+ )}
117
+ {creditSubType === 'shi' && (
118
+ <>
119
+ {renderField('shiId', t('shiId', 'SHI ID'))}
120
+ {renderField('shiExpiryDate', t('expiryDate', 'Expiry Date'), true)}
121
+ </>
122
+ )}
123
+ {creditSubType === 'insurance' && (
124
+ <>
125
+ {renderField('insuranceName', t('insuranceName', 'Insurance Name'))}
126
+ {renderField('insuranceId', t('insuranceId', 'Insurance ID'))}
127
+ {renderField('insuranceCode', t('insuranceCode', 'Insurance Code'))}
128
+ {renderField('insuranceZone', t('insuranceZone', 'Insurance Zone'))}
129
+ {renderField('insuranceExpiryDate', t('insuranceExpiryDate', 'Insurance Expiry Date'), true)}
130
+ </>
131
+ )}
132
+ {creditSubType === 'creditCompany' && (
133
+ <>
134
+ {renderField(
135
+ 'creditCompany',
136
+ t('creditCompany', 'Credit Company'),
137
+ false,
138
+ true,
139
+ creditCompanies.map((company) => ({ uuid: company.uuid, name: company.name })),
140
+ )}
141
+ </>
142
+ )}
143
+ </FormGroup>
144
+ );
145
+ };
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+ import { Controller, Control, FieldErrors } from 'react-hook-form';
3
+ import { Dropdown, FormGroup } from '@carbon/react';
4
+ import { useConfig } from '@openmrs/esm-framework';
5
+ import type { TFunction } from 'i18next';
6
+ import type { BillingFormData } from '../billing-information.resource';
7
+ import type { ClinicalWorkflowConfig } from '../../../config-schema';
8
+ import styles from '../billing-information.scss';
9
+
10
+ type CreditSubTypeSelectionProps = {
11
+ control: Control<BillingFormData>;
12
+ errors: FieldErrors<BillingFormData>;
13
+ t: TFunction;
14
+ creditSubType?: string;
15
+ setValue: (name: string, value: any, options?: { shouldDirty?: boolean }) => void;
16
+ };
17
+
18
+ export const CreditSubTypeSelection: React.FC<CreditSubTypeSelectionProps> = ({
19
+ control,
20
+ errors,
21
+ t,
22
+ creditSubType,
23
+ setValue,
24
+ }) => {
25
+ const { billingVisitAttributeTypes } = useConfig<ClinicalWorkflowConfig>();
26
+ const creditSubTypes = [
27
+ { value: 'cbhi', label: 'CBHI' },
28
+ { value: 'shi', label: 'SHI' },
29
+ { value: 'insurance', label: 'Insurance' },
30
+ { value: 'creditCompany', label: 'Credit Company' },
31
+ ];
32
+
33
+ return (
34
+ <FormGroup className={styles.creditDetailsContainer} legendText={t('creditType', 'Credit Type')}>
35
+ <Controller
36
+ name="creditSubType"
37
+ control={control}
38
+ render={({ field: { onChange, value } }) => (
39
+ <Dropdown
40
+ id="creditSubType"
41
+ titleText={t('selectCreditType', 'Select Credit Type')}
42
+ label={t('selectCreditType', 'Select Credit Type')}
43
+ items={creditSubTypes}
44
+ itemToString={(item) => (item ? item.label : '')}
45
+ selectedItem={creditSubTypes.find((item) => item.value === value) || null}
46
+ onChange={({ selectedItem }) => {
47
+ const subTypeValue = selectedItem?.value;
48
+ onChange(subTypeValue);
49
+ setValue('creditSubType', subTypeValue, { shouldDirty: true });
50
+ // Store credit sub-type in attributes
51
+ if (subTypeValue && billingVisitAttributeTypes.creditType) {
52
+ setValue(`attributes.${billingVisitAttributeTypes.creditType}`, subTypeValue, { shouldDirty: true });
53
+ }
54
+ // Clear credit details when changing sub-type
55
+ if (billingVisitAttributeTypes.creditTypeDetails) {
56
+ setValue(`attributes.${billingVisitAttributeTypes.creditTypeDetails}`, '', { shouldDirty: false });
57
+ }
58
+ }}
59
+ invalid={!!errors.creditSubType}
60
+ invalidText={errors.creditSubType?.message}
61
+ />
62
+ )}
63
+ />
64
+ </FormGroup>
65
+ );
66
+ };
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+
3
+ export const FreeSubTypeFields: React.FC = () => {
4
+ // Free sub-type is already stored in attributes via FreeSubTypeSelection component
5
+ // No additional fields needed for free types
6
+ return null;
7
+ };
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import { Controller, Control, FieldErrors } from 'react-hook-form';
3
+ import { Dropdown, FormGroup } from '@carbon/react';
4
+ import { useConfig } from '@openmrs/esm-framework';
5
+ import type { TFunction } from 'i18next';
6
+ import type { BillingFormData } from '../billing-information.resource';
7
+ import type { ClinicalWorkflowConfig } from '../../../config-schema';
8
+ import styles from '../billing-information.scss';
9
+
10
+ type FreeSubTypeSelectionProps = {
11
+ control: Control<BillingFormData>;
12
+ errors: FieldErrors<BillingFormData>;
13
+ t: TFunction;
14
+ freeSubType?: string;
15
+ setValue: (name: string, value: any, options?: { shouldDirty?: boolean }) => void;
16
+ };
17
+
18
+ export const FreeSubTypeSelection: React.FC<FreeSubTypeSelectionProps> = ({
19
+ control,
20
+ errors,
21
+ t,
22
+ freeSubType,
23
+ setValue,
24
+ }) => {
25
+ const { billingVisitAttributeTypes } = useConfig<ClinicalWorkflowConfig>();
26
+ const freeSubTypes = [
27
+ { value: '24hour', label: '24 Hour' },
28
+ { value: 'staff', label: 'Staff' },
29
+ { value: 'exempted', label: 'Exempted' },
30
+ ];
31
+
32
+ return (
33
+ <FormGroup className={styles.freeDetailsContainer} legendText={t('freeType', 'Free Type')}>
34
+ <Controller
35
+ name="freeSubType"
36
+ control={control}
37
+ render={({ field: { onChange, value } }) => (
38
+ <Dropdown
39
+ id="freeSubType"
40
+ titleText={t('selectFreeType', 'Select Free Type')}
41
+ label={t('selectFreeType', 'Select Free Type')}
42
+ items={freeSubTypes}
43
+ itemToString={(item) => (item ? item.label : '')}
44
+ selectedItem={freeSubTypes.find((item) => item.value === value) || null}
45
+ onChange={({ selectedItem }) => {
46
+ const subTypeValue = selectedItem?.value;
47
+ onChange(subTypeValue);
48
+ setValue('freeSubType', subTypeValue, { shouldDirty: true });
49
+ // Store free sub-type in attributes
50
+ if (subTypeValue && billingVisitAttributeTypes.freeType) {
51
+ setValue(`attributes.${billingVisitAttributeTypes.freeType}`, subTypeValue, { shouldDirty: true });
52
+ }
53
+ }}
54
+ invalid={!!errors.freeSubType}
55
+ invalidText={errors.freeSubType?.message}
56
+ />
57
+ )}
58
+ />
59
+ </FormGroup>
60
+ );
61
+ };
@@ -0,0 +1,5 @@
1
+ export { BillingTypeAttributes } from './BillingTypeAttributes';
2
+ export { CreditSubTypeSelection } from './CreditSubTypeSelection';
3
+ export { FreeSubTypeSelection } from './FreeSubTypeSelection';
4
+ export { CreditSubTypeFields } from './CreditSubTypeFields';
5
+ export { FreeSubTypeFields } from './FreeSubTypeFields';
@@ -0,0 +1,4 @@
1
+ export { usePaymentModes } from './usePaymentModes';
2
+ export { useCreditCompanies } from './useCreditCompanies';
3
+ export { useBillingForm } from './useBillingForm';
4
+ export { useBillingType } from './useBillingType';
@@ -0,0 +1,22 @@
1
+ import { useMemo } from 'react';
2
+ import { useForm } from 'react-hook-form';
3
+ import { zodResolver } from '@hookform/resolvers/zod';
4
+ import type { TFunction } from 'i18next';
5
+ import { createBillingFormSchema, type BillingFormData } from '../billing-information.resource';
6
+
7
+ export const useBillingForm = (t: TFunction, billingTypes: any[]) => {
8
+ const billingFormSchema = useMemo(() => createBillingFormSchema(t, billingTypes), [t, billingTypes]);
9
+
10
+ const form = useForm<BillingFormData>({
11
+ resolver: zodResolver(billingFormSchema),
12
+ mode: 'onTouched',
13
+ defaultValues: {
14
+ billingTypeUuid: undefined,
15
+ creditSubType: undefined,
16
+ freeSubType: undefined,
17
+ attributes: {},
18
+ },
19
+ });
20
+
21
+ return form;
22
+ };
@@ -0,0 +1,18 @@
1
+ import { useMemo } from 'react';
2
+
3
+ export const useBillingType = (billingTypes: any[], billingTypeUuid?: string) => {
4
+ const selectedBillingType = useMemo(
5
+ () => billingTypes.find((bt) => bt.uuid === billingTypeUuid),
6
+ [billingTypes, billingTypeUuid],
7
+ );
8
+
9
+ const isCreditType = useMemo(() => selectedBillingType?.name?.toLowerCase() === 'credit', [selectedBillingType]);
10
+
11
+ const isFreeType = useMemo(() => selectedBillingType?.name?.toLowerCase() === 'free', [selectedBillingType]);
12
+
13
+ return {
14
+ selectedBillingType,
15
+ isCreditType,
16
+ isFreeType,
17
+ };
18
+ };
@@ -0,0 +1,17 @@
1
+ import useSWR from 'swr';
2
+ import { openmrsFetch } from '@openmrs/esm-framework';
3
+
4
+ export const useCreditCompanies = (enabled: boolean) => {
5
+ const creditCompaniesUrl = `/ws/rest/v1/cashier/creditCompany?v=full`;
6
+ const {
7
+ data: creditCompaniesData,
8
+ error,
9
+ isLoading,
10
+ } = useSWR<{ data: { results: any[] } }>(enabled ? creditCompaniesUrl : null, openmrsFetch);
11
+
12
+ return {
13
+ creditCompanies: creditCompaniesData?.data?.results || [],
14
+ isLoading,
15
+ error,
16
+ };
17
+ };
@@ -0,0 +1,17 @@
1
+ import useSWR from 'swr';
2
+ import { openmrsFetch } from '@openmrs/esm-framework';
3
+
4
+ export const usePaymentModes = () => {
5
+ const paymentModesUrl = `/ws/rest/v1/cashier/paymentMode?v=full`;
6
+ const {
7
+ data: paymentModesData,
8
+ error,
9
+ isLoading,
10
+ } = useSWR<{ data: { results: any[] } }>(paymentModesUrl, openmrsFetch);
11
+
12
+ return {
13
+ billingTypes: paymentModesData?.data?.results || [],
14
+ isLoading,
15
+ error,
16
+ };
17
+ };