feat(WEB): 입력 컴포넌트 공통화 및 UI 개선
- 숫자/통화/전화번호/사업자번호 등 특수 입력 컴포넌트 추가 - MobileCard 컴포넌트 통합 (ListMobileCard 제거) - IntegratedListTemplateV2 페이지네이션 버그 수정 (NaN 이슈) - IntegratedDetailTemplate 타이틀 중복 수정 - 문서 시스템 컴포넌트 추가 - 헤더 벨 아이콘 포커스 스타일 개선 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,8 @@ import { Package, Settings, Plus, Trash2 } from "lucide-react";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { Button } from "../ui/button";
|
||||
import { Input } from "../ui/input";
|
||||
import { NumberInput } from "../ui/number-input";
|
||||
import { QuantityInput } from "../ui/quantity-input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -245,18 +247,16 @@ export function LocationDetailPanel({
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm text-gray-600 w-20">오픈사이즈</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<Input
|
||||
type="number"
|
||||
<NumberInput
|
||||
value={location.openWidth}
|
||||
onChange={(e) => handleFieldChange("openWidth", parseFloat(e.target.value) || 0)}
|
||||
onChange={(value) => handleFieldChange("openWidth", value ?? 0)}
|
||||
disabled={disabled}
|
||||
className="w-24 h-8 text-center font-bold"
|
||||
/>
|
||||
<span className="text-gray-400">×</span>
|
||||
<Input
|
||||
type="number"
|
||||
<NumberInput
|
||||
value={location.openHeight}
|
||||
onChange={(e) => handleFieldChange("openHeight", parseFloat(e.target.value) || 0)}
|
||||
onChange={(value) => handleFieldChange("openHeight", value ?? 0)}
|
||||
disabled={disabled}
|
||||
className="w-24 h-8 text-center font-bold"
|
||||
/>
|
||||
@@ -284,13 +284,12 @@ export function LocationDetailPanel({
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-gray-500">수량</span>
|
||||
<Input
|
||||
type="number"
|
||||
min="1"
|
||||
<QuantityInput
|
||||
value={location.quantity}
|
||||
onChange={(e) => handleFieldChange("quantity", parseInt(e.target.value) || 1)}
|
||||
onChange={(value) => handleFieldChange("quantity", value ?? 1)}
|
||||
disabled={disabled}
|
||||
className="w-24 h-7 text-center font-semibold"
|
||||
min={1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -366,12 +365,12 @@ export function LocationDetailPanel({
|
||||
<TableCell className="font-medium">{item.item_name}</TableCell>
|
||||
<TableCell className="text-center text-gray-600">{item.manufacture_size || "-"}</TableCell>
|
||||
<TableCell className="text-center">
|
||||
<Input
|
||||
type="number"
|
||||
defaultValue={item.quantity}
|
||||
<QuantityInput
|
||||
value={item.quantity}
|
||||
onChange={() => {}}
|
||||
className="w-16 h-8 text-center"
|
||||
min={1}
|
||||
readOnly={disabled}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
@@ -437,12 +436,12 @@ export function LocationDetailPanel({
|
||||
</Select>
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
<Input
|
||||
type="number"
|
||||
defaultValue={item.quantity}
|
||||
<QuantityInput
|
||||
value={item.quantity}
|
||||
onChange={() => {}}
|
||||
className="w-16 h-8 text-center"
|
||||
min={1}
|
||||
readOnly={disabled}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
@@ -495,12 +494,12 @@ export function LocationDetailPanel({
|
||||
<TableCell className="text-center text-gray-600">{item.type || "-"}</TableCell>
|
||||
<TableCell className="text-center text-gray-600">{item.spec || "-"}</TableCell>
|
||||
<TableCell className="text-center">
|
||||
<Input
|
||||
type="number"
|
||||
defaultValue={item.quantity}
|
||||
<QuantityInput
|
||||
value={item.quantity}
|
||||
onChange={() => {}}
|
||||
className="w-16 h-8 text-center"
|
||||
min={1}
|
||||
readOnly={disabled}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
@@ -563,12 +562,12 @@ export function LocationDetailPanel({
|
||||
</Select>
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
<Input
|
||||
type="number"
|
||||
defaultValue={item.quantity}
|
||||
<QuantityInput
|
||||
value={item.quantity}
|
||||
onChange={() => {}}
|
||||
className="w-16 h-8 text-center"
|
||||
min={1}
|
||||
readOnly={disabled}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="text-center">
|
||||
|
||||
@@ -14,6 +14,8 @@ import { toast } from "sonner";
|
||||
|
||||
import { Button } from "../ui/button";
|
||||
import { Input } from "../ui/input";
|
||||
import { NumberInput } from "../ui/number-input";
|
||||
import { QuantityInput } from "../ui/quantity-input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -398,21 +400,19 @@ export function LocationListPanel({
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-gray-600">가로</label>
|
||||
<Input
|
||||
type="number"
|
||||
<NumberInput
|
||||
placeholder="5000"
|
||||
value={formData.openWidth}
|
||||
onChange={(e) => handleFormChange("openWidth", e.target.value)}
|
||||
value={formData.openWidth ? Number(formData.openWidth) : undefined}
|
||||
onChange={(value) => handleFormChange("openWidth", value?.toString() ?? "")}
|
||||
className="h-8 text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-gray-600">세로</label>
|
||||
<Input
|
||||
type="number"
|
||||
<NumberInput
|
||||
placeholder="3000"
|
||||
value={formData.openHeight}
|
||||
onChange={(e) => handleFormChange("openHeight", e.target.value)}
|
||||
value={formData.openHeight ? Number(formData.openHeight) : undefined}
|
||||
onChange={(value) => handleFormChange("openHeight", value?.toString() ?? "")}
|
||||
className="h-8 text-sm"
|
||||
/>
|
||||
</div>
|
||||
@@ -436,12 +436,11 @@ export function LocationListPanel({
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-xs text-gray-600">수량</label>
|
||||
<Input
|
||||
type="number"
|
||||
min="1"
|
||||
value={formData.quantity}
|
||||
onChange={(e) => handleFormChange("quantity", e.target.value)}
|
||||
<QuantityInput
|
||||
value={formData.quantity ? Number(formData.quantity) : undefined}
|
||||
onChange={(value) => handleFormChange("quantity", value?.toString() ?? "")}
|
||||
className="h-8 text-sm"
|
||||
min={1}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -41,7 +41,7 @@ import {
|
||||
type StatCard,
|
||||
type ListParams,
|
||||
} from '@/components/templates/UniversalListPage';
|
||||
import { ListMobileCard, InfoField } from '@/components/organisms/ListMobileCard';
|
||||
import { ListMobileCard, InfoField } from '@/components/organisms/MobileCard';
|
||||
import { StandardDialog } from '@/components/molecules/StandardDialog';
|
||||
import {
|
||||
AlertDialog,
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
import { useState, useEffect, useMemo, useCallback } from "react";
|
||||
import { Input } from "../ui/input";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import { QuantityInput } from "../ui/quantity-input";
|
||||
import { CurrencyInput } from "../ui/currency-input";
|
||||
import { PhoneInput } from "../ui/phone-input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -697,11 +700,11 @@ export function QuoteRegistration({
|
||||
</FormField>
|
||||
|
||||
<FormField label="연락처" htmlFor="contact" type="custom">
|
||||
<Input
|
||||
<PhoneInput
|
||||
id="contact"
|
||||
placeholder="010-1234-5678"
|
||||
value={formData.contact}
|
||||
onChange={(e) => handleFieldChange("contact", e.target.value)}
|
||||
onChange={(value) => handleFieldChange("contact", value)}
|
||||
/>
|
||||
</FormField>
|
||||
</FormFieldGrid>
|
||||
@@ -983,14 +986,13 @@ export function QuoteRegistration({
|
||||
error={errors[`item-${activeItemIndex}-quantity`]}
|
||||
htmlFor={`quantity-${activeItemIndex}`}
|
||||
>
|
||||
<Input
|
||||
<QuantityInput
|
||||
id={`quantity-${activeItemIndex}`}
|
||||
type="number"
|
||||
min="1"
|
||||
value={formData.items[activeItemIndex].quantity}
|
||||
onChange={(e) =>
|
||||
handleItemChange(activeItemIndex, "quantity", parseInt(e.target.value) || 1)
|
||||
onChange={(value) =>
|
||||
handleItemChange(activeItemIndex, "quantity", value ?? 1)
|
||||
}
|
||||
min={1}
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
@@ -1014,13 +1016,12 @@ export function QuoteRegistration({
|
||||
type="custom"
|
||||
htmlFor={`inspectionFee-${activeItemIndex}`}
|
||||
>
|
||||
<Input
|
||||
<CurrencyInput
|
||||
id={`inspectionFee-${activeItemIndex}`}
|
||||
type="number"
|
||||
placeholder="예: 50000"
|
||||
value={formData.items[activeItemIndex].inspectionFee}
|
||||
onChange={(e) =>
|
||||
handleItemChange(activeItemIndex, "inspectionFee", parseInt(e.target.value) || 0)
|
||||
onChange={(value) =>
|
||||
handleItemChange(activeItemIndex, "inspectionFee", value ?? 0)
|
||||
}
|
||||
/>
|
||||
</FormField>
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Button } from "../ui/button";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { Input } from "../ui/input";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
import { PhoneInput } from "../ui/phone-input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -503,10 +504,10 @@ export function QuoteRegistrationV2({
|
||||
</div>
|
||||
<div>
|
||||
<label className="text-sm font-medium text-gray-700">연락처</label>
|
||||
<Input
|
||||
<PhoneInput
|
||||
placeholder="010-1234-5678"
|
||||
value={formData.contact}
|
||||
onChange={(e) => handleFieldChange("contact", e.target.value)}
|
||||
onChange={(value) => handleFieldChange("contact", value)}
|
||||
disabled={isViewMode}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user