vchikalkin 540145d80a feat(service-card): add price and description fields to service data card
- Introduced NumberField for price input and TextareaField for service description in the ServiceDataCard component.
- Updated ServiceCard component to display the new description and price fields, enhancing service details visibility.
- Added formatMoney utility for consistent currency formatting across the application.
- Updated GraphQL fragments and types to include price and description fields for services.
2025-08-20 17:22:51 +03:00

58 lines
1.6 KiB
TypeScript

'use client';
import { useDebouncedOnChangeCallback, useFocus } from './hooks';
import { Input } from '@repo/ui/components/ui/input';
import { Label } from '@repo/ui/components/ui/label';
import { type ChangeEvent, useState } from 'react';
type FieldProps = {
readonly disabled?: boolean;
readonly id: string;
readonly label: string;
readonly onChange?: (value: null | number) => Promise<void> | void;
readonly readOnly?: boolean;
readonly value: null | number;
};
export function NumberField({
disabled = false,
id,
label,
onChange,
readOnly,
value: initialValue,
}: FieldProps) {
const [value, setValue] = useState(initialValue?.toString() ?? '');
const { debouncedCallback, isPending } = useDebouncedOnChangeCallback<null | number>(onChange);
const inputRef = useFocus(isPending);
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
const newValue = event.target.value;
setValue(newValue);
// Преобразуем строку в число или null
const numericValue = newValue === '' ? null : Number(newValue);
// Проверяем, что это валидное число
if (numericValue === null || (!Number.isNaN(numericValue) && numericValue >= 0)) {
debouncedCallback(numericValue);
}
};
return (
<div className="space-y-2">
<Label htmlFor={id}>{label}</Label>
<Input
className="bg-secondary outline-none focus:ring-0 focus:ring-offset-0"
disabled={disabled || isPending}
id={id}
onChange={handleChange}
readOnly={readOnly}
ref={inputRef}
type="number"
value={value}
/>
</div>
);
}