fix: 품목관리 수정 기능 버그 수정 및 Sales 페이지 추가
## 품목관리 수정 버그 수정 - FG(제품) 수정 시 품목명 반영 안되는 문제 해결 - productName → name 필드 매핑 추가 - FG 품목코드 = 품목명 동기화 로직 추가 - Materials(SM, RM, CS) 수정페이지 진입 오류 해결 - UNIQUE 제약조건 위반 오류 해결 ## Sales 페이지 - 거래처관리 (client-management-sales-admin) 페이지 구현 - 견적관리 (quote-management) 페이지 구현 - 관련 컴포넌트 및 훅 추가 ## 기타 - 회원가입 페이지 차단 처리 - 디버깅용 콘솔 로그 추가 (PUT 요청/응답 확인용) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
247
src/components/clients/ClientDetail.tsx
Normal file
247
src/components/clients/ClientDetail.tsx
Normal file
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* 거래처 상세 보기 컴포넌트
|
||||
*
|
||||
* 스크린샷 기준 4개 섹션:
|
||||
* 1. 기본 정보
|
||||
* 2. 연락처 정보
|
||||
* 3. 결제 정보
|
||||
* 4. 악성채권 정보 (있는 경우 빨간 테두리)
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { Button } from "../ui/button";
|
||||
import { Badge } from "../ui/badge";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card";
|
||||
import {
|
||||
Building2,
|
||||
Phone,
|
||||
CreditCard,
|
||||
AlertTriangle,
|
||||
ArrowLeft,
|
||||
Pencil,
|
||||
Trash2,
|
||||
MapPin,
|
||||
Mail,
|
||||
} from "lucide-react";
|
||||
import { Client } from "../../hooks/useClientList";
|
||||
|
||||
interface ClientDetailProps {
|
||||
client: Client;
|
||||
onBack: () => void;
|
||||
onEdit: () => void;
|
||||
onDelete: () => void;
|
||||
}
|
||||
|
||||
// 상세 항목 표시 컴포넌트
|
||||
function DetailItem({
|
||||
label,
|
||||
value,
|
||||
icon,
|
||||
valueClassName,
|
||||
}: {
|
||||
label: string;
|
||||
value: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
valueClassName?: string;
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<p className="text-xs text-muted-foreground mb-1">{label}</p>
|
||||
<div className={`flex items-center gap-2 ${valueClassName || ""}`}>
|
||||
{icon}
|
||||
<span className="font-medium">{value || "-"}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ClientDetail({
|
||||
client,
|
||||
onBack,
|
||||
onEdit,
|
||||
onDelete,
|
||||
}: ClientDetailProps) {
|
||||
const router = useRouter();
|
||||
|
||||
// 금액 포맷
|
||||
const formatCurrency = (amount: string) => {
|
||||
if (!amount) return "-";
|
||||
const num = Number(amount);
|
||||
return `₩${num.toLocaleString()}`;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* 헤더 */}
|
||||
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<Building2 className="h-6 w-6 text-primary" />
|
||||
<h1 className="text-2xl font-bold">{client.name}</h1>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" onClick={onBack}>
|
||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||
목록
|
||||
</Button>
|
||||
<Button variant="outline" onClick={onEdit}>
|
||||
<Pencil className="h-4 w-4 mr-2" />
|
||||
수정
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={onDelete}>
|
||||
<Trash2 className="h-4 w-4 mr-2" />
|
||||
삭제
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 1. 기본 정보 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-2">
|
||||
<Building2 className="h-5 w-5 text-primary" />
|
||||
<CardTitle>기본 정보</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<DetailItem label="CODE" value={client.code} />
|
||||
<DetailItem label="사업자번호" value={client.businessNo} />
|
||||
<DetailItem
|
||||
label="거래처 유형"
|
||||
value={
|
||||
<Badge
|
||||
variant={
|
||||
client.clientType === "매출"
|
||||
? "default"
|
||||
: client.clientType === "매입"
|
||||
? "secondary"
|
||||
: "outline"
|
||||
}
|
||||
>
|
||||
{client.clientType}
|
||||
</Badge>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<DetailItem label="거래처명" value={client.name} />
|
||||
<DetailItem label="대표자" value={client.representative} />
|
||||
<DetailItem
|
||||
label="상태"
|
||||
value={
|
||||
<Badge
|
||||
variant={client.status === "활성" ? "default" : "secondary"}
|
||||
className={
|
||||
client.status === "활성"
|
||||
? "bg-green-100 text-green-800"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{client.status}
|
||||
</Badge>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<DetailItem
|
||||
label="주소"
|
||||
value={client.address}
|
||||
icon={<MapPin className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
<DetailItem label="업태" value={client.businessType} />
|
||||
<DetailItem label="종목" value={client.businessItem} />
|
||||
</div>
|
||||
|
||||
{client.memo && (
|
||||
<DetailItem label="비고" value={client.memo} />
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 2. 연락처 정보 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-2">
|
||||
<Phone className="h-5 w-5 text-primary" />
|
||||
<CardTitle>연락처 정보</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<DetailItem
|
||||
label="전화"
|
||||
value={client.phone}
|
||||
icon={<Phone className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
<DetailItem
|
||||
label="휴대전화"
|
||||
value={client.mobile}
|
||||
icon={<Phone className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||
<DetailItem label="팩스" value={client.fax} />
|
||||
<DetailItem
|
||||
label="이메일"
|
||||
value={client.email}
|
||||
icon={<Mail className="h-4 w-4 text-muted-foreground" />}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||
<DetailItem label="담당자명" value={client.managerName} />
|
||||
<DetailItem label="담당자 연락처" value={client.managerTel} />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 3. 결제 정보 */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-2">
|
||||
<CreditCard className="h-5 w-5 text-primary" />
|
||||
<CardTitle>결제 정보</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<DetailItem label="매입 결제일" value={client.purchasePaymentDay} />
|
||||
<DetailItem label="매출 결제일" value={client.salesPaymentDay} />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* 4. 악성채권 정보 (있는 경우에만 표시) */}
|
||||
{client.badDebt && (
|
||||
<Card className="border-red-300 bg-red-50/30">
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-2">
|
||||
<AlertTriangle className="h-5 w-5 text-red-500" />
|
||||
<CardTitle className="text-red-700">악성채권 정보</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<DetailItem
|
||||
label="악성채권 금액"
|
||||
value={formatCurrency(client.badDebtAmount)}
|
||||
valueClassName="text-red-600 font-bold"
|
||||
/>
|
||||
<DetailItem label="수령일" value={client.badDebtReceiveDate} />
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-4">
|
||||
<DetailItem label="종료일" value={client.badDebtEndDate} />
|
||||
<DetailItem label="진행 상태" value={client.badDebtProgress} />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user