feat(WEB): 입력 컴포넌트 공통화 및 UI 개선
- 숫자/통화/전화번호/사업자번호 등 특수 입력 컴포넌트 추가 - MobileCard 컴포넌트 통합 (ListMobileCard 제거) - IntegratedListTemplateV2 페이지네이션 버그 수정 (NaN 이슈) - IntegratedDetailTemplate 타이틀 중복 수정 - 문서 시스템 컴포넌트 추가 - 헤더 벨 아이콘 포커스 스타일 개선 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user