feat: update session payment logic to ensure accurate payable amounts and reflect consumables in cost breakdown

- Adjusted the calculation of payable amounts in PaymentStep to align with server logic, ensuring overpayments are handled correctly.
- Enhanced DetailsStep to include consumables in the itemized cost breakdown, ensuring consistency with patient share calculations.
- Updated tests for SessionPaymentPage to validate new behavior regarding overpayments and consumable listings.
- Modified PatientController to register SessionPayment correctly when settling sessions via wallet, preventing double charges.
- Refactored WalletService to remove outdated methods and ensure wallet transactions reflect the correct amounts after discounts.
- Improved accessibility in SearchableSelect component by adding aria labels and ensuring proper role attributes for screen readers.
- Updated styles to ensure minimum touch targets meet WCAG guidelines for mobile usability.
This commit is contained in:
hamed
2026-07-19 13:29:46 +03:30
parent 0d9784b03a
commit 773f9d4d16
22 changed files with 251 additions and 70 deletions
@@ -18,6 +18,14 @@ interface Props {
noOptionsMessage?: string;
inputId?: string;
height?: number;
/**
* نام دسترس‌پذیر فیلد. react-select ورودی داخلی خودش را بدون label رندر می‌کند و
* placeholder را هم به‌صورت div می‌گذارد نه attribute، پس بدون این، فیلد برای
* screen reader بی‌نام می‌ماند. پیش‌فرض روی placeholder می‌افتد.
*/
ariaLabel?: string;
/** اگر label قابل‌مشاهده‌ای وجود دارد، id آن را بده (بر ariaLabel اولویت دارد). */
ariaLabelledBy?: string;
}
export default function SearchableSelect({
@@ -31,6 +39,8 @@ export default function SearchableSelect({
noOptionsMessage = 'موردی یافت نشد',
inputId,
height = 42,
ariaLabel,
ariaLabelledBy,
}: Props) {
const darkMode = useUiStore((s) => s.darkMode);
@@ -118,6 +128,8 @@ export default function SearchableSelect({
noOptionsMessage={() => noOptionsMessage}
loadingMessage={() => 'در حال بارگذاری...'}
inputId={inputId}
aria-labelledby={ariaLabelledBy}
aria-label={ariaLabelledBy ? undefined : (ariaLabel ?? placeholder)}
/>
);
}