refactor: update pricing input handling to use PriceInput component

- Replaced raw input fields for pricing with PriceInput component across various forms and modals to ensure consistent formatting and accessibility.
- Updated tests to reflect changes in pricing input handling, ensuring values are displayed in toman with proper formatting.
- Enhanced accessibility by adding aria-labels and aria-invalid attributes to PriceInput components.
- Adjusted UI elements to improve layout and user experience, particularly in forms related to service items and scheduling.
- Changed labels from "نمایش در نوبت‌دهی" to "نمایش در نوبت‌دهی آنلاین" for clarity.
This commit is contained in:
hamed
2026-08-09 08:38:23 +03:30
parent e48ab9a974
commit b49abee52f
18 changed files with 299 additions and 96 deletions
@@ -9,6 +9,9 @@ import ResourceServicesPanel from './ResourceServicesPanel';
const resource = { uuid: 'r-1', name: 'دستگاه شمارهٔ ۲' } as never;
/** PriceInput مبلغ را با جداکنندهٔ fa-IR نشان می‌دهد، نه رقمِ خام. */
const fa = (n: number) => new Intl.NumberFormat('fa-IR').format(n);
const services = [
{ uuid: 's-1', name: 'لیزر پا', duration_minutes: 30, price_rials: 8_000_000 },
{ uuid: 's-2', name: 'لیزر دست', duration_minutes: 20, price_rials: 5_000_000 },
@@ -57,12 +60,17 @@ describe('ResourceServicesPanel', () => {
expect(price).toHaveValue('');
});
it('override را ذخیره می‌کند', async () => {
it('قیمت به تومان گرفته می‌شود و به ریال ذخیره می‌شود', async () => {
const onSave = vi.fn();
renderWithProviders(<ResourceServicesPanel {...props({ onSave })} />);
// برچسب و placeholder هر دو تومان‌اند؛ پیش از این برچسب «ریال» بود و ورودی
// کاربر ده برابر کمتر از چیزی می‌شد که فکر می‌کرد وارد کرده.
expect(screen.getByText('قیمت (تومان)')).toBeInTheDocument();
expect(screen.queryByText('قیمت (ریال)')).not.toBeInTheDocument();
const price = screen.getByPlaceholderText(/پیش‌فرض سرویس/);
await userEvent.type(price, '9500000');
await userEvent.type(price, '950000');
await userEvent.click(screen.getByRole('button', { name: 'ذخیره' }));
expect(onSave).toHaveBeenCalledWith([
@@ -70,6 +78,33 @@ describe('ResourceServicesPanel', () => {
]);
});
it('قیمتِ override شده به تومان در فیلد می‌نشیند', () => {
renderWithProviders(<ResourceServicesPanel {...props({
offerings: [{ ...offerings[0], price_rials: 9_500_000, price_source: 'resource_option' }] as never,
})} />);
expect(screen.getByDisplayValue(fa(950_000))).toBeInTheDocument();
});
it('سرویس تازه با مدت و قیمت پیش‌فرض خودش پر می‌شود', async () => {
const onSave = vi.fn();
renderWithProviders(<ResourceServicesPanel {...props({ onSave })} />);
await userEvent.click(screen.getByText('یک سرویس انتخاب کنید'));
await userEvent.click(await screen.findByText('لیزر دست'));
// ۵٬۰۰۰٬۰۰۰ ریال = ۵۰۰٬۰۰۰ تومان
expect(screen.getByDisplayValue(fa(500_000))).toBeInTheDocument();
expect(screen.getByDisplayValue('20')).toBeInTheDocument();
await userEvent.click(screen.getByRole('button', { name: 'ذخیره' }));
expect(onSave).toHaveBeenCalledWith([
{ service_uuid: 's-1', duration_minutes: '15', price_rials: '', active: true },
{ service_uuid: 's-2', duration_minutes: '20', price_rials: '5000000', active: true },
]);
});
it('پاک‌کردن مقدار یعنی بازگشت به ارث، نه صفر', async () => {
const onSave = vi.fn();
renderWithProviders(<ResourceServicesPanel {...props({ onSave })} />);