Files
vexplor/frontend/components/ui/input.tsx
kjs 6ddc84f285 Update date handling in inventory and sales order pages
- Refactored date handling in the InventoryStatusPage to use `toLocaleString` for transaction dates and last in dates, ensuring correct timezone formatting.
- Introduced FormDatePicker in SalesOrderPage for date inputs, enhancing user experience with automatic formatting and improved date handling.
- Added a checkbox for filtering items by customer in SalesOrderPage, allowing users to view only items registered for the selected customer.

This update improves date accuracy and user interaction in the inventory and sales order modules.
2026-04-29 18:20:01 +09:00

110 lines
4.4 KiB
TypeScript

import * as React from "react";
import { cn } from "@/lib/utils";
import { FormDatePicker } from "@/components/screen/filters/FormDatePicker";
export interface InputProps extends React.ComponentProps<"input"> {
label?: string; // 툴팁에 표시할 라벨
enableEnterNavigation?: boolean; // Enter 키로 다음 필드 이동 활성화
}
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, label, enableEnterNavigation = false, onKeyDown, ...props }, ref) => {
// type="date" / "datetime-local" → 공통 FormDatePicker로 자동 위임 (타이핑 자릿수 자동 진행)
if (type === "date" || type === "datetime-local") {
const fakeEvent = (v: string) => ({
target: { value: v },
currentTarget: { value: v },
} as unknown as React.ChangeEvent<HTMLInputElement>);
return (
<FormDatePicker
id={(props as any).id}
value={(props.value as string) || ""}
onChange={(v) => props.onChange?.(fakeEvent(v))}
placeholder={(props as any).placeholder}
disabled={props.disabled}
readOnly={props.readOnly}
includeTime={type === "datetime-local"}
/>
);
}
const [showTooltip, setShowTooltip] = React.useState(false);
const [tooltipPosition, setTooltipPosition] = React.useState({ x: 0, y: 0 });
const handleMouseMove = (e: React.MouseEvent<HTMLInputElement>) => {
if (label) {
setTooltipPosition({ x: e.clientX, y: e.clientY });
}
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
// Enter 키 네비게이션
if (enableEnterNavigation && e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
// 현재 input의 form 내에서 다음 input 찾기
const form = e.currentTarget.form;
if (form) {
const inputs = Array.from(form.querySelectorAll('input:not([disabled]):not([readonly]), select:not([disabled]), textarea:not([disabled]):not([readonly])')) as HTMLElement[];
const currentIndex = inputs.indexOf(e.currentTarget);
if (currentIndex !== -1 && currentIndex < inputs.length - 1) {
// 다음 input으로 포커스 이동
inputs[currentIndex + 1].focus();
}
} else {
// form이 없는 경우, 문서 전체에서 다음 input 찾기
const allInputs = Array.from(document.querySelectorAll('input:not([disabled]):not([readonly]), select:not([disabled]), textarea:not([disabled]):not([readonly])')) as HTMLElement[];
const currentIndex = allInputs.indexOf(e.currentTarget);
if (currentIndex !== -1 && currentIndex < allInputs.length - 1) {
allInputs[currentIndex + 1].focus();
}
}
}
// 기존 onKeyDown 핸들러 호출
onKeyDown?.(e);
};
return (
<>
<input
ref={ref}
type={type}
data-slot="input"
className={cn(
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-10 w-full min-w-0 rounded-md border bg-transparent px-3 py-2 text-sm transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className,
)}
onMouseEnter={() => label && setShowTooltip(true)}
onMouseLeave={() => setShowTooltip(false)}
onMouseMove={handleMouseMove}
onKeyDown={handleKeyDown}
{...props}
/>
{/* 툴팁 */}
{showTooltip && label && (
<div
className="pointer-events-none fixed z-[9999] rounded-md bg-popover px-3 py-1.5 text-xs text-popover-foreground shadow-md border border-border"
style={{
left: `${tooltipPosition.x + 10}px`,
top: `${tooltipPosition.y - 30}px`,
}}
>
{label}
</div>
)}
</>
);
}
);
Input.displayName = "Input";
export { Input };