feat(WEB): 입력 컴포넌트 공통화 및 UI 개선

- 숫자/통화/전화번호/사업자번호 등 특수 입력 컴포넌트 추가
- MobileCard 컴포넌트 통합 (ListMobileCard 제거)
- IntegratedListTemplateV2 페이지네이션 버그 수정 (NaN 이슈)
- IntegratedDetailTemplate 타이틀 중복 수정
- 문서 시스템 컴포넌트 추가
- 헤더 벨 아이콘 포커스 스타일 개선

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-01-21 20:56:17 +09:00
parent cfa72fe19b
commit 835c06ce94
190 changed files with 8575 additions and 2354 deletions

View File

@@ -30,6 +30,8 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { CurrencyInput } from '@/components/ui/currency-input';
import { NumberInput } from '@/components/ui/number-input';
import { Badge } from '@/components/ui/badge';
import { Separator } from '@/components/ui/separator';
import {
@@ -477,21 +479,15 @@ export function PricingFormClient({
<Label>
<span className="text-red-500">*</span>
</Label>
<div className="relative">
<Input
type="number"
value={purchasePrice || ''}
onChange={(e) => {
setPurchasePrice(parseInt(e.target.value) || 0);
setErrors((prev) => { const n = {...prev}; delete n.purchasePrice; delete n.salesPrice; return n; });
}}
placeholder="0"
className={errors.purchasePrice ? 'border-red-500 pr-12' : 'pr-12'}
/>
<span className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground">
</span>
</div>
<CurrencyInput
value={purchasePrice}
onChange={(value) => {
setPurchasePrice(value ?? 0);
setErrors((prev) => { const n = {...prev}; delete n.purchasePrice; delete n.salesPrice; return n; });
}}
placeholder="0"
error={!!errors.purchasePrice}
/>
</div>
<div>
<Label></Label>
@@ -514,13 +510,13 @@ export function PricingFormClient({
<div>
<Label>LOSS (%)</Label>
<div className="relative">
<Input
type="number"
step="0.1"
value={loss || ''}
onChange={(e) => setLoss(parseFloat(e.target.value) || 0)}
<NumberInput
value={loss}
onChange={(value) => setLoss(value ?? 0)}
placeholder="0"
className="pr-12"
step={0.1}
allowDecimal
/>
<span className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground">
%
@@ -532,18 +528,11 @@ export function PricingFormClient({
</div>
<div>
<Label></Label>
<div className="relative">
<Input
type="number"
value={processingCost || ''}
onChange={(e) => setProcessingCost(parseInt(e.target.value) || 0)}
placeholder="0"
className="pr-12"
/>
<span className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground">
</span>
</div>
<CurrencyInput
value={processingCost}
onChange={(value) => setProcessingCost(value ?? 0)}
placeholder="0"
/>
</div>
</div>
@@ -637,13 +626,13 @@ export function PricingFormClient({
<div>
<Label> (%)</Label>
<div className="relative">
<Input
type="number"
step="0.1"
value={marginRate || ''}
onChange={(e) => handleMarginRateChange(parseFloat(e.target.value) || 0)}
<NumberInput
value={marginRate}
onChange={(value) => handleMarginRateChange(value ?? 0)}
placeholder="0"
className="pr-12"
step={0.1}
allowDecimal
/>
<span className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground">
%
@@ -657,21 +646,15 @@ export function PricingFormClient({
<Label>
<span className="text-red-500">*</span>
</Label>
<div className="relative">
<Input
type="number"
value={salesPrice || ''}
onChange={(e) => {
handleSalesPriceChange(parseInt(e.target.value) || 0);
setErrors((prev) => { const n = {...prev}; delete n.purchasePrice; delete n.salesPrice; return n; });
}}
placeholder="0"
className={errors.salesPrice ? 'border-red-500 pr-12' : 'pr-12'}
/>
<span className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground">
</span>
</div>
<CurrencyInput
value={salesPrice}
onChange={(value) => {
handleSalesPriceChange(value ?? 0);
setErrors((prev) => { const n = {...prev}; delete n.purchasePrice; delete n.salesPrice; return n; });
}}
placeholder="0"
error={!!errors.salesPrice}
/>
<p className="text-xs text-muted-foreground mt-1">
</p>

View File

@@ -29,7 +29,7 @@ import {
type TableColumn,
type StatCard,
} from '@/components/templates/UniversalListPage';
import { ListMobileCard, InfoField } from '@/components/organisms/ListMobileCard';
import { ListMobileCard, InfoField } from '@/components/organisms/MobileCard';
import type { PricingListItem, ItemType } from './types';
import { ITEM_TYPE_LABELS, ITEM_TYPE_COLORS } from './types';