- Implemented TransactionIndex component for displaying transactions with pagination and filtering options. - Created TransactionCardRow component for rendering individual transaction details. - Added TransactionItemSubRow component for displaying detailed order items within a transaction. - Integrated delete confirmation dialog for transaction deletion. - Updated routes to include transaction management with appropriate permissions.
103 lines
2.5 KiB
TypeScript
103 lines
2.5 KiB
TypeScript
import { useCallback, useRef, useState } from 'react';
|
|
import {
|
|
InputGroup,
|
|
InputGroupAddon,
|
|
InputGroupInput,
|
|
InputGroupText,
|
|
} from '@/components/ui/input-group';
|
|
|
|
type RupiahInputProps = {
|
|
name?: string;
|
|
id?: string;
|
|
defaultValue?: number;
|
|
value?: number;
|
|
onValueChange?: (value: number) => void;
|
|
placeholder?: string;
|
|
disabled?: boolean;
|
|
min?: number;
|
|
max?: number;
|
|
className?: string;
|
|
};
|
|
|
|
function formatRupiah(value: number): string {
|
|
return value.toLocaleString('id-ID');
|
|
}
|
|
|
|
function parseRupiah(value: string): number {
|
|
const cleaned = value.replace(/[^0-9]/g, '');
|
|
|
|
return cleaned === '' ? 0 : parseInt(cleaned, 10);
|
|
}
|
|
|
|
export function RupiahInput({
|
|
name,
|
|
id,
|
|
defaultValue = 0,
|
|
value,
|
|
onValueChange,
|
|
placeholder = '0',
|
|
disabled = false,
|
|
min,
|
|
max,
|
|
className,
|
|
}: RupiahInputProps) {
|
|
const isControlled = value !== undefined;
|
|
const [displayValue, setDisplayValue] = useState(
|
|
formatRupiah(isControlled ? value : defaultValue),
|
|
);
|
|
const lastValidRef = useRef(isControlled ? value : defaultValue);
|
|
|
|
if (isControlled) {
|
|
const formatted = formatRupiah(value);
|
|
|
|
if (formatted !== displayValue) {
|
|
setDisplayValue(formatted);
|
|
lastValidRef.current = value;
|
|
}
|
|
}
|
|
|
|
const handleChange = useCallback(
|
|
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const raw = parseRupiah(e.target.value);
|
|
let clamped = raw;
|
|
|
|
if (min !== undefined && raw < min) {
|
|
clamped = min;
|
|
}
|
|
|
|
if (max !== undefined && raw > max) {
|
|
clamped = max;
|
|
}
|
|
|
|
lastValidRef.current = clamped;
|
|
setDisplayValue(formatRupiah(clamped));
|
|
onValueChange?.(clamped);
|
|
},
|
|
[min, max, onValueChange],
|
|
);
|
|
|
|
const handleBlur = useCallback(() => {
|
|
setDisplayValue(formatRupiah(lastValidRef.current));
|
|
}, []);
|
|
|
|
return (
|
|
<InputGroup className={className}>
|
|
<InputGroupAddon>
|
|
<InputGroupText>Rp</InputGroupText>
|
|
</InputGroupAddon>
|
|
<InputGroupInput
|
|
name={name}
|
|
id={id}
|
|
type="text"
|
|
inputMode="numeric"
|
|
value={displayValue}
|
|
onChange={handleChange}
|
|
onBlur={handleBlur}
|
|
placeholder={placeholder}
|
|
disabled={disabled}
|
|
autoComplete="off"
|
|
/>
|
|
</InputGroup>
|
|
);
|
|
}
|