summaryrefslogtreecommitdiff
path: root/lib/vendor-investigation
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-04-28 02:13:30 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-04-28 02:13:30 +0000
commitef4c533ebacc2cdc97e518f30e9a9350004fcdfb (patch)
tree345251a3ed0f4429716fa5edaa31024d8f4cb560 /lib/vendor-investigation
parent9ceed79cf32c896f8a998399bf1b296506b2cd4a (diff)
~20250428 작업사항
Diffstat (limited to 'lib/vendor-investigation')
-rw-r--r--lib/vendor-investigation/service.ts85
-rw-r--r--lib/vendor-investigation/table/contract-dialog.tsx85
-rw-r--r--lib/vendor-investigation/table/investigation-table.tsx56
-rw-r--r--lib/vendor-investigation/table/items-dialog.tsx73
4 files changed, 272 insertions, 27 deletions
diff --git a/lib/vendor-investigation/service.ts b/lib/vendor-investigation/service.ts
index b731a95c..e3d03cd4 100644
--- a/lib/vendor-investigation/service.ts
+++ b/lib/vendor-investigation/service.ts
@@ -12,6 +12,7 @@ import { sendEmail } from "../mail/sendEmail";
import fs from "fs"
import path from "path"
import { v4 as uuid } from "uuid"
+import { vendorsLogs } from "@/db/schema";
export async function getVendorsInvestigation(input: GetVendorsInvestigationSchema) {
return unstable_cache(
@@ -44,7 +45,7 @@ export async function getVendorsInvestigation(input: GetVendorsInvestigationSche
const finalWhere = and(
advancedWhere,
globalWhere,
- eq(vendorInvestigationsView.vendorStatus, "PQ_SUBMITTED")
+ // eq(vendorInvestigationsView.vendorStatus, "PQ_APPROVED")
)
@@ -82,6 +83,8 @@ export async function getVendorsInvestigation(input: GetVendorsInvestigationSche
// 7) Calculate pageCount
const pageCount = Math.ceil(total / input.perPage)
+ console.log(data,"data")
+
// Now 'data' already contains JSON arrays of contacts & items
// thanks to the subqueries in the view definition!
return { data, pageCount }
@@ -100,50 +103,84 @@ export async function getVendorsInvestigation(input: GetVendorsInvestigationSche
}
+/**
+ * Get existing investigations for a list of vendor IDs
+ *
+ * @param vendorIds Array of vendor IDs to check for existing investigations
+ * @returns Array of investigation data
+ */
+export async function getExistingInvestigationsForVendors(vendorIds: number[]) {
+ if (!vendorIds.length) return []
+
+ try {
+ // Query the vendorInvestigationsView using the vendorIds
+ const investigations = await db.query.vendorInvestigations.findMany({
+ where: inArray(vendorInvestigationsView.vendorId, vendorIds),
+ orderBy: [desc(vendorInvestigationsView.investigationCreatedAt)],
+ })
+
+ return investigations
+ } catch (error) {
+ console.error("Error fetching existing investigations:", error)
+ return []
+ }
+}
+
interface RequestInvestigateVendorsInput {
ids: number[]
}
export async function requestInvestigateVendors({
- ids,
-}: RequestInvestigateVendorsInput) {
+ ids, userId // userId를 추가
+}: RequestInvestigateVendorsInput & { userId: number }) {
try {
if (!ids || ids.length === 0) {
return { error: "No vendor IDs provided." }
}
- // 1. Create a new investigation row for each vendor
- // You could also check if an investigation already exists for each vendor
- // before inserting. For now, we’ll assume we always insert new ones.
- const newRecords = await db
- .insert(vendorInvestigations)
- .values(
- ids.map((vendorId) => ({
- vendorId
- }))
- )
- .returning()
-
- // 2. Optionally, send an email notification
- // Adjust recipient, subject, and body as needed.
+ const result = await db.transaction(async (tx) => {
+ // 1. Create a new investigation row for each vendor
+ const newRecords = await tx
+ .insert(vendorInvestigations)
+ .values(
+ ids.map((vendorId) => ({
+ vendorId
+ }))
+ )
+ .returning();
+
+ // 2. 각 벤더에 대해 로그 기록
+ await Promise.all(
+ ids.map(async (vendorId) => {
+ await tx.insert(vendorsLogs).values({
+ vendorId: vendorId,
+ userId: userId,
+ action: "investigation_requested",
+ comment: "Investigation requested for this vendor",
+ });
+ })
+ );
+
+ return newRecords;
+ });
+
+ // 3. 이메일 발송 (트랜잭션 외부에서 실행)
await sendEmail({
to: "dujin.kim@dtsolution.io",
subject: "New Vendor Investigation(s) Requested",
- // This template name could match a Handlebars file like: `investigation-request.hbs`
template: "investigation-request",
context: {
- // For example, if you're translating in Korean:
language: "ko",
- // Add any data you want to use within the template
vendorIds: ids,
notes: "Please initiate the planned investigations soon."
},
- })
+ });
- // 3. Optionally, revalidate any pages that might show updated data
- // revalidatePath("/your-vendors-page") // or wherever you list the vendors
+ // 4. 캐시 무효화
+ revalidateTag("vendors");
+ revalidateTag("vendor-investigations");
- return { data: newRecords, error: null }
+ return { data: result, error: null }
} catch (err: unknown) {
const errorMessage = err instanceof Error ? err.message : String(err)
return { error: errorMessage }
diff --git a/lib/vendor-investigation/table/contract-dialog.tsx b/lib/vendor-investigation/table/contract-dialog.tsx
new file mode 100644
index 00000000..28e6963b
--- /dev/null
+++ b/lib/vendor-investigation/table/contract-dialog.tsx
@@ -0,0 +1,85 @@
+"use client"
+
+import * as React from "react"
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+ DialogFooter,
+} from "@/components/ui/dialog"
+import { Button } from "@/components/ui/button"
+import { Avatar } from "@/components/ui/avatar"
+import { ScrollArea } from "@/components/ui/scroll-area"
+import { ContactItem } from "@/config/vendorInvestigationsColumnsConfig"
+
+interface ContactsDialogProps {
+ open: boolean
+ onOpenChange: (open: boolean) => void
+ investigationId: number | null
+ contacts: ContactItem[]
+}
+
+export function ContactsDialog({
+ open,
+ onOpenChange,
+ investigationId,
+ contacts,
+}: ContactsDialogProps) {
+ return (
+ <Dialog open={open} onOpenChange={onOpenChange}>
+ <DialogContent className="sm:max-w-md">
+ <DialogHeader>
+ <DialogTitle>Vendor Contacts</DialogTitle>
+ <DialogDescription>
+ {contacts.length > 0
+ ? `Showing ${contacts.length} contacts for investigation #${investigationId}`
+ : `No contacts found for investigation #${investigationId}`}
+ </DialogDescription>
+ </DialogHeader>
+ <ScrollArea className="max-h-[60vh] pr-4">
+ {contacts.length > 0 ? (
+ <div className="space-y-4">
+ {contacts.map((contact, index) => (
+ <div
+ key={index}
+ className="flex items-start gap-4 p-3 rounded-lg border"
+ >
+ <Avatar className="w-10 h-10">
+ <span>{contact.contactName?.charAt(0) || "C"}</span>
+ </Avatar>
+ <div className="flex-1 space-y-1">
+ <p className="font-medium">{contact.contactName || "Unnamed"}</p>
+ {contact.contactEmail && (
+ <p className="text-sm text-muted-foreground">
+ {contact.contactEmail}
+ </p>
+ )}
+ {contact.contactPhone && (
+ <p className="text-sm text-muted-foreground">
+ {contact.contactPhone}
+ </p>
+ )}
+ {contact.contactPosition && (
+ <p className="text-sm text-muted-foreground">
+ Position: {contact.contactPosition}
+ </p>
+ )}
+ </div>
+ </div>
+ ))}
+ </div>
+ ) : (
+ <div className="text-center py-6 text-muted-foreground">
+ No contacts available
+ </div>
+ )}
+ </ScrollArea>
+ <DialogFooter>
+ <Button onClick={() => onOpenChange(false)}>Close</Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ )
+} \ No newline at end of file
diff --git a/lib/vendor-investigation/table/investigation-table.tsx b/lib/vendor-investigation/table/investigation-table.tsx
index fa4e2ab8..56aa7962 100644
--- a/lib/vendor-investigation/table/investigation-table.tsx
+++ b/lib/vendor-investigation/table/investigation-table.tsx
@@ -21,6 +21,8 @@ import {
PossibleItem
} from "@/config/vendorInvestigationsColumnsConfig"
import { UpdateVendorInvestigationSheet } from "./update-investigation-sheet"
+import { ItemsDrawer } from "./items-dialog"
+import { ContactsDialog } from "./contract-dialog"
interface VendorsTableProps {
promises: Promise<
@@ -71,18 +73,48 @@ export function VendorsInvestigationTable({ promises }: VendorsTableProps) {
} as VendorInvestigationsViewWithContacts
})
}, [rawResponse.data])
+
+ console.log(transformedData)
const pageCount = rawResponse.pageCount
+ // Add state for row actions
const [rowAction, setRowAction] = React.useState<DataTableRowAction<VendorInvestigationsViewWithContacts> | null>(null)
+ // Add state for contacts dialog
+ const [contactsDialogOpen, setContactsDialogOpen] = React.useState(false)
+ const [selectedContacts, setSelectedContacts] = React.useState<ContactItem[]>([])
+ const [selectedContactInvestigationId, setSelectedContactInvestigationId] = React.useState<number | null>(null)
+
+ // Add state for items drawer
+ const [itemsDrawerOpen, setItemsDrawerOpen] = React.useState(false)
+ const [selectedItems, setSelectedItems] = React.useState<PossibleItem[]>([])
+ const [selectedItemInvestigationId, setSelectedItemInvestigationId] = React.useState<number | null>(null)
+
+ // Create handlers for opening the contacts dialog and items drawer
+ const openContactsModal = React.useCallback((investigationId: number, contacts: ContactItem[]) => {
+ setSelectedContactInvestigationId(investigationId)
+ setSelectedContacts(contacts || [])
+ setContactsDialogOpen(true)
+ }, [])
+
+ const openItemsDrawer = React.useCallback((investigationId: number, items: PossibleItem[]) => {
+ setSelectedItemInvestigationId(investigationId)
+ setSelectedItems(items || [])
+ setItemsDrawerOpen(true)
+ }, [])
+
// Get router
const router = useRouter()
- // Call getColumns() with router injection
+ // Call getColumns() with all required functions
const columns = React.useMemo(
- () => getColumns({ setRowAction }),
- [setRowAction, router]
+ () => getColumns({
+ setRowAction,
+ openContactsModal,
+ openItemsDrawer
+ }),
+ [setRowAction, openContactsModal, openItemsDrawer]
)
const filterFields: DataTableFilterField<VendorInvestigationsViewWithContacts>[] = [
@@ -123,11 +155,29 @@ export function VendorsInvestigationTable({ promises }: VendorsTableProps) {
<VendorsTableToolbarActions table={table} />
</DataTableAdvancedToolbar>
</DataTable>
+
+ {/* Update Investigation Sheet */}
<UpdateVendorInvestigationSheet
open={rowAction?.type === "update"}
onOpenChange={() => setRowAction(null)}
investigation={rowAction?.row.original ?? null}
/>
+
+ {/* Contacts Dialog */}
+ <ContactsDialog
+ open={contactsDialogOpen}
+ onOpenChange={setContactsDialogOpen}
+ investigationId={selectedContactInvestigationId}
+ contacts={selectedContacts}
+ />
+
+ {/* Items Drawer */}
+ <ItemsDrawer
+ open={itemsDrawerOpen}
+ onOpenChange={setItemsDrawerOpen}
+ investigationId={selectedItemInvestigationId}
+ items={selectedItems}
+ />
</>
)
} \ No newline at end of file
diff --git a/lib/vendor-investigation/table/items-dialog.tsx b/lib/vendor-investigation/table/items-dialog.tsx
new file mode 100644
index 00000000..5d010ff4
--- /dev/null
+++ b/lib/vendor-investigation/table/items-dialog.tsx
@@ -0,0 +1,73 @@
+"use client"
+
+import * as React from "react"
+import {
+ Sheet,
+ SheetContent,
+ SheetDescription,
+ SheetHeader,
+ SheetTitle,
+ SheetFooter,
+} from "@/components/ui/sheet"
+import { Button } from "@/components/ui/button"
+import { ScrollArea } from "@/components/ui/scroll-area"
+import { PossibleItem } from "@/config/vendorInvestigationsColumnsConfig"
+
+interface ItemsDrawerProps {
+ open: boolean
+ onOpenChange: (open: boolean) => void
+ investigationId: number | null
+ items: PossibleItem[]
+}
+
+export function ItemsDrawer({
+ open,
+ onOpenChange,
+ investigationId,
+ items,
+}: ItemsDrawerProps) {
+ return (
+ <Sheet open={open} onOpenChange={onOpenChange}>
+ <SheetContent className="sm:max-w-md">
+ <SheetHeader>
+ <SheetTitle>Possible Items</SheetTitle>
+ <SheetDescription>
+ {items.length > 0
+ ? `Showing ${items.length} items for investigation #${investigationId}`
+ : `No items found for investigation #${investigationId}`}
+ </SheetDescription>
+ </SheetHeader>
+ <ScrollArea className="max-h-[70vh] mt-6 pr-4">
+ {items.length > 0 ? (
+ <div className="space-y-4">
+ {items.map((item, index) => (
+ <div
+ key={index}
+ className="flex flex-col gap-2 p-3 rounded-lg border"
+ >
+ <div className="flex justify-between items-start">
+ <h4 className="font-medium">{item.itemName || "Unknown Item"}</h4>
+ {item.itemName && (
+ <span className="text-xs bg-muted px-2 py-1 rounded">
+ {item.itemCode}
+ </span>
+ )}
+ </div>
+
+
+ </div>
+ ))}
+ </div>
+ ) : (
+ <div className="text-center py-6 text-muted-foreground">
+ No items available
+ </div>
+ )}
+ </ScrollArea>
+ <SheetFooter className="mt-4">
+ <Button onClick={() => onOpenChange(false)}>Close</Button>
+ </SheetFooter>
+ </SheetContent>
+ </Sheet>
+ )
+} \ No newline at end of file