diff options
Diffstat (limited to 'lib/tech-vendors')
| -rw-r--r-- | lib/tech-vendors/contacts-table/add-contact-dialog.tsx | 9 | ||||
| -rw-r--r-- | lib/tech-vendors/contacts-table/delete-contact-dialog.tsx | 81 | ||||
| -rw-r--r-- | lib/tech-vendors/contacts-table/update-contact-sheet.tsx | 13 | ||||
| -rw-r--r-- | lib/tech-vendors/possible-items/add-item-dialog.tsx | 4 | ||||
| -rw-r--r-- | lib/tech-vendors/possible-items/possible-items-toolbar-actions.tsx | 2 | ||||
| -rw-r--r-- | lib/tech-vendors/service.ts | 131 | ||||
| -rw-r--r-- | lib/tech-vendors/table/add-vendor-dialog.tsx | 13 | ||||
| -rw-r--r-- | lib/tech-vendors/table/delete-tech-vendors-dialog.tsx | 80 | ||||
| -rw-r--r-- | lib/tech-vendors/table/import-button.tsx | 19 | ||||
| -rw-r--r-- | lib/tech-vendors/table/tech-vendors-table-columns.tsx | 12 | ||||
| -rw-r--r-- | lib/tech-vendors/table/tech-vendors-table.tsx | 7 | ||||
| -rw-r--r-- | lib/tech-vendors/table/update-vendor-sheet.tsx | 13 | ||||
| -rw-r--r-- | lib/tech-vendors/utils.ts | 84 |
13 files changed, 403 insertions, 65 deletions
diff --git a/lib/tech-vendors/contacts-table/add-contact-dialog.tsx b/lib/tech-vendors/contacts-table/add-contact-dialog.tsx index 90ba4e04..447c44d7 100644 --- a/lib/tech-vendors/contacts-table/add-contact-dialog.tsx +++ b/lib/tech-vendors/contacts-table/add-contact-dialog.tsx @@ -21,6 +21,7 @@ import { type CreateTechVendorContactSchema,
} from "@/lib/tech-vendors/validations"
import { createTechVendorContact } from "@/lib/tech-vendors/service"
+import { normalizeEmail } from "@/lib/tech-vendors/utils"
interface AddContactDialogProps {
vendorId: number
@@ -46,8 +47,14 @@ export function AddContactDialog({ vendorId }: AddContactDialogProps) { })
async function onSubmit(data: CreateTechVendorContactSchema) {
+ // 이메일을 소문자로 변환
+ const normalizedData = {
+ ...data,
+ contactEmail: normalizeEmail(data.contactEmail),
+ }
+
// 혹은 여기서 data.vendorId = vendorId; 해줘도 됨
- const result = await createTechVendorContact(data)
+ const result = await createTechVendorContact(normalizedData)
if (result.error) {
alert(`에러: ${result.error}`)
return
diff --git a/lib/tech-vendors/contacts-table/delete-contact-dialog.tsx b/lib/tech-vendors/contacts-table/delete-contact-dialog.tsx new file mode 100644 index 00000000..6c8b14d7 --- /dev/null +++ b/lib/tech-vendors/contacts-table/delete-contact-dialog.tsx @@ -0,0 +1,81 @@ +"use client" + +import * as React from "react" +import { toast } from "sonner" +import { Trash2 } from "lucide-react" + +import { Button } from "@/components/ui/button" +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog" + +import { deleteTechVendorContact } from "../service" +import type { TechVendorContact } from "@/db/schema/techVendors" + +interface DeleteContactDialogProps { + contact: TechVendorContact + vendorId: number + onSuccess?: () => void +} + +export function DeleteContactDialog({ contact, vendorId, onSuccess }: DeleteContactDialogProps) { + const [isDeleting, setIsDeleting] = React.useState(false) + + const handleDelete = async () => { + setIsDeleting(true) + try { + const result = await deleteTechVendorContact(contact.id, vendorId) + + if (result.success) { + toast.success("담당자가 성공적으로 삭제되었습니다.") + onSuccess?.() + } else { + toast.error(result.error || "담당자 삭제 중 오류가 발생했습니다.") + } + } catch (error) { + console.error("담당자 삭제 오류:", error) + toast.error("담당자 삭제 중 오류가 발생했습니다.") + } finally { + setIsDeleting(false) + } + } + + return ( + <AlertDialog> + <AlertDialogTrigger asChild> + <Button variant="ghost" size="sm" className="text-red-600 hover:text-red-700 hover:bg-red-50"> + <Trash2 className="size-4 mr-2" /> + 삭제 + </Button> + </AlertDialogTrigger> + <AlertDialogContent> + <AlertDialogHeader> + <AlertDialogTitle>담당자 삭제</AlertDialogTitle> + <AlertDialogDescription> + <strong>{contact.contactName}</strong> 담당자를 정말 삭제하시겠습니까? + <br /> + 이 작업은 되돌릴 수 없으며, 관련된 모든 데이터(아이템 매핑 등)가 함께 삭제됩니다. + </AlertDialogDescription> + </AlertDialogHeader> + <AlertDialogFooter> + <AlertDialogCancel>취소</AlertDialogCancel> + <AlertDialogAction + onClick={handleDelete} + disabled={isDeleting} + className="bg-red-600 hover:bg-red-700" + > + {isDeleting ? "삭제 중..." : "삭제"} + </AlertDialogAction> + </AlertDialogFooter> + </AlertDialogContent> + </AlertDialog> + ) +} diff --git a/lib/tech-vendors/contacts-table/update-contact-sheet.tsx b/lib/tech-vendors/contacts-table/update-contact-sheet.tsx index 4713790c..877e8b4f 100644 --- a/lib/tech-vendors/contacts-table/update-contact-sheet.tsx +++ b/lib/tech-vendors/contacts-table/update-contact-sheet.tsx @@ -28,6 +28,7 @@ import { Loader2 } from "lucide-react" import type { TechVendorContact } from "@/db/schema/techVendors"
import { updateTechVendorContactSchema, type UpdateTechVendorContactSchema } from "../validations"
import { updateTechVendorContact } from "../service"
+import { normalizeEmail } from "../utils"
interface UpdateContactSheetProps
extends React.ComponentPropsWithoutRef<typeof Sheet> {
@@ -67,13 +68,19 @@ export function UpdateContactSheet({ contact, vendorId, ...props }: UpdateContac async function onSubmit(data: UpdateTechVendorContactSchema) {
if (!contact) return
-
+
startTransition(async () => {
try {
- const { error } = await updateTechVendorContact({
+ // 이메일을 소문자로 변환
+ const normalizedData = {
+ ...data,
+ contactEmail: normalizeEmail(data.contactEmail) || undefined,
+ }
+
+ const { error } = await updateTechVendorContact({
id: contact.id,
vendorId: vendorId,
- ...data
+ ...normalizedData,
})
if (error) throw new Error(error)
diff --git a/lib/tech-vendors/possible-items/add-item-dialog.tsx b/lib/tech-vendors/possible-items/add-item-dialog.tsx index 0e6edd19..ceb34276 100644 --- a/lib/tech-vendors/possible-items/add-item-dialog.tsx +++ b/lib/tech-vendors/possible-items/add-item-dialog.tsx @@ -166,9 +166,9 @@ export function AddItemDialog({ open, onOpenChange, vendorId }: AddItemDialogPro <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-4xl max-h-[90vh] flex flex-col">
<DialogHeader>
- <DialogTitle>아이템 추가</DialogTitle>
+ <DialogTitle>아이템 연결</DialogTitle>
<DialogDescription>
- 추가할 아이템을 선택하세요. 복수 선택이 가능합니다.
+ 연결할 아이템을 선택하세요. 복수 선택이 가능합니다.
</DialogDescription>
</DialogHeader>
diff --git a/lib/tech-vendors/possible-items/possible-items-toolbar-actions.tsx b/lib/tech-vendors/possible-items/possible-items-toolbar-actions.tsx index bed65727..49a673ff 100644 --- a/lib/tech-vendors/possible-items/possible-items-toolbar-actions.tsx +++ b/lib/tech-vendors/possible-items/possible-items-toolbar-actions.tsx @@ -362,7 +362,7 @@ export function PossibleItemsTableToolbarActions({ onClick={onAdd} > <Plus className="mr-2 h-4 w-4" /> - 아이템 추가 + 아이템 연결 </Button> {selectedRows.length > 0 && ( diff --git a/lib/tech-vendors/service.ts b/lib/tech-vendors/service.ts index 72f8632d..940e59ce 100644 --- a/lib/tech-vendors/service.ts +++ b/lib/tech-vendors/service.ts @@ -40,6 +40,7 @@ import { sql } from "drizzle-orm"; import { decryptWithServerAction } from "@/components/drm/drmUtils"; import { deleteFile, saveDRMFile } from "../file-stroage"; import { techSalesContactPossibleItems } from "@/db/schema"; +import { normalizeEmailFields } from "./utils"; /* ----------------------------------------------------- 1) 조회 관련 @@ -563,24 +564,6 @@ export async function updateTechVendorContact(input: UpdateTechVendorContactSche } } -export async function deleteTechVendorContact(contactId: number, vendorId: number) { - unstable_noStore(); - try { - const [deletedContact] = await db - .delete(techVendorContacts) - .where(eq(techVendorContacts.id, contactId)) - .returning(); - - // 캐시 무효화 - revalidateTag(`tech-vendor-contacts-${contactId}`); - revalidateTag(`tech-vendor-contacts-${vendorId}`); - - return { data: deletedContact, error: null }; - } catch (err) { - return { data: null, error: getErrorMessage(err) }; - } -} - /* ----------------------------------------------------- 5) 아이템 관리 ----------------------------------------------------- */ @@ -4116,4 +4099,116 @@ export async function generateContactPossibleItemsErrorExcel(errors: Array<{ return new Blob([buffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", }) +} + +/** + * 기술영업 벤더 담당자 삭제 (cascade 방식) + * 1. tech_sales_contact_possible_items (해당 담당자의 아이템 매핑) + * 2. tech_vendor_contacts (해당 담당자) + */ +export async function deleteTechVendorContact(contactId: number, vendorId: number) { + unstable_noStore() + + try { + const result = await db.transaction(async (tx) => { + // 1. 해당 담당자의 아이템 매핑 삭제 (tech_sales_contact_possible_items) + await tx + .delete(techSalesContactPossibleItems) + .where(eq(techSalesContactPossibleItems.contactId, contactId)) + + // 2. 담당자 삭제 (tech_vendor_contacts) + const deletedContact = await tx + .delete(techVendorContacts) + .where(eq(techVendorContacts.id, contactId)) + .returning() + + return deletedContact + }) + + // 캐시 무효화 + revalidateTag(`tech-vendor-contacts-${vendorId}`) + revalidateTag("tech-vendors") + + return { + success: true, + data: result, + error: null + } + } catch (err) { + console.error("담당자 삭제 오류:", err) + return { + success: false, + data: null, + error: getErrorMessage(err) + } + } +} + +/** + * 기술영업 벤더 삭제 (cascade 방식) + * 1. tech_sales_contact_possible_items (담당자별 아이템) + * 2. tech_vendor_possible_items (벤더 아이템) + * 3. tech_vendor_contacts (벤더 담당자) + * 4. tech_vendor_attachments (벤더 첨부파일) + * 5. tech_vendors (벤더) + */ +// 임시 삭제 함수 +export async function deleteTechVendor(vendorId: number) { + unstable_noStore() + + try { + const result = await db.transaction(async (tx) => { + // 1. 해당 벤더의 담당자별 아이템 삭제 (tech_sales_contact_possible_items) + await tx + .delete(techSalesContactPossibleItems) + .where( + inArray( + techSalesContactPossibleItems.contactId, + tx + .select({ contactId: techVendorContacts.id }) + .from(techVendorContacts) + .where(eq(techVendorContacts.vendorId, vendorId)) + ) + ) + + // 2. 해당 벤더의 아이템 삭제 (tech_vendor_possible_items) + await tx + .delete(techVendorPossibleItems) + .where(eq(techVendorPossibleItems.vendorId, vendorId)) + + // 3. 해당 벤더의 담당자 삭제 (tech_vendor_contacts) + await tx + .delete(techVendorContacts) + .where(eq(techVendorContacts.vendorId, vendorId)) + + // 4. 해당 벤더의 첨부파일 삭제 (tech_vendor_attachments) + await tx + .delete(techVendorAttachments) + .where(eq(techVendorAttachments.vendorId, vendorId)) + + // 5. 벤더 삭제 (tech_vendors) + const deletedVendor = await tx + .delete(techVendors) + .where(eq(techVendors.id, vendorId)) + .returning() + + return deletedVendor + }) + + // 캐시 무효화 + revalidateTag("tech-vendors") + + return { + success: true, + data: result, + error: null + } + } catch (err) { + console.error("벤더 삭제 오류:", err) + return { + success: false, + data: null, + error: getErrorMessage(err) + } + } }
\ No newline at end of file diff --git a/lib/tech-vendors/table/add-vendor-dialog.tsx b/lib/tech-vendors/table/add-vendor-dialog.tsx index e89f5d6b..f696b30a 100644 --- a/lib/tech-vendors/table/add-vendor-dialog.tsx +++ b/lib/tech-vendors/table/add-vendor-dialog.tsx @@ -30,6 +30,7 @@ import { Textarea } from "@/components/ui/textarea" import { Plus, Loader2 } from "lucide-react"
import { addTechVendor } from "../service"
+import { normalizeEmailFields } from "../utils"
// 폼 스키마 정의
const addVendorSchema = z.object({
@@ -92,6 +93,13 @@ export function AddVendorDialog({ onSuccess }: AddVendorDialogProps) { const onSubmit = async (data: AddVendorFormData) => {
setIsLoading(true)
try {
+ // 이메일 필드들을 소문자로 변환
+ const normalizedEmails = normalizeEmailFields({
+ email: data.email,
+ agentEmail: data.agentEmail,
+ representativeEmail: data.representativeEmail,
+ })
+
const result = await addTechVendor({
...data,
vendorCode: data.vendorCode || null,
@@ -100,13 +108,14 @@ export function AddVendorDialog({ onSuccess }: AddVendorDialogProps) { countryFab: data.countryFab || null,
agentName: data.agentName || null,
agentPhone: data.agentPhone || null,
- agentEmail: data.agentEmail || null,
+ agentEmail: normalizedEmails.agentEmail,
address: data.address || null,
phone: data.phone || null,
website: data.website || null,
+ email: normalizedEmails.email,
techVendorType: data.techVendorType.join(','),
representativeName: data.representativeName || null,
- representativeEmail: data.representativeEmail || null,
+ representativeEmail: normalizedEmails.representativeEmail,
representativePhone: data.representativePhone || null,
representativeBirth: data.representativeBirth || null,
taxId: data.taxId || "",
diff --git a/lib/tech-vendors/table/delete-tech-vendors-dialog.tsx b/lib/tech-vendors/table/delete-tech-vendors-dialog.tsx new file mode 100644 index 00000000..4fd3f32a --- /dev/null +++ b/lib/tech-vendors/table/delete-tech-vendors-dialog.tsx @@ -0,0 +1,80 @@ +"use client" + +import * as React from "react" +import { toast } from "sonner" +import { Trash2 } from "lucide-react" + +import { Button } from "@/components/ui/button" +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog" + +import { deleteTechVendor } from "../service" +import type { TechVendor } from "@/db/schema/techVendors" + +interface DeleteTechVendorDialogProps { + vendor: TechVendor + onSuccess?: () => void +} +// 임시 삭제 버튼 +export function DeleteTechVendorDialog({ vendor, onSuccess }: DeleteTechVendorDialogProps) { + const [isDeleting, setIsDeleting] = React.useState(false) + + const handleDelete = async () => { + setIsDeleting(true) + try { + const result = await deleteTechVendor(vendor.id) + + if (result.success) { + toast.success("벤더가 성공적으로 삭제되었습니다.") + onSuccess?.() + } else { + toast.error(result.error || "벤더 삭제 중 오류가 발생했습니다.") + } + } catch (error) { + console.error("벤더 삭제 오류:", error) + toast.error("벤더 삭제 중 오류가 발생했습니다.") + } finally { + setIsDeleting(false) + } + } + + return ( + <AlertDialog> + <AlertDialogTrigger asChild> + <Button variant="ghost" size="sm" className="text-red-600 hover:text-red-700 hover:bg-red-50"> + <Trash2 className="size-4 mr-2" /> + 삭제 + </Button> + </AlertDialogTrigger> + <AlertDialogContent> + <AlertDialogHeader> + <AlertDialogTitle>벤더 삭제</AlertDialogTitle> + <AlertDialogDescription> + <strong>{vendor.vendorName}</strong> 벤더를 정말 삭제하시겠습니까? + <br /> + 이 작업은 되돌릴 수 없으며, 관련된 모든 데이터(담당자, 아이템 등)가 함께 삭제됩니다. + </AlertDialogDescription> + </AlertDialogHeader> + <AlertDialogFooter> + <AlertDialogCancel>취소</AlertDialogCancel> + <AlertDialogAction + onClick={handleDelete} + disabled={isDeleting} + className="bg-red-600 hover:bg-red-700" + > + {isDeleting ? "삭제 중..." : "삭제"} + </AlertDialogAction> + </AlertDialogFooter> + </AlertDialogContent> + </AlertDialog> + ) +} diff --git a/lib/tech-vendors/table/import-button.tsx b/lib/tech-vendors/table/import-button.tsx index 85b16bc7..e0f95195 100644 --- a/lib/tech-vendors/table/import-button.tsx +++ b/lib/tech-vendors/table/import-button.tsx @@ -17,6 +17,7 @@ import { import { Progress } from "@/components/ui/progress"
import { importTechVendorsFromExcel } from "../service"
import { decryptWithServerAction } from "@/components/drm/drmUtils"
+import { normalizeEmail } from "../utils"
interface ImportTechVendorButtonProps {
onSuccess?: () => void;
@@ -192,13 +193,17 @@ export function ImportTechVendorButton({ onSuccess }: ImportTechVendorButtonProp // 벤더 데이터 처리
const vendors = dataRows.map(row => {
- const vendorEmail = row["이메일"] || row["email"] || "";
+ // 이메일들을 소문자로 변환
+ const vendorEmail = normalizeEmail(row["이메일"] || row["email"]) || null;
+ const contactEmail = normalizeEmail(row["담당자이메일"] || row["contactEmail"]);
+ const agentEmail = normalizeEmail(row["에이전트이메일"] || row["agentEmail"]);
+ const representativeEmail = normalizeEmail(row["대표자이메일"] || row["representativeEmail"]);
+
const contactName = row["담당자명"] || row["contactName"] || "";
- const contactEmail = row["담당자이메일"] || row["contactEmail"] || "";
-
+
// 담당자 정보 처리: 담당자가 없으면 벤더 이메일을 기본 담당자로 사용
const contacts = [];
-
+
if (contactName && contactEmail) {
// 명시적인 담당자가 있는 경우
contacts.push({
@@ -221,7 +226,7 @@ export function ImportTechVendorButton({ onSuccess }: ImportTechVendorButtonProp isPrimary: true
});
}
-
+
return {
vendorName: row["업체명"] || row["vendorName"] || "",
vendorCode: row["업체코드"] || row["vendorCode"] || null,
@@ -232,13 +237,13 @@ export function ImportTechVendorButton({ onSuccess }: ImportTechVendorButtonProp countryFab: row["제조국"] || row["countryFab"] || null,
agentName: row["에이전트명"] || row["agentName"] || null,
agentPhone: row["에이전트연락처"] || row["agentPhone"] || null,
- agentEmail: row["에이전트이메일"] || row["agentEmail"] || null,
+ agentEmail: agentEmail,
address: row["주소"] || row["address"] || null,
phone: row["전화번호"] || row["phone"] || null,
website: row["웹사이트"] || row["website"] || null,
techVendorType: row["벤더타입"] || row["techVendorType"] || "",
representativeName: row["대표자명"] || row["representativeName"] || null,
- representativeEmail: row["대표자이메일"] || row["representativeEmail"] || null,
+ representativeEmail: representativeEmail,
representativePhone: row["대표자연락처"] || row["representativePhone"] || null,
representativeBirth: row["대표자생년월일"] || row["representativeBirth"] || null,
items: row["아이템"] || row["items"] || "",
diff --git a/lib/tech-vendors/table/tech-vendors-table-columns.tsx b/lib/tech-vendors/table/tech-vendors-table-columns.tsx index da17a975..c1bf6229 100644 --- a/lib/tech-vendors/table/tech-vendors-table-columns.tsx +++ b/lib/tech-vendors/table/tech-vendors-table-columns.tsx @@ -30,6 +30,7 @@ import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table- import { techVendorColumnsConfig } from "@/config/techVendorColumnsConfig"
import { Separator } from "@/components/ui/separator"
import { getVendorStatusIcon } from "../utils"
+import { DeleteTechVendorDialog } from "./delete-tech-vendors-dialog"
// 타입 정의 추가
type StatusType = (typeof techVendors.status.enumValues)[number];
@@ -47,6 +48,7 @@ type NextRouter = ReturnType<typeof useRouter>; interface GetColumnsProps {
setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<TechVendor> | null>>;
router: NextRouter;
+ onVendorDeleted?: () => void;
}
@@ -55,7 +57,7 @@ interface GetColumnsProps { /**
* tanstack table 컬럼 정의 (중첩 헤더 버전)
*/
-export function getColumns({ setRowAction, router }: GetColumnsProps): ColumnDef<TechVendor>[] {
+export function getColumns({ setRowAction, router, onVendorDeleted }: GetColumnsProps): ColumnDef<TechVendor>[] {
// ----------------------------------------------------------------
// 1) select 컬럼 (체크박스)
// ----------------------------------------------------------------
@@ -169,6 +171,14 @@ export function getColumns({ setRowAction, router }: GetColumnsProps): ColumnDef </DropdownMenuSubContent>
</DropdownMenuSub>
+ <Separator />
+ {/* 벤더 임시 삭제 버튼 */}
+ <div className="px-2 py-1">
+ <DeleteTechVendorDialog
+ vendor={row.original}
+ onSuccess={onVendorDeleted}
+ />
+ </div>
</DropdownMenuContent>
</DropdownMenu>
diff --git a/lib/tech-vendors/table/tech-vendors-table.tsx b/lib/tech-vendors/table/tech-vendors-table.tsx index 553ff109..e432a453 100644 --- a/lib/tech-vendors/table/tech-vendors-table.tsx +++ b/lib/tech-vendors/table/tech-vendors-table.tsx @@ -58,8 +58,13 @@ export function TechVendorsTable({ const router = useRouter()
// getColumns() 호출 시, router를 주입
+ // 임시 삭제 버튼 추가
const columns = React.useMemo(
- () => getColumns({ setRowAction, router }),
+ () => getColumns({
+ setRowAction,
+ router,
+ onVendorDeleted: () => router.refresh()
+ }),
[setRowAction, router]
)
diff --git a/lib/tech-vendors/table/update-vendor-sheet.tsx b/lib/tech-vendors/table/update-vendor-sheet.tsx index 8498df51..3cbb2ba3 100644 --- a/lib/tech-vendors/table/update-vendor-sheet.tsx +++ b/lib/tech-vendors/table/update-vendor-sheet.tsx @@ -45,6 +45,7 @@ import { useSession } from "next-auth/react" import { TechVendor, techVendors } from "@/db/schema/techVendors"
import { updateTechVendorSchema, type UpdateTechVendorSchema } from "../validations"
import { modifyTechVendor } from "../service"
+import { normalizeEmailFields } from "../utils"
interface UpdateVendorSheetProps
extends React.ComponentPropsWithRef<typeof Sheet> {
@@ -186,12 +187,22 @@ export function UpdateVendorSheet({ vendor, ...props }: UpdateVendorSheetProps) ? `상태 변경: ${getStatusConfig(oldStatus).label} → ${getStatusConfig(newStatus).label}`
: "" // Empty string instead of undefined
+ // 이메일 필드들을 소문자로 변환
+ const normalizedEmails = normalizeEmailFields({
+ email: data.email,
+ agentEmail: data.agentEmail,
+ representativeEmail: data.representativeEmail,
+ })
+
// 업체 정보 업데이트 - userId와 상태 변경 코멘트 추가
- const { error } = await modifyTechVendor({
+ const { error } = await modifyTechVendor({
id: String(vendor.id),
userId: Number(session.user.id), // Add user ID from session
comment: statusComment, // Add comment for status changes
...data, // 모든 데이터 전달 - 서비스 함수에서 필요한 필드만 처리
+ email: normalizedEmails.email || undefined,
+ agentEmail: normalizedEmails.agentEmail || undefined,
+ representativeEmail: normalizedEmails.representativeEmail || undefined,
techVendorType: Array.isArray(data.techVendorType) ? data.techVendorType.join(',') : undefined,
})
diff --git a/lib/tech-vendors/utils.ts b/lib/tech-vendors/utils.ts index ac91cd8d..ea8cdbd3 100644 --- a/lib/tech-vendors/utils.ts +++ b/lib/tech-vendors/utils.ts @@ -1,28 +1,56 @@ -import { LucideIcon, CheckCircle2, CircleAlert, Clock, ShieldAlert, Mail, BarChart2 } from "lucide-react";
-import type { TechVendor } from "@/db/schema/techVendors";
-
-type StatusType = TechVendor["status"];
-
-/**
- * 기술벤더 상태에 대한 아이콘을 반환합니다.
- */
-export function getVendorStatusIcon(status: StatusType): LucideIcon {
- switch (status) {
- case "PENDING_INVITE":
- return Clock;
- case "INVITED":
- return Mail;
- case "QUOTE_COMPARISON":
- return BarChart2;
- case "ACTIVE":
- return CheckCircle2;
- case "INACTIVE":
- return CircleAlert;
- case "BLACKLISTED":
- return ShieldAlert;
- default:
- return CircleAlert;
- }
-}
-
-
+import { LucideIcon, CheckCircle2, CircleAlert, Clock, ShieldAlert, Mail, BarChart2 } from "lucide-react"; +import type { TechVendor } from "@/db/schema/techVendors"; + +type StatusType = TechVendor["status"]; + +/** + * 기술벤더 상태에 대한 아이콘을 반환합니다. + */ +export function getVendorStatusIcon(status: StatusType): LucideIcon { + switch (status) { + case "PENDING_INVITE": + return Clock; + case "INVITED": + return Mail; + case "QUOTE_COMPARISON": + return BarChart2; + case "ACTIVE": + return CheckCircle2; + case "INACTIVE": + return CircleAlert; + case "BLACKLISTED": + return ShieldAlert; + default: + return CircleAlert; + } +} + +/** + * 이메일을 소문자로 변환합니다. + * null, undefined, 또는 빈 문자열인 경우 null을 반환합니다. + */ +export function normalizeEmail(email: string | null | undefined): string | null { + if (!email || typeof email !== 'string' || email.trim() === '') { + return null; + } + return email.toLowerCase().trim(); +} + +/** + * 여러 이메일 필드들을 소문자로 변환합니다. + */ +export function normalizeEmailFields(data: { + email?: string | null; + agentEmail?: string | null; + representativeEmail?: string | null; + contactEmail?: string | null; +}) { + return { + email: normalizeEmail(data.email), + agentEmail: normalizeEmail(data.agentEmail), + representativeEmail: normalizeEmail(data.representativeEmail), + contactEmail: normalizeEmail(data.contactEmail), + }; +} + + |
