refactor: Simplify customer form validation and remove unused fields
- Updated the customer form validation to only check for the business number, removing checks for contact phone and email. - Removed unused input fields for contact person, phone, and email from the customer management page to streamline the form and improve user experience. - This change aims to enhance the clarity and usability of the customer management interface across multiple company implementations.
This commit is contained in:
@@ -715,7 +715,7 @@ export default function CustomerManagementPage() {
|
||||
const handleCustomerSave = async () => {
|
||||
if (!customerForm.customer_name) { toast.error("거래처명은 필수입니다."); return; }
|
||||
if (!customerForm.status) { toast.error("상태는 필수입니다."); return; }
|
||||
const errors = validateForm(customerForm, ["contact_phone", "email", "business_number"]);
|
||||
const errors = validateForm(customerForm, ["business_number"]);
|
||||
setFormErrors(errors);
|
||||
if (Object.keys(errors).length > 0) {
|
||||
toast.error("입력 형식을 확인해주세요.");
|
||||
@@ -1877,35 +1877,6 @@ export default function CustomerManagementPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">거래처담당자</Label>
|
||||
<Input
|
||||
value={customerForm.contact_person || ""}
|
||||
onChange={(e) => setCustomerForm((p) => ({ ...p, contact_person: e.target.value }))}
|
||||
placeholder="거래처담당자"
|
||||
className="h-9"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">전화번호</Label>
|
||||
<Input
|
||||
value={customerForm.contact_phone || ""}
|
||||
onChange={(e) => handleFormChange("contact_phone", e.target.value)}
|
||||
placeholder="010-0000-0000"
|
||||
className={cn("h-9", formErrors.contact_phone && "border-destructive")}
|
||||
/>
|
||||
{formErrors.contact_phone && <p className="text-xs text-destructive">{formErrors.contact_phone}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">이메일</Label>
|
||||
<Input
|
||||
value={customerForm.email || ""}
|
||||
onChange={(e) => handleFormChange("email", e.target.value)}
|
||||
placeholder="example@email.com"
|
||||
className={cn("h-9", formErrors.email && "border-destructive")}
|
||||
/>
|
||||
{formErrors.email && <p className="text-xs text-destructive">{formErrors.email}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">사업자번호</Label>
|
||||
<Input
|
||||
|
||||
@@ -517,29 +517,44 @@ export default function SalesOrderPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// 삭제 (마스터 단위)
|
||||
// 삭제 (선택한 디테일 삭제 → 디테일 0건인 마스터 자동 삭제)
|
||||
const handleDelete = async () => {
|
||||
if (checkedIds.length === 0) { toast.error("삭제할 수주를 선택해주세요."); return; }
|
||||
const selectedItems = orders.filter((o) => checkedIds.includes(o.id));
|
||||
const orderNos = [...new Set(selectedItems.map((o) => o.order_no))];
|
||||
const ok = await confirm(`${orderNos.length}건의 수주를 삭제하시겠습니까?`, {
|
||||
if (checkedIds.length === 0) { toast.error("삭제할 항목을 선택해주세요."); return; }
|
||||
const ok = await confirm(`${checkedIds.length}건의 수주 항목을 삭제하시겠습니까?`, {
|
||||
description: "삭제된 데이터는 복구할 수 없습니다.",
|
||||
variant: "destructive",
|
||||
confirmText: "삭제",
|
||||
});
|
||||
if (!ok) return;
|
||||
try {
|
||||
// 1. 선택한 디테일 삭제
|
||||
await apiClient.delete(`/table-management/tables/${DETAIL_TABLE}/delete`, {
|
||||
data: checkedIds.map((id) => ({ id })),
|
||||
});
|
||||
|
||||
// 2. 영향받는 수주번호의 잔여 디테일 확인 → 0건이면 마스터도 삭제
|
||||
const selectedItems = orders.filter((o) => checkedIds.includes(o.id));
|
||||
const orderNos = [...new Set(selectedItems.map((o) => o.order_no))];
|
||||
for (const orderNo of orderNos) {
|
||||
const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, {
|
||||
const remainRes = await apiClient.post(`/table-management/tables/${DETAIL_TABLE}/data`, {
|
||||
page: 1, size: 1,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || [];
|
||||
if (masters.length > 0) {
|
||||
await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, {
|
||||
data: masters.map((m: any) => ({ id: m.id })),
|
||||
const remaining = remainRes.data?.data?.data || remainRes.data?.data?.rows || [];
|
||||
if (remaining.length === 0) {
|
||||
// 디테일 0건 → 마스터 삭제
|
||||
const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, {
|
||||
page: 1, size: 1,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || [];
|
||||
if (masters.length > 0) {
|
||||
await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, {
|
||||
data: masters.map((m: any) => ({ id: m.id })),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
toast.success("삭제되었습니다.");
|
||||
@@ -918,7 +933,7 @@ export default function SalesOrderPage() {
|
||||
{/* 데이터 테이블 (플랫 리스트) */}
|
||||
<div className="flex-1 overflow-hidden rounded-lg border border-border bg-card">
|
||||
<div className="h-full overflow-auto">
|
||||
<Table style={{ minWidth: "1500px" }}>
|
||||
<Table noWrapper style={{ minWidth: "1500px" }}>
|
||||
<colgroup>
|
||||
<col style={{ width: "40px" }} />
|
||||
<col style={{ width: "140px" }} />
|
||||
@@ -1107,6 +1122,7 @@ export default function SalesOrderPage() {
|
||||
<Select value={masterForm.input_mode || ""} onValueChange={(v) => {
|
||||
setMasterForm((p) => {
|
||||
const next = { ...p, input_mode: v };
|
||||
// 입력방식 변경 시 거래처 관련 값 초기화
|
||||
delete next.partner_id;
|
||||
delete next.delivery_partner_id;
|
||||
delete next.delivery_address;
|
||||
|
||||
@@ -715,7 +715,7 @@ export default function CustomerManagementPage() {
|
||||
const handleCustomerSave = async () => {
|
||||
if (!customerForm.customer_name) { toast.error("거래처명은 필수입니다."); return; }
|
||||
if (!customerForm.status) { toast.error("상태는 필수입니다."); return; }
|
||||
const errors = validateForm(customerForm, ["contact_phone", "email", "business_number"]);
|
||||
const errors = validateForm(customerForm, ["business_number"]);
|
||||
setFormErrors(errors);
|
||||
if (Object.keys(errors).length > 0) {
|
||||
toast.error("입력 형식을 확인해주세요.");
|
||||
@@ -1877,35 +1877,6 @@ export default function CustomerManagementPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">거래처담당자</Label>
|
||||
<Input
|
||||
value={customerForm.contact_person || ""}
|
||||
onChange={(e) => setCustomerForm((p) => ({ ...p, contact_person: e.target.value }))}
|
||||
placeholder="거래처담당자"
|
||||
className="h-9"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">전화번호</Label>
|
||||
<Input
|
||||
value={customerForm.contact_phone || ""}
|
||||
onChange={(e) => handleFormChange("contact_phone", e.target.value)}
|
||||
placeholder="010-0000-0000"
|
||||
className={cn("h-9", formErrors.contact_phone && "border-destructive")}
|
||||
/>
|
||||
{formErrors.contact_phone && <p className="text-xs text-destructive">{formErrors.contact_phone}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">이메일</Label>
|
||||
<Input
|
||||
value={customerForm.email || ""}
|
||||
onChange={(e) => handleFormChange("email", e.target.value)}
|
||||
placeholder="example@email.com"
|
||||
className={cn("h-9", formErrors.email && "border-destructive")}
|
||||
/>
|
||||
{formErrors.email && <p className="text-xs text-destructive">{formErrors.email}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">사업자번호</Label>
|
||||
<Input
|
||||
|
||||
@@ -517,29 +517,44 @@ export default function SalesOrderPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// 삭제 (마스터 단위)
|
||||
// 삭제 (선택한 디테일 삭제 → 디테일 0건인 마스터 자동 삭제)
|
||||
const handleDelete = async () => {
|
||||
if (checkedIds.length === 0) { toast.error("삭제할 수주를 선택해주세요."); return; }
|
||||
const selectedItems = orders.filter((o) => checkedIds.includes(o.id));
|
||||
const orderNos = [...new Set(selectedItems.map((o) => o.order_no))];
|
||||
const ok = await confirm(`${orderNos.length}건의 수주를 삭제하시겠습니까?`, {
|
||||
if (checkedIds.length === 0) { toast.error("삭제할 항목을 선택해주세요."); return; }
|
||||
const ok = await confirm(`${checkedIds.length}건의 수주 항목을 삭제하시겠습니까?`, {
|
||||
description: "삭제된 데이터는 복구할 수 없습니다.",
|
||||
variant: "destructive",
|
||||
confirmText: "삭제",
|
||||
});
|
||||
if (!ok) return;
|
||||
try {
|
||||
// 1. 선택한 디테일 삭제
|
||||
await apiClient.delete(`/table-management/tables/${DETAIL_TABLE}/delete`, {
|
||||
data: checkedIds.map((id) => ({ id })),
|
||||
});
|
||||
|
||||
// 2. 영향받는 수주번호의 잔여 디테일 확인 → 0건이면 마스터도 삭제
|
||||
const selectedItems = orders.filter((o) => checkedIds.includes(o.id));
|
||||
const orderNos = [...new Set(selectedItems.map((o) => o.order_no))];
|
||||
for (const orderNo of orderNos) {
|
||||
const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, {
|
||||
const remainRes = await apiClient.post(`/table-management/tables/${DETAIL_TABLE}/data`, {
|
||||
page: 1, size: 1,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || [];
|
||||
if (masters.length > 0) {
|
||||
await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, {
|
||||
data: masters.map((m: any) => ({ id: m.id })),
|
||||
const remaining = remainRes.data?.data?.data || remainRes.data?.data?.rows || [];
|
||||
if (remaining.length === 0) {
|
||||
// 디테일 0건 → 마스터 삭제
|
||||
const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, {
|
||||
page: 1, size: 1,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || [];
|
||||
if (masters.length > 0) {
|
||||
await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, {
|
||||
data: masters.map((m: any) => ({ id: m.id })),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
toast.success("삭제되었습니다.");
|
||||
@@ -918,7 +933,7 @@ export default function SalesOrderPage() {
|
||||
{/* 데이터 테이블 (플랫 리스트) */}
|
||||
<div className="flex-1 overflow-hidden rounded-lg border border-border bg-card">
|
||||
<div className="h-full overflow-auto">
|
||||
<Table style={{ minWidth: "1500px" }}>
|
||||
<Table noWrapper style={{ minWidth: "1500px" }}>
|
||||
<colgroup>
|
||||
<col style={{ width: "40px" }} />
|
||||
<col style={{ width: "140px" }} />
|
||||
@@ -1107,6 +1122,7 @@ export default function SalesOrderPage() {
|
||||
<Select value={masterForm.input_mode || ""} onValueChange={(v) => {
|
||||
setMasterForm((p) => {
|
||||
const next = { ...p, input_mode: v };
|
||||
// 입력방식 변경 시 거래처 관련 값 초기화
|
||||
delete next.partner_id;
|
||||
delete next.delivery_partner_id;
|
||||
delete next.delivery_address;
|
||||
|
||||
@@ -715,7 +715,7 @@ export default function CustomerManagementPage() {
|
||||
const handleCustomerSave = async () => {
|
||||
if (!customerForm.customer_name) { toast.error("거래처명은 필수입니다."); return; }
|
||||
if (!customerForm.status) { toast.error("상태는 필수입니다."); return; }
|
||||
const errors = validateForm(customerForm, ["contact_phone", "email", "business_number"]);
|
||||
const errors = validateForm(customerForm, ["business_number"]);
|
||||
setFormErrors(errors);
|
||||
if (Object.keys(errors).length > 0) {
|
||||
toast.error("입력 형식을 확인해주세요.");
|
||||
@@ -1877,35 +1877,6 @@ export default function CustomerManagementPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">거래처담당자</Label>
|
||||
<Input
|
||||
value={customerForm.contact_person || ""}
|
||||
onChange={(e) => setCustomerForm((p) => ({ ...p, contact_person: e.target.value }))}
|
||||
placeholder="거래처담당자"
|
||||
className="h-9"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">전화번호</Label>
|
||||
<Input
|
||||
value={customerForm.contact_phone || ""}
|
||||
onChange={(e) => handleFormChange("contact_phone", e.target.value)}
|
||||
placeholder="010-0000-0000"
|
||||
className={cn("h-9", formErrors.contact_phone && "border-destructive")}
|
||||
/>
|
||||
{formErrors.contact_phone && <p className="text-xs text-destructive">{formErrors.contact_phone}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">이메일</Label>
|
||||
<Input
|
||||
value={customerForm.email || ""}
|
||||
onChange={(e) => handleFormChange("email", e.target.value)}
|
||||
placeholder="example@email.com"
|
||||
className={cn("h-9", formErrors.email && "border-destructive")}
|
||||
/>
|
||||
{formErrors.email && <p className="text-xs text-destructive">{formErrors.email}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">사업자번호</Label>
|
||||
<Input
|
||||
|
||||
@@ -517,29 +517,44 @@ export default function SalesOrderPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// 삭제 (마스터 단위)
|
||||
// 삭제 (선택한 디테일 삭제 → 디테일 0건인 마스터 자동 삭제)
|
||||
const handleDelete = async () => {
|
||||
if (checkedIds.length === 0) { toast.error("삭제할 수주를 선택해주세요."); return; }
|
||||
const selectedItems = orders.filter((o) => checkedIds.includes(o.id));
|
||||
const orderNos = [...new Set(selectedItems.map((o) => o.order_no))];
|
||||
const ok = await confirm(`${orderNos.length}건의 수주를 삭제하시겠습니까?`, {
|
||||
if (checkedIds.length === 0) { toast.error("삭제할 항목을 선택해주세요."); return; }
|
||||
const ok = await confirm(`${checkedIds.length}건의 수주 항목을 삭제하시겠습니까?`, {
|
||||
description: "삭제된 데이터는 복구할 수 없습니다.",
|
||||
variant: "destructive",
|
||||
confirmText: "삭제",
|
||||
});
|
||||
if (!ok) return;
|
||||
try {
|
||||
// 1. 선택한 디테일 삭제
|
||||
await apiClient.delete(`/table-management/tables/${DETAIL_TABLE}/delete`, {
|
||||
data: checkedIds.map((id) => ({ id })),
|
||||
});
|
||||
|
||||
// 2. 영향받는 수주번호의 잔여 디테일 확인 → 0건이면 마스터도 삭제
|
||||
const selectedItems = orders.filter((o) => checkedIds.includes(o.id));
|
||||
const orderNos = [...new Set(selectedItems.map((o) => o.order_no))];
|
||||
for (const orderNo of orderNos) {
|
||||
const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, {
|
||||
const remainRes = await apiClient.post(`/table-management/tables/${DETAIL_TABLE}/data`, {
|
||||
page: 1, size: 1,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || [];
|
||||
if (masters.length > 0) {
|
||||
await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, {
|
||||
data: masters.map((m: any) => ({ id: m.id })),
|
||||
const remaining = remainRes.data?.data?.data || remainRes.data?.data?.rows || [];
|
||||
if (remaining.length === 0) {
|
||||
// 디테일 0건 → 마스터 삭제
|
||||
const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, {
|
||||
page: 1, size: 1,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || [];
|
||||
if (masters.length > 0) {
|
||||
await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, {
|
||||
data: masters.map((m: any) => ({ id: m.id })),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
toast.success("삭제되었습니다.");
|
||||
@@ -918,7 +933,7 @@ export default function SalesOrderPage() {
|
||||
{/* 데이터 테이블 (플랫 리스트) */}
|
||||
<div className="flex-1 overflow-hidden rounded-lg border border-border bg-card">
|
||||
<div className="h-full overflow-auto">
|
||||
<Table style={{ minWidth: "1500px" }}>
|
||||
<Table noWrapper style={{ minWidth: "1500px" }}>
|
||||
<colgroup>
|
||||
<col style={{ width: "40px" }} />
|
||||
<col style={{ width: "140px" }} />
|
||||
@@ -1107,6 +1122,7 @@ export default function SalesOrderPage() {
|
||||
<Select value={masterForm.input_mode || ""} onValueChange={(v) => {
|
||||
setMasterForm((p) => {
|
||||
const next = { ...p, input_mode: v };
|
||||
// 입력방식 변경 시 거래처 관련 값 초기화
|
||||
delete next.partner_id;
|
||||
delete next.delivery_partner_id;
|
||||
delete next.delivery_address;
|
||||
|
||||
@@ -715,7 +715,7 @@ export default function CustomerManagementPage() {
|
||||
const handleCustomerSave = async () => {
|
||||
if (!customerForm.customer_name) { toast.error("거래처명은 필수입니다."); return; }
|
||||
if (!customerForm.status) { toast.error("상태는 필수입니다."); return; }
|
||||
const errors = validateForm(customerForm, ["contact_phone", "email", "business_number"]);
|
||||
const errors = validateForm(customerForm, ["business_number"]);
|
||||
setFormErrors(errors);
|
||||
if (Object.keys(errors).length > 0) {
|
||||
toast.error("입력 형식을 확인해주세요.");
|
||||
@@ -1877,35 +1877,6 @@ export default function CustomerManagementPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">거래처담당자</Label>
|
||||
<Input
|
||||
value={customerForm.contact_person || ""}
|
||||
onChange={(e) => setCustomerForm((p) => ({ ...p, contact_person: e.target.value }))}
|
||||
placeholder="거래처담당자"
|
||||
className="h-9"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">전화번호</Label>
|
||||
<Input
|
||||
value={customerForm.contact_phone || ""}
|
||||
onChange={(e) => handleFormChange("contact_phone", e.target.value)}
|
||||
placeholder="010-0000-0000"
|
||||
className={cn("h-9", formErrors.contact_phone && "border-destructive")}
|
||||
/>
|
||||
{formErrors.contact_phone && <p className="text-xs text-destructive">{formErrors.contact_phone}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">이메일</Label>
|
||||
<Input
|
||||
value={customerForm.email || ""}
|
||||
onChange={(e) => handleFormChange("email", e.target.value)}
|
||||
placeholder="example@email.com"
|
||||
className={cn("h-9", formErrors.email && "border-destructive")}
|
||||
/>
|
||||
{formErrors.email && <p className="text-xs text-destructive">{formErrors.email}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">사업자번호</Label>
|
||||
<Input
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -715,7 +715,7 @@ export default function CustomerManagementPage() {
|
||||
const handleCustomerSave = async () => {
|
||||
if (!customerForm.customer_name) { toast.error("거래처명은 필수입니다."); return; }
|
||||
if (!customerForm.status) { toast.error("상태는 필수입니다."); return; }
|
||||
const errors = validateForm(customerForm, ["contact_phone", "email", "business_number"]);
|
||||
const errors = validateForm(customerForm, ["business_number"]);
|
||||
setFormErrors(errors);
|
||||
if (Object.keys(errors).length > 0) {
|
||||
toast.error("입력 형식을 확인해주세요.");
|
||||
@@ -1877,35 +1877,6 @@ export default function CustomerManagementPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">거래처담당자</Label>
|
||||
<Input
|
||||
value={customerForm.contact_person || ""}
|
||||
onChange={(e) => setCustomerForm((p) => ({ ...p, contact_person: e.target.value }))}
|
||||
placeholder="거래처담당자"
|
||||
className="h-9"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">전화번호</Label>
|
||||
<Input
|
||||
value={customerForm.contact_phone || ""}
|
||||
onChange={(e) => handleFormChange("contact_phone", e.target.value)}
|
||||
placeholder="010-0000-0000"
|
||||
className={cn("h-9", formErrors.contact_phone && "border-destructive")}
|
||||
/>
|
||||
{formErrors.contact_phone && <p className="text-xs text-destructive">{formErrors.contact_phone}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">이메일</Label>
|
||||
<Input
|
||||
value={customerForm.email || ""}
|
||||
onChange={(e) => handleFormChange("email", e.target.value)}
|
||||
placeholder="example@email.com"
|
||||
className={cn("h-9", formErrors.email && "border-destructive")}
|
||||
/>
|
||||
{formErrors.email && <p className="text-xs text-destructive">{formErrors.email}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">사업자번호</Label>
|
||||
<Input
|
||||
|
||||
@@ -517,29 +517,44 @@ export default function SalesOrderPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// 삭제 (마스터 단위)
|
||||
// 삭제 (선택한 디테일 삭제 → 디테일 0건인 마스터 자동 삭제)
|
||||
const handleDelete = async () => {
|
||||
if (checkedIds.length === 0) { toast.error("삭제할 수주를 선택해주세요."); return; }
|
||||
const selectedItems = orders.filter((o) => checkedIds.includes(o.id));
|
||||
const orderNos = [...new Set(selectedItems.map((o) => o.order_no))];
|
||||
const ok = await confirm(`${orderNos.length}건의 수주를 삭제하시겠습니까?`, {
|
||||
if (checkedIds.length === 0) { toast.error("삭제할 항목을 선택해주세요."); return; }
|
||||
const ok = await confirm(`${checkedIds.length}건의 수주 항목을 삭제하시겠습니까?`, {
|
||||
description: "삭제된 데이터는 복구할 수 없습니다.",
|
||||
variant: "destructive",
|
||||
confirmText: "삭제",
|
||||
});
|
||||
if (!ok) return;
|
||||
try {
|
||||
// 1. 선택한 디테일 삭제
|
||||
await apiClient.delete(`/table-management/tables/${DETAIL_TABLE}/delete`, {
|
||||
data: checkedIds.map((id) => ({ id })),
|
||||
});
|
||||
|
||||
// 2. 영향받는 수주번호의 잔여 디테일 확인 → 0건이면 마스터도 삭제
|
||||
const selectedItems = orders.filter((o) => checkedIds.includes(o.id));
|
||||
const orderNos = [...new Set(selectedItems.map((o) => o.order_no))];
|
||||
for (const orderNo of orderNos) {
|
||||
const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, {
|
||||
const remainRes = await apiClient.post(`/table-management/tables/${DETAIL_TABLE}/data`, {
|
||||
page: 1, size: 1,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || [];
|
||||
if (masters.length > 0) {
|
||||
await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, {
|
||||
data: masters.map((m: any) => ({ id: m.id })),
|
||||
const remaining = remainRes.data?.data?.data || remainRes.data?.data?.rows || [];
|
||||
if (remaining.length === 0) {
|
||||
// 디테일 0건 → 마스터 삭제
|
||||
const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, {
|
||||
page: 1, size: 1,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || [];
|
||||
if (masters.length > 0) {
|
||||
await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, {
|
||||
data: masters.map((m: any) => ({ id: m.id })),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
toast.success("삭제되었습니다.");
|
||||
|
||||
@@ -715,7 +715,7 @@ export default function CustomerManagementPage() {
|
||||
const handleCustomerSave = async () => {
|
||||
if (!customerForm.customer_name) { toast.error("거래처명은 필수입니다."); return; }
|
||||
if (!customerForm.status) { toast.error("상태는 필수입니다."); return; }
|
||||
const errors = validateForm(customerForm, ["contact_phone", "email", "business_number"]);
|
||||
const errors = validateForm(customerForm, ["business_number"]);
|
||||
setFormErrors(errors);
|
||||
if (Object.keys(errors).length > 0) {
|
||||
toast.error("입력 형식을 확인해주세요.");
|
||||
@@ -1877,35 +1877,6 @@ export default function CustomerManagementPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">거래처담당자</Label>
|
||||
<Input
|
||||
value={customerForm.contact_person || ""}
|
||||
onChange={(e) => setCustomerForm((p) => ({ ...p, contact_person: e.target.value }))}
|
||||
placeholder="거래처담당자"
|
||||
className="h-9"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">전화번호</Label>
|
||||
<Input
|
||||
value={customerForm.contact_phone || ""}
|
||||
onChange={(e) => handleFormChange("contact_phone", e.target.value)}
|
||||
placeholder="010-0000-0000"
|
||||
className={cn("h-9", formErrors.contact_phone && "border-destructive")}
|
||||
/>
|
||||
{formErrors.contact_phone && <p className="text-xs text-destructive">{formErrors.contact_phone}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">이메일</Label>
|
||||
<Input
|
||||
value={customerForm.email || ""}
|
||||
onChange={(e) => handleFormChange("email", e.target.value)}
|
||||
placeholder="example@email.com"
|
||||
className={cn("h-9", formErrors.email && "border-destructive")}
|
||||
/>
|
||||
{formErrors.email && <p className="text-xs text-destructive">{formErrors.email}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">사업자번호</Label>
|
||||
<Input
|
||||
|
||||
@@ -517,29 +517,44 @@ export default function SalesOrderPage() {
|
||||
}
|
||||
};
|
||||
|
||||
// 삭제 (마스터 단위)
|
||||
// 삭제 (선택한 디테일 삭제 → 디테일 0건인 마스터 자동 삭제)
|
||||
const handleDelete = async () => {
|
||||
if (checkedIds.length === 0) { toast.error("삭제할 수주를 선택해주세요."); return; }
|
||||
const selectedItems = orders.filter((o) => checkedIds.includes(o.id));
|
||||
const orderNos = [...new Set(selectedItems.map((o) => o.order_no))];
|
||||
const ok = await confirm(`${orderNos.length}건의 수주를 삭제하시겠습니까?`, {
|
||||
if (checkedIds.length === 0) { toast.error("삭제할 항목을 선택해주세요."); return; }
|
||||
const ok = await confirm(`${checkedIds.length}건의 수주 항목을 삭제하시겠습니까?`, {
|
||||
description: "삭제된 데이터는 복구할 수 없습니다.",
|
||||
variant: "destructive",
|
||||
confirmText: "삭제",
|
||||
});
|
||||
if (!ok) return;
|
||||
try {
|
||||
// 1. 선택한 디테일 삭제
|
||||
await apiClient.delete(`/table-management/tables/${DETAIL_TABLE}/delete`, {
|
||||
data: checkedIds.map((id) => ({ id })),
|
||||
});
|
||||
|
||||
// 2. 영향받는 수주번호의 잔여 디테일 확인 → 0건이면 마스터도 삭제
|
||||
const selectedItems = orders.filter((o) => checkedIds.includes(o.id));
|
||||
const orderNos = [...new Set(selectedItems.map((o) => o.order_no))];
|
||||
for (const orderNo of orderNos) {
|
||||
const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, {
|
||||
const remainRes = await apiClient.post(`/table-management/tables/${DETAIL_TABLE}/data`, {
|
||||
page: 1, size: 1,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || [];
|
||||
if (masters.length > 0) {
|
||||
await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, {
|
||||
data: masters.map((m: any) => ({ id: m.id })),
|
||||
const remaining = remainRes.data?.data?.data || remainRes.data?.data?.rows || [];
|
||||
if (remaining.length === 0) {
|
||||
// 디테일 0건 → 마스터 삭제
|
||||
const masterRes = await apiClient.post(`/table-management/tables/${MASTER_TABLE}/data`, {
|
||||
page: 1, size: 1,
|
||||
dataFilter: { enabled: true, filters: [{ columnName: "order_no", operator: "equals", value: orderNo }] },
|
||||
autoFilter: true,
|
||||
});
|
||||
const masters = masterRes.data?.data?.data || masterRes.data?.data?.rows || [];
|
||||
if (masters.length > 0) {
|
||||
await apiClient.delete(`/table-management/tables/${MASTER_TABLE}/delete`, {
|
||||
data: masters.map((m: any) => ({ id: m.id })),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
toast.success("삭제되었습니다.");
|
||||
@@ -918,7 +933,7 @@ export default function SalesOrderPage() {
|
||||
{/* 데이터 테이블 (플랫 리스트) */}
|
||||
<div className="flex-1 overflow-hidden rounded-lg border border-border bg-card">
|
||||
<div className="h-full overflow-auto">
|
||||
<Table style={{ minWidth: "1500px" }}>
|
||||
<Table noWrapper style={{ minWidth: "1500px" }}>
|
||||
<colgroup>
|
||||
<col style={{ width: "40px" }} />
|
||||
<col style={{ width: "140px" }} />
|
||||
@@ -1107,6 +1122,7 @@ export default function SalesOrderPage() {
|
||||
<Select value={masterForm.input_mode || ""} onValueChange={(v) => {
|
||||
setMasterForm((p) => {
|
||||
const next = { ...p, input_mode: v };
|
||||
// 입력방식 변경 시 거래처 관련 값 초기화
|
||||
delete next.partner_id;
|
||||
delete next.delivery_partner_id;
|
||||
delete next.delivery_address;
|
||||
|
||||
@@ -715,7 +715,7 @@ export default function CustomerManagementPage() {
|
||||
const handleCustomerSave = async () => {
|
||||
if (!customerForm.customer_name) { toast.error("거래처명은 필수입니다."); return; }
|
||||
if (!customerForm.status) { toast.error("상태는 필수입니다."); return; }
|
||||
const errors = validateForm(customerForm, ["contact_phone", "email", "business_number"]);
|
||||
const errors = validateForm(customerForm, ["business_number"]);
|
||||
setFormErrors(errors);
|
||||
if (Object.keys(errors).length > 0) {
|
||||
toast.error("입력 형식을 확인해주세요.");
|
||||
@@ -1877,35 +1877,6 @@ export default function CustomerManagementPage() {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">거래처담당자</Label>
|
||||
<Input
|
||||
value={customerForm.contact_person || ""}
|
||||
onChange={(e) => setCustomerForm((p) => ({ ...p, contact_person: e.target.value }))}
|
||||
placeholder="거래처담당자"
|
||||
className="h-9"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">전화번호</Label>
|
||||
<Input
|
||||
value={customerForm.contact_phone || ""}
|
||||
onChange={(e) => handleFormChange("contact_phone", e.target.value)}
|
||||
placeholder="010-0000-0000"
|
||||
className={cn("h-9", formErrors.contact_phone && "border-destructive")}
|
||||
/>
|
||||
{formErrors.contact_phone && <p className="text-xs text-destructive">{formErrors.contact_phone}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">이메일</Label>
|
||||
<Input
|
||||
value={customerForm.email || ""}
|
||||
onChange={(e) => handleFormChange("email", e.target.value)}
|
||||
placeholder="example@email.com"
|
||||
className={cn("h-9", formErrors.email && "border-destructive")}
|
||||
/>
|
||||
{formErrors.email && <p className="text-xs text-destructive">{formErrors.email}</p>}
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
<Label className="text-sm">사업자번호</Label>
|
||||
<Input
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user