-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
(feat) Billable Exemption config (#503)
* (feat) Add Billable exemption config * Sync yarn.lock * Sync yarn.lock * Sync yarn.lock update package.json --------- Co-authored-by: Donald Kibet <[email protected]>
- Loading branch information
1 parent
60767a4
commit b40fe59
Showing
20 changed files
with
893 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
packages/esm-billing-app/src/billable-exemption/billable-exemptions.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import React from 'react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import BillingHeader from '../billing-header/billing-header.component'; | ||
import { BillableExemptionsViewer } from '../billable-services/billable-exemptions/billable-exemptions-viewer.component'; | ||
|
||
export const BillableExemptions = () => { | ||
const { t } = useTranslation(); | ||
return ( | ||
<div> | ||
<BillingHeader title={t('billableExemptionAdministration', 'Exemption Administration')} /> | ||
<BillableExemptionsViewer /> | ||
</div> | ||
); | ||
}; |
18 changes: 18 additions & 0 deletions
18
...app/src/billable-services/billable-exemptions/action-buttons/action-buttons.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import React from 'react'; | ||
import SaveSchemamModal from '../modals/save-schema.modal'; | ||
import type { Schema } from '../../../types'; | ||
import styles from './action-buttons.scss'; | ||
|
||
interface ActionButtonsProps { | ||
schema: Schema; | ||
} | ||
|
||
const ActionButtons: React.FC<ActionButtonsProps> = ({ schema }) => { | ||
return ( | ||
<div className={styles.actionButtons}> | ||
<SaveSchemamModal schema={schema} /> | ||
</div> | ||
); | ||
}; | ||
|
||
export default ActionButtons; |
12 changes: 12 additions & 0 deletions
12
...-billing-app/src/billable-services/billable-exemptions/action-buttons/action-buttons.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
@use '@carbon/layout'; | ||
|
||
.actionButtons { | ||
display: flex; | ||
align-items: center; | ||
justify-content: flex-end; | ||
margin: layout.$spacing-05 0; | ||
|
||
> button { | ||
margin-left: layout.$spacing-05; | ||
} | ||
} |
173 changes: 173 additions & 0 deletions
173
...ng-app/src/billable-services/billable-exemptions/billable-exemptions-viewer.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
import React, { useCallback, useEffect, useState } from 'react'; | ||
import classNames from 'classnames'; | ||
import { useTranslation } from 'react-i18next'; | ||
import type { IMarker } from 'react-ace'; | ||
import { | ||
Button, | ||
Column, | ||
Grid, | ||
InlineLoading, | ||
InlineNotification, | ||
Tab, | ||
TabList, | ||
TabPanel, | ||
TabPanels, | ||
Tabs, | ||
} from '@carbon/react'; | ||
import SchemaEditor from '../billable-exemptions/schema-editor/schema-editor.component'; | ||
import SchemaViewer from '../billable-exemptions/schema-editor/schema-viewer-component'; | ||
import { useSystemBillableSetting } from '../../hooks/useSystemBillableSetting'; | ||
import ActionButtons from '../billable-exemptions/action-buttons/action-buttons.component'; | ||
import { EmptyState } from '@openmrs/esm-patient-common-lib'; | ||
import type { Schema } from '../../types'; | ||
import styles from './billable-exemptions.scss'; | ||
|
||
interface MarkerProps extends IMarker { | ||
text: string; | ||
} | ||
|
||
const ErrorNotification = ({ error, title }: { error: Error; title: string }) => ( | ||
<InlineNotification | ||
className={styles.errorNotification} | ||
kind="error" | ||
lowContrast | ||
subtitle={error?.message} | ||
title={title} | ||
/> | ||
); | ||
|
||
export const BillableExemptionsViewer = () => { | ||
const { t } = useTranslation(); | ||
const { billableExceptionResource, isLoading, error } = useSystemBillableSetting('kenyaemr.billing.exemptions'); | ||
const billableExceptionSchema = billableExceptionResource?.value ?? ''; | ||
|
||
const [schema, setSchema] = useState<Schema | null>(null); | ||
const [stringifiedSchema, setStringifiedSchema] = useState(''); | ||
const [selectedIndex, setSelectedIndex] = useState(0); | ||
const [invalidJsonErrorMessage, setInvalidJsonErrorMessage] = useState(''); | ||
const [errors, setErrors] = useState<Array<MarkerProps>>([]); | ||
const [validationOn, setValidationOn] = useState(true); | ||
|
||
const resetErrorMessage = useCallback(() => setInvalidJsonErrorMessage(''), []); | ||
|
||
const handleSchemaChange = useCallback( | ||
(updatedSchema: string) => { | ||
resetErrorMessage(); | ||
setStringifiedSchema(updatedSchema); | ||
|
||
try { | ||
const parsedSchema = JSON.parse(updatedSchema); | ||
setSchema(parsedSchema); | ||
} catch (error) { | ||
setInvalidJsonErrorMessage(t('invalidJsonError', 'Invalid JSON input.')); | ||
} | ||
}, | ||
[resetErrorMessage], | ||
); | ||
|
||
const updateSchema = useCallback((updatedSchema: Schema) => { | ||
try { | ||
setSchema(updatedSchema); | ||
setStringifiedSchema(JSON.stringify(updatedSchema, null, 2)); | ||
} catch (error) { | ||
setInvalidJsonErrorMessage(t('saveError', 'Failed to save schema.')); | ||
} | ||
}, []); | ||
|
||
useEffect(() => { | ||
if (billableExceptionSchema) { | ||
try { | ||
const parsedSchema: Schema = JSON.parse(billableExceptionSchema); | ||
setSchema(parsedSchema); | ||
setStringifiedSchema(JSON.stringify(parsedSchema, null, 2)); | ||
} catch (error) { | ||
setInvalidJsonErrorMessage(t('invalidJsonError', 'Invalid JSON received for the schema.')); | ||
} | ||
} | ||
}, [billableExceptionSchema, t]); | ||
|
||
const inputDummySchema = useCallback(() => { | ||
const dummySchema = { | ||
services: { | ||
all: [ | ||
{ concept: '856000001122243', description: 'HIV Viral Load' }, | ||
{ concept: '167441', description: 'PCR' }, | ||
{ concept: '162202', description: 'GeneXpert' }, | ||
], | ||
'program:HIV': [{ concept: '1000051', description: 'Registration' }], | ||
'program:TB': [{ concept: '162202', description: 'GeneXpert' }], | ||
'age<5': [{ concept: '32', description: 'Malaria Smear' }], | ||
'visitAttribute:prisoner': [{ concept: '32', description: 'Malaria Smear' }], | ||
}, | ||
commodities: {}, | ||
}; | ||
|
||
setStringifiedSchema(JSON.stringify(dummySchema, null, 2)); | ||
updateSchema(dummySchema); | ||
}, [updateSchema]); | ||
|
||
const handleTabChange = (event) => { | ||
setSelectedIndex(event.selectedIndex); | ||
}; | ||
|
||
return ( | ||
<div className={styles.container}> | ||
<Grid className={classNames(styles.grid)}> | ||
<Column lg={16} md={16} className={styles.column}> | ||
<div className={styles.actionButtons}> | ||
{isLoading ? ( | ||
<InlineLoading description={`${t('loadingSchema', 'Loading schema')}...`} /> | ||
) : ( | ||
<h1 className={styles.schemaName}>{t('exemptionSchema', 'Exemption Schema')}</h1> | ||
)} | ||
</div> | ||
<div className={styles.heading}> | ||
<span className={styles.tabHeading}>{t('schemaEditor', 'Exemptions Schema Editor')}</span> | ||
<div className={styles.topBtns}> | ||
{!schema && selectedIndex === 1 && ( | ||
<Button kind="ghost" onClick={inputDummySchema}> | ||
{t('inputSampleSchema', 'Input sample schema')} | ||
</Button> | ||
)} | ||
{schema && selectedIndex === 1 && <ActionButtons schema={schema} />} | ||
</div> | ||
</div> | ||
{error && <ErrorNotification error={error} title={t('schemaLoadError', 'Error loading schema')} />} | ||
<Tabs onChange={handleTabChange} selected={selectedIndex}> | ||
<TabList aria-label="Schema previews"> | ||
<Tab>{t('preview', 'Schema Preview')}</Tab> | ||
<Tab>{schema ? t('editSchema', 'Edit Schema') : t('addSchema', 'Add Schema')}</Tab> | ||
</TabList> | ||
<TabPanels> | ||
<TabPanel> | ||
{stringifiedSchema ? ( | ||
<SchemaViewer data={stringifiedSchema} /> | ||
) : ( | ||
<div className={styles.emptyStateWrapper}> | ||
<EmptyState | ||
displayText={t('noSchemaExemption', 'No schema available add exemption schema')} | ||
headerTitle={t('noSchema', 'No schema available')} | ||
/> | ||
</div> | ||
)} | ||
</TabPanel> | ||
<TabPanel> | ||
<div className={styles.editorContainer}> | ||
<SchemaEditor | ||
errors={errors} | ||
isLoading={isLoading} | ||
onSchemaChange={handleSchemaChange} | ||
setErrors={setErrors} | ||
setValidationOn={setValidationOn} | ||
stringifiedSchema={stringifiedSchema} | ||
validationOn={validationOn} | ||
/> | ||
</div> | ||
</TabPanel> | ||
</TabPanels> | ||
</Tabs> | ||
</Column> | ||
</Grid> | ||
</div> | ||
); | ||
}; |
75 changes: 75 additions & 0 deletions
75
packages/esm-billing-app/src/billable-services/billable-exemptions/billable-exemptions.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
@use '@carbon/colors'; | ||
@use '@carbon/layout'; | ||
@use '@carbon/type'; | ||
|
||
.container { | ||
padding: layout.$spacing-05 layout.$spacing-10; | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
|
||
.grid { | ||
margin-left: 0; | ||
margin-right: 0; | ||
padding-left: 0; | ||
padding-right: 0; | ||
max-width: 100%; | ||
|
||
:global(.cds--tabs__nav-item--selected) { | ||
border-bottom: 2px solid var(--cds-border-interactive, colors.$teal-70); | ||
outline: none !important; | ||
} | ||
} | ||
|
||
.column { | ||
margin-left: 0; | ||
margin-right: 0; | ||
} | ||
.errorNotification { | ||
min-width: 100%; | ||
margin: 0; | ||
padding: 0; | ||
} | ||
|
||
.actionButtons { | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-between; | ||
|
||
button { | ||
margin-left: layout.$spacing-05; | ||
} | ||
} | ||
|
||
.schemaName { | ||
@include type.type-style('heading-03'); | ||
} | ||
|
||
.editorContainer { | ||
padding: layout.$spacing-05; | ||
|
||
:global(.ace_editor .ace_content .ace_layer .error) { | ||
position: absolute; | ||
background-color: colors.$red-60; | ||
border-radius: 0; | ||
} | ||
} | ||
|
||
.heading { | ||
display: flex; | ||
margin-right: 1rem; | ||
align-items: center; | ||
} | ||
.tabHeading { | ||
display: flex; | ||
align-items: center; | ||
@include type.type-style('heading-compact-01'); | ||
min-height: 2.5rem; | ||
width: 100%; | ||
padding: 0.75rem; | ||
} | ||
|
||
.topBtns { | ||
display: flex; | ||
align-items: center; | ||
} |
39 changes: 39 additions & 0 deletions
39
...s/esm-billing-app/src/billable-services/billable-exemptions/modals/save-schema-modal.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
@use '@carbon/layout'; | ||
|
||
.spinner { | ||
&:global(.cds--inline-loading) { | ||
min-height: 1rem; | ||
} | ||
|
||
:global(.cds--inline-loading__text) { | ||
font-size: unset; | ||
} | ||
} | ||
|
||
.modalHeader { | ||
:global { | ||
.cds--modal-close-button { | ||
position: absolute; | ||
inset-block-start: 0; | ||
inset-inline-end: 0; | ||
margin: 0; | ||
margin-top: calc(-1 * #{layout.$spacing-05}); | ||
} | ||
|
||
.cds--modal-close { | ||
background-color: rgba(0, 0, 0, 0); | ||
|
||
&:hover { | ||
background-color: var(--cds-layer-hover); | ||
} | ||
} | ||
|
||
.cds--popover--left > .cds--popover > .cds--popover-content { | ||
transform: translate(-4rem, 0.85rem); | ||
} | ||
|
||
.cds--popover--left > .cds--popover > .cds--popover-caret { | ||
transform: translate(-3.75rem, 1.25rem); | ||
} | ||
} | ||
} |
Oops, something went wrong.