diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-04-28 02:13:30 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-04-28 02:13:30 +0000 |
| commit | ef4c533ebacc2cdc97e518f30e9a9350004fcdfb (patch) | |
| tree | 345251a3ed0f4429716fa5edaa31024d8f4cb560 /lib/pq | |
| parent | 9ceed79cf32c896f8a998399bf1b296506b2cd4a (diff) | |
~20250428 작업사항
Diffstat (limited to 'lib/pq')
| -rw-r--r-- | lib/pq/service.ts | 77 | ||||
| -rw-r--r-- | lib/pq/table/import-pq-handler.tsx | 11 |
2 files changed, 54 insertions, 34 deletions
diff --git a/lib/pq/service.ts b/lib/pq/service.ts index ad7e60c4..6159a307 100644 --- a/lib/pq/service.ts +++ b/lib/pq/service.ts @@ -19,6 +19,7 @@ import { writeFile, mkdir } from 'fs/promises'; import { GetVendorsSchema } from "../vendors/validations"; import { countVendors, selectVendors } from "../vendors/repository"; import { projects } from "@/db/schema"; +import { headers } from 'next/headers'; /** * PQ 목록 조회 @@ -144,8 +145,8 @@ export async function getPQs( const createPqSchema = z.object({ code: z.string().min(1, "Code is required"), checkPoint: z.string().min(1, "Check point is required"), - description: z.string().optional(), - remarks: z.string().optional(), + description: z.string().optional().nullable(), + remarks: z.string().optional().nullable(), groupName: z.string().optional() }); @@ -409,7 +410,7 @@ export async function getPQDataByVendorId( contractInfo: pqCriteriasExtension.contractInfo, additionalRequirement: pqCriteriasExtension.additionalRequirement, - // 벤더 응답 필드 + // 협력업체 응답 필드 answer: vendorPqCriteriaAnswers.answer, answerId: vendorPqCriteriaAnswers.id, @@ -653,8 +654,8 @@ export async function savePQAnswersAction(input: SavePQInput) { /** - * PQ 제출 서버 액션 - 벤더 상태를 PQ_SUBMITTED로 업데이트 - * @param vendorId 벤더 ID + * PQ 제출 서버 액션 - 협력업체 상태를 PQ_SUBMITTED로 업데이트 + * @param vendorId 협력업체 ID */ export async function submitPQAction({ vendorId, @@ -666,6 +667,9 @@ export async function submitPQAction({ unstable_noStore(); try { + const headersList = await headers(); + const host = headersList.get('host') || 'localhost:3000'; + // 1. 모든 PQ 항목에 대한 응답이 있는지 검증 const queryConditions = [ eq(vendorPqCriteriaAnswers.vendorId, vendorId) @@ -690,7 +694,7 @@ export async function submitPQAction({ return { ok: false, error: "No PQ answers found" }; } - // 2. 벤더 정보 조회 + // 2. 협력업체 정보 조회 const vendor = await db .select({ id: vendors.id, @@ -770,7 +774,7 @@ export async function submitPQAction({ }); } } else { - // 일반 PQ인 경우 벤더 상태 검증 및 업데이트 + // 일반 PQ인 경우 협력업체 상태 검증 및 업데이트 const allowedStatuses = ["IN_PQ", "PENDING_REVIEW", "IN_REVIEW", "REJECTED", "PQ_FAILED"]; if (!allowedStatuses.includes(vendor.status)) { @@ -798,8 +802,8 @@ export async function submitPQAction({ : `[eVCP] PQ Submitted: ${vendor.vendorName}`; const adminUrl = projectId - ? `${process.env.NEXT_PUBLIC_APP_URL}/admin/vendors/${vendorId}/projects/${projectId}/pq` - : `${process.env.NEXT_PUBLIC_APP_URL}/admin/vendors/${vendorId}/pq`; + ? `http://${host}/evcp/pq/${vendorId}?projectId=${projectId}` + : `http://${host}/evcp/pq/${vendorId}`; await sendEmail({ to: process.env.ADMIN_EMAIL, @@ -828,9 +832,7 @@ export async function submitPQAction({ ? `[eVCP] Project PQ Submission Confirmation for ${projectName}` : "[eVCP] PQ Submission Confirmation"; - const portalUrl = projectId - ? `${process.env.NEXT_PUBLIC_APP_URL}/dashboard/projects/${projectId}` - : `${process.env.NEXT_PUBLIC_APP_URL}/dashboard`; + const portalUrl = `${host}/dashboard`; await sendEmail({ to: vendor.email, @@ -843,7 +845,7 @@ export async function submitPQAction({ isProjectPQ: !!projectId, submittedDate: currentDate.toLocaleString(), portalUrl, - } + } }); } catch (emailError) { console.error("Failed to send vendor confirmation:", emailError); @@ -1001,10 +1003,10 @@ export async function getVendorsInPQ(input: GetVendorsSchema) { // 트랜잭션 내에서 데이터 조회 const { data, total } = await db.transaction(async (tx) => { - // 벤더 ID 모음 (중복 제거용) + // 협력업체 ID 모음 (중복 제거용) const vendorIds = new Set<number>(); - // 1-A) 일반 PQ 답변이 있는 벤더 찾기 (status와 상관없이) + // 1-A) 일반 PQ 답변이 있는 협력업체 찾기 (status와 상관없이) const generalPqVendors = await tx .select({ vendorId: vendorPqCriteriaAnswers.vendorId @@ -1025,7 +1027,7 @@ export async function getVendorsInPQ(input: GetVendorsSchema) { generalPqVendors.forEach(v => vendorIds.add(v.vendorId)); - // 1-B) 프로젝트 PQ 답변이 있는 벤더 ID 조회 (status와 상관없이) + // 1-B) 프로젝트 PQ 답변이 있는 협력업체 ID 조회 (status와 상관없이) const projectPqVendors = await tx .select({ vendorId: vendorProjectPQs.vendorId @@ -1046,7 +1048,7 @@ export async function getVendorsInPQ(input: GetVendorsSchema) { projectPqVendors.forEach(v => vendorIds.add(v.vendorId)); - // 중복 제거된 벤더 ID 배열 + // 중복 제거된 협력업체 ID 배열 const uniqueVendorIds = Array.from(vendorIds); // 총 개수 (중복 제거 후) @@ -1059,13 +1061,13 @@ export async function getVendorsInPQ(input: GetVendorsSchema) { // 페이징 처리 (정렬 후 limit/offset 적용) const paginatedIds = uniqueVendorIds.slice(offset, offset + input.perPage); - // 2) 페이징된 벤더 상세 정보 조회 + // 2) 페이징된 협력업체 상세 정보 조회 const vendorsData = await selectVendors(tx, { where: inArray(vendors.id, paginatedIds), orderBy: input.sort.length > 0 ? input.sort.map((item) => - item.desc ? desc(vendors[item.id]) : asc(vendors[item.id]) - ) + item.desc ? desc(vendors.vendorName) : asc(vendors.vendorName) + ) : [asc(vendors.createdAt)], }); @@ -1204,7 +1206,7 @@ function getPqStatusDisplay( return "PQ 정보 없음"; } -// 벤더 상태 텍스트 변환 +// 협력업체 상태 텍스트 변환 function getPqVendorStatusText(status: string): string { switch (status) { case "IN_PQ": return "진행중"; @@ -1249,6 +1251,9 @@ export type VendorStatus = if (!vendor) { return { ok: false, error: "Vendor not found" } } + const headersList = await headers(); + const host = headersList.get('host') || 'localhost:3000'; + const loginUrl = `http://${host}/partners/pq` // 3) Send email await sendEmail({ @@ -1258,7 +1263,7 @@ export type VendorStatus = context: { name: vendor.vendorName, status: newStatus, - loginUrl: `${process.env.NEXT_PUBLIC_URL}/partners/pq`, // etc. + loginUrl: loginUrl, // etc. }, }) revalidateTag("vendors") @@ -1354,7 +1359,7 @@ export async function updateProjectPQStatusAction({ projectName: project.name, rejectionReason: status === "REJECTED" ? comment : undefined, hasRejectionReason: status === "REJECTED" && !!comment, - loginUrl: `${process.env.NEXT_PUBLIC_URL}/partners/projects/${projectId}/pq`, + loginUrl: `${process.env.NEXT_PUBLIC_URL}/partners/pq?projectId=${projectId}`, approvalDate: status === "APPROVED" ? currentDate.toLocaleDateString() : undefined, rejectionDate: status === "REJECTED" ? currentDate.toLocaleDateString() : undefined, }, @@ -1385,7 +1390,7 @@ interface ItemComment { /** * PQ 변경 요청 처리 서버 액션 * - * @param vendorId 벤더 ID + * @param vendorId 협력업체 ID * @param comment 항목별 코멘트 배열 (answerId, checkPoint, code, comment로 구성) * @param generalComment 전체 PQ에 대한 일반 코멘트 (선택사항) */ @@ -1394,11 +1399,15 @@ export async function requestPqChangesAction({ projectId, comment, generalComment, + reviewerName, + reviewerId }: { vendorId: number; projectId?: number; // Optional project ID for project-specific PQs comment: ItemComment[]; generalComment?: string; + reviewerName?: string; + reviewerId?: string; }) { try { // 1) 상태 업데이트 (PQ 타입에 따라 다르게 처리) @@ -1442,7 +1451,7 @@ export async function requestPqChangesAction({ .where(eq(vendors.id, vendorId)); } - // 2) 벤더 정보 가져오기 + // 2) 협력업체 정보 가져오기 const vendor = await db .select() .from(vendors) @@ -1469,8 +1478,7 @@ export async function requestPqChangesAction({ // 3) 각 항목별 코멘트 저장 const currentDate = new Date(); - const reviewerId = 1; // 관리자 ID (실제 구현에서는 세션에서 가져옵니다) - const reviewerName = "AdminUser"; // 실제 구현에서는 세션에서 가져옵니다 + // 병렬로 모든 코멘트 저장 if (comment && comment.length > 0) { @@ -1508,7 +1516,7 @@ export async function requestPqChangesAction({ // 로그인 URL - 프로젝트 PQ인 경우 다른 경로로 안내 const loginUrl = projectId - ? `${process.env.NEXT_PUBLIC_URL}/partners/projects/${projectId}/pq` + ? `${process.env.NEXT_PUBLIC_URL}/partners/pq?projectId=${projectId}` : `${process.env.NEXT_PUBLIC_URL}/partners/pq`; await sendEmail({ @@ -1676,9 +1684,22 @@ export async function getVendorPQsList(vendorId: number): Promise<VendorPQsList> export async function loadGeneralPQData(vendorId: number) { + "use server"; return getPQDataByVendorId(vendorId) } export async function loadProjectPQData(vendorId: number, projectId: number) { + "use server"; return getPQDataByVendorId(vendorId, projectId) +} + +export async function loadGeneralPQAction(vendorId: number) { + return getPQDataByVendorId(vendorId); +} + +export async function loadProjectPQAction(vendorId: number, projectId?: number): Promise<PQGroupData[]> { + if (!projectId) { + throw new Error("Project ID is required for loading project PQ data"); + } + return getPQDataByVendorId(vendorId, projectId); }
\ No newline at end of file diff --git a/lib/pq/table/import-pq-handler.tsx b/lib/pq/table/import-pq-handler.tsx index aa5e6c47..13431ba7 100644 --- a/lib/pq/table/import-pq-handler.tsx +++ b/lib/pq/table/import-pq-handler.tsx @@ -77,10 +77,10 @@ export async function processFileImport( code: row.Code?.toString().trim() ?? "", checkPoint: row["Check Point"]?.toString().trim() ?? "", groupName: row["Group Name"]?.toString().trim() ?? "", - description: row.Description?.toString() ?? null, - remarks: row.Remarks?.toString() ?? null, - contractInfo: row["Contract Info"]?.toString() ?? null, - additionalRequirement: row["Additional Requirements"]?.toString() ?? null, + description: row.Description?.toString() ?? "", + remarks: row.Remarks?.toString() ?? "", + contractInfo: row["Contract Info"]?.toString() ?? "", + additionalRequirement: row["Additional Requirements"]?.toString() ?? "", }; // 데이터 유효성 검사 @@ -109,8 +109,7 @@ export async function processFileImport( // PQ 생성 서버 액션 호출 const createResult = await createPq({ ...cleanedRow, - projectId: projectId, - isProjectSpecific: !!projectId, + projectId: projectId || 0 }); if (createResult.success) { |
