Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(fix) SJT-120 Add the clock in and clock out modal and fix the service type filter. #108

Merged
merged 4 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/esm-billing-app/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ehospital/esm-billing-app",
"version": "1.1.8",
"version": "1.2.0",
"description": "Billing frontend module for use in O3",
"browser": "dist/ehospital-esm-billing-app.js",
"main": "src/index.ts",
Expand Down Expand Up @@ -121,5 +121,5 @@
"*.{js,jsx,ts,tsx}": "eslint --cache --fix"
},
"packageManager": "[email protected]",
"gitHead": "480dacb69afe841a1c69da2805b588678d93d34c"
"gitHead": "d2c82bbf857f52e0b9fde4ba748c7e5d0db80413"
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const mockBill: MappedBill = {
},
],
dateCreated: new Date().toISOString(),
dateCreatedUnformatted: '2023-09-01T12:00:00Z.000+0300',
billingService: 'billing-service-uuid',
payments: [],
patientName: 'John Doe',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Tag } from '@carbon/react';
import React from 'react';
import { colorsArray } from '../../constants';
import { useServiceTypes } from '../billable-service.resource';
import styles from './payment-history.scss';

export const AppliedFilterTags = ({ appliedFilters }: { appliedFilters: Array<string> }) => {
const { serviceTypes } = useServiceTypes();

const getRandomTagColor = () => {
const randomIndex = Math.floor(Math.random() * colorsArray.length);
return colorsArray[randomIndex];
};

const tags = appliedFilters
.filter((f) => Boolean(f))
.map((f) => {
const serviceType = serviceTypes.find((sT) => sT.uuid === f);
const isServiceTypeUUID = Boolean(serviceType);
return isServiceTypeUUID ? serviceType.display : f;
});

if (tags.length === 0) {
return;
}

return (
<div className={styles.tagWrapper}>
{tags.map((tag, index) => (
<Tag key={index} type={getRandomTagColor()}>
{tag}
</Tag>
))}
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Checkbox, usePrefix } from '@carbon/react';
import React, { ChangeEvent } from 'react';
import { MappedBill } from '../../types';
import styles from './payment-history.scss';

export const CashierFilter = ({
selectedCashierCheckboxes,
bills,
applyCashierFilter,
}: {
selectedCashierCheckboxes: Array<string>;
bills: MappedBill[];
applyCashierFilter: (filters: Array<string>) => void;
}) => {
const cashiers = Array.from(new Map(bills.map((bill) => [bill.cashier.uuid, bill.cashier])).values());

const prefix = usePrefix();

const handleCheckboxChange = (e: ChangeEvent<HTMLInputElement>) => {
const checkboxId = e.target.id;
const isChecked = e.target.checked;

const checkboxValue: HTMLSpanElement | null = document.querySelector(`label[for="${checkboxId}"]`);

if (isChecked && checkboxValue) {
applyCashierFilter([...selectedCashierCheckboxes, checkboxValue.innerText]);
} else {
applyCashierFilter(selectedCashierCheckboxes.filter((item) => item !== checkboxValue?.innerText));
}
};

return (
<div className={styles.checkBoxWrapper}>
<fieldset className={`${prefix}--fieldset`}>
<legend className={`${prefix}--label`}>Cashiers</legend>
{cashiers.length === 0 && <p className={styles.noCashiersInRange}>No Cashiers In Bills Range</p>}
{cashiers.map((cashier) => (
<Checkbox
labelText={cashier.display}
id={`checkbox-${cashier.display}`}
onChange={handleCheckboxChange}
checked={selectedCashierCheckboxes.includes(cashier.display)}
/>
))}
</fieldset>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Button, Popover, PopoverContent, usePrefix } from '@carbon/react';
import { Filter as FilterIcon } from '@carbon/react/icons';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { MappedBill } from '../../types';
import { CashierFilter } from './cashier-filter.component';
import styles from './payment-history.scss';
import { PaymentTypeFilter } from './payment-type-filter.component';
import { ServiceTypeFilter } from './service-type-filter.component';

interface FilterProps {
applyFilters: (filters: Array<string>) => void;
resetFilters: () => void;
bills: MappedBill[];
}

export const Filter = ({ applyFilters, resetFilters, bills }: FilterProps) => {
const [isOpen, setIsOpen] = useState(false);

const [selectedPaymentTypeCheckBoxes, setSelectedPaymentTypeCheckBoxes] = useState<Array<string>>([]);
const [selectedCashierCheckboxes, setSelectedCashierCheckboxes] = useState<Array<string>>([]);
const [selectedServiceTypeCheckboxes, setSelectedServiceTypeCheckboxes] = useState<Array<string>>([]);

const { t } = useTranslation();
const prefix = usePrefix();

const handleApplyFilter = () => {
applyFilters([...selectedCashierCheckboxes, ...selectedPaymentTypeCheckBoxes, ...selectedServiceTypeCheckboxes]);
setIsOpen(false);
};

const handleResetFilter = () => {
resetFilters();
setSelectedPaymentTypeCheckBoxes([]);
setSelectedCashierCheckboxes([]);
setSelectedServiceTypeCheckboxes([]);
setIsOpen(false);
};

const applyCashierFilter = (appliedCashierCheckboxes: Array<string>) => {
setSelectedCashierCheckboxes(appliedCashierCheckboxes);
};

const applyPaymentTypeFilter = (appliedPaymentTypeCheckboxes: Array<string>) => {
setSelectedPaymentTypeCheckBoxes(appliedPaymentTypeCheckboxes);
};

const applyServiceTypeFilter = (appliedServiceTypes: Array<string>) => {
setSelectedServiceTypeCheckboxes(appliedServiceTypes);
};

return (
<Popover align={'left'} caret={true} open={isOpen} onRequestClose={() => setIsOpen(false)}>
<button
aria-label="Filtering"
type="button"
aria-expanded={isOpen}
onClick={() => {
setIsOpen(!isOpen);
}}
className={`${prefix}--toolbar-action ${prefix}--overflow-menu`}>
<FilterIcon />
</button>
<PopoverContent id="containerCheckbox">
<div className={styles.checkBoxWrapper}>
<ServiceTypeFilter
applyServiceTypeFilter={applyServiceTypeFilter}
bills={bills}
selectedServiceTypeCheckboxes={selectedServiceTypeCheckboxes}
/>
<PaymentTypeFilter
applyPaymentTypeFilter={applyPaymentTypeFilter}
selectedPaymentTypeCheckBoxes={selectedPaymentTypeCheckBoxes}
/>
<CashierFilter
applyCashierFilter={applyCashierFilter}
bills={bills}
selectedCashierCheckboxes={selectedCashierCheckboxes}
/>
</div>
<Button kind="secondary" title="Reset filters" onClick={handleResetFilter}>
{t('resetFilters', ' Reset filters')}
</Button>
<Button kind="primary" title="Reset filters" onClick={handleApplyFilter}>
{t('applyFilter', 'Apply filter')}
</Button>
</PopoverContent>
</Popover>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { DataTableSkeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@carbon/react';
import { EmptyState, ErrorState } from '@openmrs/esm-patient-common-lib';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useBills } from '../../billing.resource';
import { MappedBill } from '../../types';
// import { headers } from './payment-history-viewer.component';
import styles from './payment-history.scss';

export const PaymentHistoryTable = ({
tableData,
paidBillsResponse,
renderedRows,
}: {
tableData: any;
paidBillsResponse: ReturnType<typeof useBills>;
renderedRows: MappedBill[];
}) => {
const { t } = useTranslation();
const { bills, error, isLoading } = paidBillsResponse;
const { rows, getHeaderProps, getRowProps, getTableProps } = tableData;

const headers = [
{ header: t('billDate', 'Date'), key: 'dateCreated' },
{ header: t('patientName', 'Patient Name'), key: 'patientName' },
{ header: t('totalAmount', 'Total Amount'), key: 'totalAmount' },
{ header: t('billingService', 'Service'), key: 'billingService' },
{ header: t('referenceCodes', ' Reference Codes'), key: 'referenceCodes' },
{ header: t('status', 'Status'), key: 'status' },
];

if (isLoading) {
return (
<div className={styles.dataTableSkeleton}>
<DataTableSkeleton
headers={headers}
aria-label="patient bills table"
showToolbar={false}
showHeader={false}
columnCount={Object.keys(headers).length}
zebra
rowCount={3}
/>
</div>
);
}

if (error) {
return (
<div className={styles.errorStateSkeleton}>
<ErrorState error={error} headerTitle={t('paidBillsErrorState', 'An error occurred fetching bills')} />
</div>
);
}

if (bills.length === 0 || renderedRows.length === 0) {
return (
<div className={styles.emptyStateWrapper}>
<EmptyState
displayText={t('noBillsFilter', 'No bills match that filter please adjust your filters')}
headerTitle={t('noBillsHeader', 'No bills match the provided filters')}
/>
</div>
);
}

return (
<Table {...getTableProps()} aria-label="sample table">
<TableHead>
<TableRow>
{headers.map((header) => (
<TableHeader
key={header.key}
{...getHeaderProps({
header,
})}>
{header.header}
</TableHeader>
))}
</TableRow>
</TableHead>
<TableBody>
{rows.map((row) => (
<TableRow
key={row.id}
{...getRowProps({
row,
})}>
{row.cells.map((cell) => (
<TableCell key={cell.id}>{cell.value}</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
);
};
Loading
Loading