-
Notifications
You must be signed in to change notification settings - Fork 1k
Add healthcare service icon and charge item price popover in Activity Definition View #15299
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| import { useTranslation } from "react-i18next"; | ||
|
|
||
| import { MonetaryDisplay } from "@/components/ui/monetary-display"; | ||
| import { | ||
| Popover, | ||
| PopoverContent, | ||
| PopoverTrigger, | ||
| } from "@/components/ui/popover"; | ||
| import { Separator } from "@/components/ui/separator"; | ||
|
|
||
| import ChargeItemPriceDisplay from "@/components/Billing/ChargeItem/ChargeItemPriceDisplay"; | ||
|
|
||
| import { cn } from "@/lib/utils"; | ||
| import { calculateTotalPrice } from "@/types/base/monetaryComponent/monetaryComponent"; | ||
| import { ChargeItemDefinitionRead } from "@/types/billing/chargeItemDefinition/chargeItemDefinition"; | ||
|
|
||
| interface ChargeItemDefinitionPopoverProps { | ||
| chargeItemDefinition: ChargeItemDefinitionRead; | ||
| className?: string; | ||
| } | ||
|
|
||
| export default function ChargeItemDefinitionPopover({ | ||
| chargeItemDefinition, | ||
| className, | ||
| }: ChargeItemDefinitionPopoverProps) { | ||
| const { t } = useTranslation(); | ||
| const priceComponents = chargeItemDefinition.price_components; | ||
|
|
||
| const hasPriceComponents = priceComponents && priceComponents.length > 0; | ||
|
|
||
| if (!hasPriceComponents) { | ||
| return ( | ||
| <span className={cn("text-sm text-gray-500", className)}>{t("na")}</span> | ||
| ); | ||
| } | ||
|
|
||
| const totalPrice = calculateTotalPrice(priceComponents); | ||
|
|
||
| return ( | ||
| <Popover> | ||
| <PopoverTrigger asChild> | ||
| <button | ||
| className={cn( | ||
| "text-sm font-medium underline decoration-dotted underline-offset-4 cursor-pointer hover:text-primary-700 transition-colors", | ||
| className, | ||
| )} | ||
| aria-label={t("view_details")} | ||
| > | ||
|
Comment on lines
+42
to
+48
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add explicit button type and visible focus state. Without 🐛 Proposed fix- <button
- className={cn(
- "text-sm font-medium underline decoration-dotted underline-offset-4 cursor-pointer hover:text-primary-700 transition-colors",
- className,
- )}
- aria-label={t("view_details")}
- >
+ <button
+ type="button"
+ className={cn(
+ "text-sm font-medium underline decoration-dotted underline-offset-4 cursor-pointer hover:text-primary-700 transition-colors focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-primary-700",
+ className,
+ )}
+ aria-label={t("view_details")}
+ >🧰 Tools🪛 Biome (2.1.2)[error] 42-48: Provide an explicit type prop for the button element. The default type of a button is submit, which causes the submission of a form when placed inside a (lint/a11y/useButtonType) 🤖 Prompt for AI Agents |
||
| <MonetaryDisplay amount={totalPrice.toString()} /> | ||
| </button> | ||
| </PopoverTrigger> | ||
| <PopoverContent | ||
| side="right" | ||
| align="start" | ||
| className="p-0 w-auto max-w-[calc(100vw-2rem)]" | ||
| > | ||
| <div className="p-3 space-y-2"> | ||
| <div> | ||
| <p className="font-medium text-sm">{chargeItemDefinition.title}</p> | ||
| {chargeItemDefinition.description && ( | ||
| <p className="text-xs text-gray-600 mt-1"> | ||
| {chargeItemDefinition.description} | ||
| </p> | ||
| )} | ||
| </div> | ||
| {chargeItemDefinition.purpose && ( | ||
| <div> | ||
| <p className="text-xs text-gray-500">{t("purpose")}</p> | ||
| <p className="text-xs text-gray-700"> | ||
| {chargeItemDefinition.purpose} | ||
| </p> | ||
| </div> | ||
| )} | ||
| </div> | ||
| <Separator /> | ||
| <ChargeItemPriceDisplay priceComponents={priceComponents} /> | ||
| </PopoverContent> | ||
| </Popover> | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,6 +5,7 @@ import { useTranslation } from "react-i18next"; | |||||||||||||||||||||||||||||||
| import { toast } from "sonner"; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import CareIcon from "@/CAREUI/icons/CareIcon"; | ||||||||||||||||||||||||||||||||
| import duoToneIcons from "@/CAREUI/icons/DuoTonePaths.json"; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; | ||||||||||||||||||||||||||||||||
| import { Badge } from "@/components/ui/badge"; | ||||||||||||||||||||||||||||||||
|
|
@@ -18,6 +19,8 @@ import { | |||||||||||||||||||||||||||||||
| } from "@/components/ui/card"; | ||||||||||||||||||||||||||||||||
| import { Separator } from "@/components/ui/separator"; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import ChargeItemDefinitionPopover from "@/components/Billing/ChargeItem/ChargeItemDefinitionPopover"; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| import BackButton from "@/components/Common/BackButton"; | ||||||||||||||||||||||||||||||||
| import ConfirmActionDialog from "@/components/Common/ConfirmActionDialog"; | ||||||||||||||||||||||||||||||||
| import Page from "@/components/Common/Page"; | ||||||||||||||||||||||||||||||||
|
|
@@ -33,6 +36,11 @@ import { | |||||||||||||||||||||||||||||||
| import activityDefinitionApi from "@/types/emr/activityDefinition/activityDefinitionApi"; | ||||||||||||||||||||||||||||||||
| import { ArrowLeft } from "lucide-react"; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| type DuoToneIconName = keyof typeof duoToneIcons; | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| const getIconName = (name: string): DuoToneIconName => | ||||||||||||||||||||||||||||||||
| `d-${name}` as DuoToneIconName; | ||||||||||||||||||||||||||||||||
|
Comment on lines
+39
to
+42
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Normalize and validate
✅ Safer icon resolution-type DuoToneIconName = keyof typeof duoToneIcons;
-
-const getIconName = (name: string): DuoToneIconName =>
- `d-${name}` as DuoToneIconName;
+type DuoToneIconName = keyof typeof duoToneIcons;
+
+const getIconName = (name: string): DuoToneIconName | null => {
+ const normalized = name.startsWith("d-") ? name : `d-${name}`;
+ return normalized in duoToneIcons ? (normalized as DuoToneIconName) : null;
+};
@@
- icon={
- definition.healthcare_service.styling_metadata?.careIcon
- ? getIconName(
- definition.healthcare_service.styling_metadata
- .careIcon,
- )
- : "d-health-worker"
- }
+ icon={
+ definition.healthcare_service.styling_metadata?.careIcon
+ ? getIconName(
+ definition.healthcare_service.styling_metadata
+ .careIcon,
+ ) ?? "d-health-worker"
+ : "d-health-worker"
+ }Also applies to: 372-379 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
|
Comment on lines
+41
to
+43
|
||||||||||||||||||||||||||||||||
| const getIconName = (name: string): DuoToneIconName => | |
| `d-${name}` as DuoToneIconName; | |
| const FALLBACK_ICON_NAME: DuoToneIconName = Object.keys( | |
| duoToneIcons, | |
| )[0] as DuoToneIconName; | |
| const getIconName = (name: string): DuoToneIconName => { | |
| const iconName = `d-${name}`; | |
| if (Object.prototype.hasOwnProperty.call(duoToneIcons, iconName)) { | |
| return iconName as DuoToneIconName; | |
| } | |
| return FALLBACK_ICON_NAME; | |
| }; |
Copilot
AI
Jan 24, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Tailwind utility classes shrink-0 flex should be ordered as flex shrink-0 according to Tailwind's recommended class ordering (layout modifiers before sizing utilities). This improves consistency and readability.
| <div className="shrink-0 flex items-center justify-center size-10 rounded-lg bg-primary-100 text-primary-700"> | |
| <div className="flex shrink-0 items-center justify-center size-10 rounded-lg bg-primary-100 text-primary-700"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add medical use-case and WCAG/accessibility notes for this reusable component.
Please document the clinical use case and keyboard/screen-reader expectations in a short JSDoc block on the component. As per coding guidelines.
📝 Example documentation
📝 Committable suggestion
🤖 Prompt for AI Agents