Skip to content

Commit

Permalink
add stock details and price components
Browse files Browse the repository at this point in the history
  • Loading branch information
jabahum committed Jan 6, 2025
1 parent 6760959 commit 6093192
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ export const stockSettingsLink = getSyncLifecycle(

export const stockManagement = getSyncLifecycle(stockManagementComponent, options);

// stock details (balances and prices)
export const orderPriceDetailsExtension = getAsyncLifecycle(
() => import('./stock-items/stock-price-details/stock-item-price-details.component'),
options,
);
export const orderStockDetailsExtension = getAsyncLifecycle(
() => import('./stock-items/stock-price-details/stock-item-stock-details.component'),
options,
);

export const root = getSyncLifecycle(Root, options);

export const deleteStockModal = getAsyncLifecycle(() => import('./stock-sources/delete-stock-modal.component'), {
Expand Down
13 changes: 13 additions & 0 deletions src/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,20 @@
{
"name": "delete-packaging-unit-button",
"component": "deletePackagingUnitButton"
},
{
"name": "order-price-details",
"component": "orderPriceDetailsExtension",
"slot": "order-item-additional-info-slot",
"order": 0
},
{
"name": "order-stock-details",
"component": "orderStockDetailsExtension",
"slot": "order-item-additional-info-slot",
"order": 1
}

],
"modals": [
{
Expand Down
28 changes: 28 additions & 0 deletions src/stock-items/stock-price-details/order-stock-details.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@use '@carbon/layout';
@use '@openmrs/esm-styleguide/src/vars' as *;

.itemInStock {
color: $support-02;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;

.itemInStockIcon {
fill: $support-02;
margin-inline-end: layout.$spacing-02;
}
}

.itemOutOfStock {
color: $danger;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;

.itemOutOfStockIcon {
fill: $danger;
margin-inline-end: layout.$spacing-02;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React, { useMemo } from 'react';
import { useOrderPrice } from '../hooks/useOrderPrice';
import styles from './order-price-details.scss';
import { SkeletonText, Tooltip } from '@carbon/react';
import { useTranslation } from 'react-i18next';
import { getLocale, InformationIcon } from '@openmrs/esm-framework';

interface OrderPriceDetailsComponentProps {
orderItemUuid: string;
}

const OrderPriceDetailsComponent: React.FC<OrderPriceDetailsComponentProps> = ({ orderItemUuid }) => {
const { t } = useTranslation();
const locale = getLocale();
const { data: priceData, isLoading, error } = useOrderPrice(orderItemUuid);

const amount = useMemo(() => {
if (!priceData || priceData.entry.length === 0) {
return null;
}
return priceData.entry[0].resource.propertyGroup[0]?.priceComponent[0]?.amount;
}, [priceData]);

const formattedPrice = useMemo((): string => {
if (!amount) return '';
try {
new Intl.NumberFormat(locale, {
style: 'currency',
currency: amount.currency,
});

return new Intl.NumberFormat(locale, {
style: 'currency',
currency: amount.currency,
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(amount.value);
} catch (error) {
console.error(`Invalid currency code: ${amount.currency}. Error: ${error.message}`);
return `${new Intl.NumberFormat(locale, {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}).format(amount.value)} ${amount.currency}`;
}
}, [locale, amount]);

if (isLoading) {
return <SkeletonText width="100px" role="progressbar" />;
}

if (!priceData || !amount || error) {
return null;
}

return (
<div className={styles.priceDetailsContainer}>
<span className={styles.priceLabel}>{t('price', 'Price')}:</span>
{formattedPrice}
<Tooltip
align="bottom-left"
className={styles.priceToolTip}
label={t(
'priceDisclaimer',
'This price is indicative and may not reflect final costs, which could vary due to discounts, insurance coverage, or other pricing rules',
)}
>
<button className={styles.priceToolTipTrigger} type="button">
<InformationIcon size={16} />
</button>
</Tooltip>
</div>
);
};

export default OrderPriceDetailsComponent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React, { useMemo } from 'react';
import { CheckmarkFilledIcon, CloseFilledIcon } from '@openmrs/esm-framework';
import { useOrderStockInfo } from '../hooks/useOrderStockInfo';
import styles from './order-stock-details.scss';
import { SkeletonText } from '@carbon/react';
import { useTranslation } from 'react-i18next';

interface OrderStockDetailsComponentProps {
orderItemUuid: string;
}

const OrderStockDetailsComponent: React.FC<OrderStockDetailsComponentProps> = ({ orderItemUuid }) => {
const { t } = useTranslation();
const { data: stockData, isLoading, error } = useOrderStockInfo(orderItemUuid);

const isInStock = useMemo(() => {
if (!stockData?.entry?.length) {
return false;
}
const resource = stockData.entry[0]?.resource;
return (
resource?.status === 'active' && typeof resource?.netContent?.value === 'number' && resource.netContent.value > 0
);
}, [stockData]);

if (isLoading) {
return <SkeletonText width="100px" role="progressbar" />;
}

if (!stockData?.entry || error) {
return null;
}

return (
<div>
{isInStock ? (
<div className={styles.itemInStock}>
<CheckmarkFilledIcon size={16} className={styles.itemInStockIcon} /> {t('inStock', 'In stock')}
</div>
) : (
<div className={styles.itemOutOfStock}>
<CloseFilledIcon size={16} className={styles.itemOutOfStockIcon} /> {t('outOfStock', 'Out of stock')}
</div>
)}
</div>
);
};

export default OrderStockDetailsComponent;
28 changes: 28 additions & 0 deletions src/stock-items/stock-price-details/stock-price-details.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
@use '@carbon/type';
@use '@carbon/layout';

.priceDetailsContainer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}

.priceLabel {
@include type.type-style('heading-compact-01');
padding-inline-end: layout.$spacing-02;
}

.priceToolTip {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

.priceToolTipTrigger {
display: flex;
padding: 0 0 0 layout.$spacing-02;
border: none;
outline: none;
}

0 comments on commit 6093192

Please sign in to comment.