Skip to content
Draft
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
16 changes: 11 additions & 5 deletions src/billing-dashboard/billing-dashboard.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@ import { omrsDateFormat } from '../constants';
import BillingHeader from '../billing-header/billing-header.component';
import BillsTable from '../bills-table/bills-table.component';
import MetricsCards from '../metrics-cards/metrics-cards.component';
import CashPointSelector from '../cash-point-selector/cash-point-selector.component';
import SelectedDateContext from '../hooks/selectedDateContext';
import SelectedCashPointContext from '../hooks/selectedCashPointContext';
import styles from './billing-dashboard.scss';

export function BillingDashboard() {
const { t } = useTranslation();
const [selectedDate, setSelectedDate] = useState<string>(dayjs().startOf('day').format(omrsDateFormat));
const [selectedCashPoint, setSelectedCashPoint] = useState(null);

const params = useParams();

Expand All @@ -23,11 +26,14 @@ export function BillingDashboard() {

return (
<SelectedDateContext.Provider value={{ selectedDate, setSelectedDate }}>
<BillingHeader title={t('home', 'Home')} />
<MetricsCards />
<section className={styles.billsTableContainer}>
<BillsTable />
</section>
<SelectedCashPointContext.Provider value={{ selectedCashPoint, setSelectedCashPoint }}>
<BillingHeader title={t('home', 'Home')} />
<CashPointSelector />
<MetricsCards />
<section className={styles.billsTableContainer}>
<BillsTable />
</section>
</SelectedCashPointContext.Provider>
</SelectedDateContext.Provider>
);
}
35 changes: 1 addition & 34 deletions src/billing-header/billing-header.component.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import React, { useContext } from 'react';
import dayjs from 'dayjs';
import { DatePickerInput, DatePicker } from '@carbon/react';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Location, UserFollow } from '@carbon/react/icons';
import { useSession } from '@openmrs/esm-framework';
import { omrsDateFormat } from '../constants';
import BillingIllustration from './billing-illustration.component';
import SelectedDateContext from '../hooks/selectedDateContext';
import styles from './billing-header.scss';

interface BillingHeaderProps {
Expand All @@ -15,9 +9,6 @@ interface BillingHeaderProps {

const BillingHeader: React.FC<BillingHeaderProps> = ({ title }) => {
const { t } = useTranslation();
const session = useSession();
const location = session?.sessionLocation?.display;
const { selectedDate, setSelectedDate } = useContext(SelectedDateContext);

return (
<div className={styles.header} data-testid="billing-header">
Expand All @@ -28,30 +19,6 @@ const BillingHeader: React.FC<BillingHeaderProps> = ({ title }) => {
<p className={styles['page-name']}>{title}</p>
</div>
</div>
<div className={styles['right-justified-items']}>
<div className={styles.userContainer}>
<p>{session?.user?.person?.display}</p>
<UserFollow size={16} className={styles.userIcon} />
</div>
<div className={styles['date-and-location']}>
<Location size={16} />
<span className={styles.value}>{location}</span>
<span className={styles.middot}>&middot;</span>
<DatePicker
onChange={([date]) => setSelectedDate(dayjs(date).startOf('day').format(omrsDateFormat))}
value={dayjs(selectedDate).format('DD MMM YYYY')}
dateFormat="d-M-Y"
datePickerType="single">
<DatePickerInput
style={{ cursor: 'pointer', backgroundColor: 'transparent', border: 'none', maxWidth: '10rem' }}
id="appointment-date-picker"
placeholder="DD-MMM-YYYY"
labelText=""
type="text"
/>
</DatePicker>
</div>
</div>
</div>
);
};
Expand Down
51 changes: 0 additions & 51 deletions src/billing-header/billing-header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
background-color: $ui-02;
border-bottom: 1px solid $ui-03;
display: flex;
justify-content: space-between;
padding: layout.$spacing-05;
}

Expand All @@ -18,15 +17,6 @@
flex-direction: row;
align-items: center;
cursor: pointer;
align-items: center;
}

.right-justified-items {
@include type.type-style('body-compact-02');
color: $text-02;
display: flex;
flex-direction: column;
justify-content: space-between;
}

.page-name {
Expand All @@ -40,44 +30,3 @@
margin-bottom: layout.$spacing-02;
}
}

.date-and-location {
display: flex;
justify-content: flex-end;
align-items: center;
}

.userContainer {
display: flex;
justify-content: flex-end;
gap: layout.$spacing-05;
}

.value {
margin-left: layout.$spacing-02;
}

.middot {
margin: 0 layout.$spacing-03;
}

.view {
@include type.type-style('label-01');
}

// Overriding styles for RTL support
html[dir='rtl'] {
.date-and-location {
& > svg {
order: -1;
}
& > span:nth-child(2) {
order: -2;
}
}
}

