summaryrefslogtreecommitdiff
path: root/lib/pq/service.ts
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/pq/service.ts
parent9ceed79cf32c896f8a998399bf1b296506b2cd4a (diff)
~20250428 작업사항
Diffstat (limited to 'lib/pq/service.ts')
-rw-r--r--lib/pq/service.ts77
1 files changed, 49 insertions, 28 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