.userIcon {
fill: $ui-05;
margin: layout.$spacing-01;
}
9 changes: 6 additions & 3 deletions src/bills-table/bills-table.component.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useId, useMemo, useState } from 'react';
import React, { useCallback, useContext, useId, useMemo, useState } from 'react';
import classNames from 'classnames';
import {
DataTable,
Expand Down Expand Up @@ -28,6 +28,7 @@ import {
} from '@openmrs/esm-framework';
import { EmptyDataIllustration } from '@openmrs/esm-patient-common-lib';
import { useBills } from '../billing.resource';
import SelectedCashPointContext from '../hooks/selectedCashPointContext';
import styles from './bills-table.scss';

const BillsTable = () => {
Expand All @@ -41,6 +42,7 @@ const BillsTable = () => {
const [pageSize, setPageSize] = useState(config?.bills?.pageSize ?? 10);
const { bills, isLoading, isValidating, error } = useBills('', '');
const [searchString, setSearchString] = useState('');
const { selectedCashPoint } = useContext(SelectedCashPointContext);

const headerData = [
{
Expand Down Expand Up @@ -75,15 +77,16 @@ const BillsTable = () => {
return bill;
})
.filter((bill) => {
const cashPointMatch = !selectedCashPoint || bill.cashPointUuid === selectedCashPoint.uuid;
const statusMatch = billPaymentStatus === '' ? true : bill.status === billPaymentStatus;
const searchMatch = !searchString
? true
: bill.patientName.toLowerCase().includes(searchString.toLowerCase()) ||
bill.identifier.toLowerCase().includes(searchString.toLowerCase());

return statusMatch && searchMatch;
return cashPointMatch && statusMatch && searchMatch;
});
}, [bills, searchString, billPaymentStatus]);
}, [bills, searchString, billPaymentStatus, selectedCashPoint]);

const { paginated, goTo, results, currentPage } = usePagination(searchResults, pageSize);

Expand Down
103 changes: 103 additions & 0 deletions src/cash-point-selector/cash-point-selector.component.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React, { useState, useEffect, useContext } from 'react';
import { InlineLoading, Tag, Dropdown } from '@carbon/react';
import { Time, Location } from '@carbon/react/icons';
import { useTranslation } from 'react-i18next';
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import duration from 'dayjs/plugin/duration';
import { getCoreTranslation, useSession } from '@openmrs/esm-framework';
import { useCashPoint } from '../billing-form/billing-form.resource';
import SelectedCashPointContext from '../hooks/selectedCashPointContext';
import styles from './cash-point-selector.scss';

dayjs.extend(relativeTime);
dayjs.extend(duration);

export default function CashPointSelector() {
const { t } = useTranslation();
const session = useSession();
const { cashPoints, isLoading, error } = useCashPoint();
const { selectedCashPoint, setSelectedCashPoint } = useContext(SelectedCashPointContext);
const [clockInTime] = useState(dayjs());
const [currentTime, setCurrentTime] = useState(dayjs());

const userName = session?.user?.person?.display;
const location = session?.sessionLocation?.display;

useEffect(() => {
if (cashPoints && cashPoints.length > 0 && !selectedCashPoint) {
setSelectedCashPoint(cashPoints[0]);
}
}, [cashPoints, selectedCashPoint, setSelectedCashPoint]);

useEffect(() => {
const interval = setInterval(() => {
setCurrentTime(dayjs());
}, 60000); // Update every minute

return () => clearInterval(interval);
}, []);

const getDuration = () => {
const diff = currentTime.diff(clockInTime);
const dur = dayjs.duration(diff);
const hours = Math.floor(dur.asHours());
return `+${hours}hs`;
};

const handleCashPointChange = ({ selectedItem }) => {
setSelectedCashPoint(selectedItem);
};

if (isLoading) {
return (
<section className={styles.container}>
<InlineLoading
status="active"
iconDescription={getCoreTranslation('loading')}
description={t('loadingCashPoints', 'Loading cash points') + '...'}
/>
</section>
);
}

if (error || !cashPoints || cashPoints.length === 0) {
return null;
}

return (
<section className={styles.container}>
<div className={styles.content}>
<div className={styles.clockInInfo}>
<Time size={16} className={styles.clockIcon} />
<span className={styles.clockInText}>
{t('clockedInOn', 'Clocked in on')} {clockInTime.format('DD MMM YYYY, HH:mm A')}
</span>
<Tag type="blue" size="sm" className={styles.durationTag}>
{getDuration()}
</Tag>
</div>
<div className={styles.rightSection}>
<div className={styles.userInfo}>
<span className={styles.userName}>{userName}</span>
<div className={styles.locationInfo}>
<Location size={16} className={styles.locationIcon} />
<span className={styles.locationText}>{location}</span>
</div>
</div>
<Dropdown
id="cash-point-selector"
items={cashPoints}
itemToString={(item) => (item ? item.name : '')}
selectedItem={selectedCashPoint}
onChange={handleCashPointChange}
label={t('selectCashPoint', 'Select cash point')}
titleText=""
size="md"
className={styles.cashPointDropdown}
/>
</div>
</div>
</section>
);
}
Loading