"use server"; // Next.js 서버 액션에서 직접 import하려면 (선택) import db from "@/db/db"; import { filterColumns } from "@/lib/filter-columns"; import { asc, desc, ilike, inArray, and, gte, lte, not, or, sql, eq } from "drizzle-orm"; import { generalEvaluations} from "@/db/schema"; import { GetGeneralEvaluationsSchema } from "./validation"; import { selectGeneralCheckLists , countGeneralCheckList} from "./repository"; export async function getGeneralCheckList(input: GetGeneralEvaluationsSchema) { try { const offset = (input.page - 1) * input.perPage; // 고급 필터 처리 - 테이블의 DataTableFilterList에서 오는 필터 const advancedFilters = input.filters || []; const advancedJoinOperator = input.joinOperator || "and"; // 고급 필터 조건 생성 let advancedWhere; if (advancedFilters.length > 0) { advancedWhere = filterColumns({ table: generalEvaluations, filters: advancedFilters, joinOperator: advancedJoinOperator, }); } // 전역 검색 조건 let globalWhere; if (input.search) { const s = `%${input.search}%`; globalWhere = or( ilike(generalEvaluations.category, s), ilike(generalEvaluations.inspectionItem, s), ilike(generalEvaluations.remarks, s), ); } // 모든 조건 결합 let whereConditions = []; if (advancedWhere) whereConditions.push(advancedWhere); if (globalWhere) whereConditions.push(globalWhere); // 조건이 있을 때만 and() 사용 const finalWhere = whereConditions.length > 0 ? and(...whereConditions) : undefined; // 정렬 조건 - 안전하게 처리 const orderBy = input.sort && input.sort.length > 0 ? input.sort.map((item) => item.desc ? desc(generalEvaluations[item.id]) : asc(generalEvaluations[item.id]) ) : [desc(generalEvaluations.updatedAt)] // 트랜잭션 내부에서 Repository 호출 const { data, total } = await db.transaction(async (tx) => { const data = await selectGeneralCheckLists(tx, { where: finalWhere, orderBy, offset, limit: input.perPage, }); const total = await countGeneralCheckList(tx, finalWhere); return { data, total }; }); const pageCount = Math.ceil(total / input.perPage); return { data, pageCount }; } catch (err) { console.error("getRfqs 에러:", err); // 에러 세부 정보 더 자세히 로깅 if (err instanceof Error) { console.error("에러 메시지:", err.message); console.error("에러 스택:", err.stack); if ('code' in err) { console.error("SQL 에러 코드:", (err as any).code); } } // 에러 발생 시 디폴트 return { data: [], pageCount: 0 }; } } // ───────────────────────────────────────────────────────────────────────────── // Types // ───────────────────────────────────────────────────────────────────────────── export type GeneralEvaluationInput = { category: string; inspectionItem: string; remarks?: string | null; isActive?: boolean; }; // ───────────────────────────────────────────────────────────────────────────── // Helpers // ───────────────────────────────────────────────────────────────────────────── async function generateSerialNumber(tx: typeof db, category: string) { const prefix = `GE-${category}-`; // 카테고리 내에서 가장 최근 시리얼 찾아서 +1 const latest = await tx .select({ serialNumber: generalEvaluations.serialNumber }) .from(generalEvaluations) .where(eq(generalEvaluations.category, category)) .orderBy(desc(generalEvaluations.serialNumber)) .limit(1); let nextSeq = 1; if (latest.length) { const parts = latest[0].serialNumber.split("-"); const last = parts[parts.length - 1]; const num = parseInt(last, 10); if (!isNaN(num)) nextSeq = num + 1; } const seqStr = nextSeq.toString().padStart(3, "0"); return `${prefix}${seqStr}`; } // ───────────────────────────────────────────────────────────────────────────── // CRUD Actions // ───────────────────────────────────────────────────────────────────────────── export async function createGeneralEvaluation(input: GeneralEvaluationInput) { return db.transaction(async (tx) => { try { const serialNumber = await generateSerialNumber(tx, input.category); const [created] = await tx .insert(generalEvaluations) .values({ serialNumber, category: input.category, inspectionItem: input.inspectionItem, remarks: input.remarks ?? null, isActive: input.isActive ?? true, }) .returning(); return { success: true, data: created, message: "체크리스트가 추가되었습니다." }; } catch (err) { console.error("createGeneralEvaluation error", err); return { success: false, message: "추가 중 오류가 발생했습니다." }; } }); } export async function updateGeneralEvaluation(id: number, fields: Partial) { try { const [updated] = await db .update(generalEvaluations) .set({ ...fields, updatedAt: sql`now()` }) .where(eq(generalEvaluations.id, id)) .returning(); return { success: true, data: updated, message: "수정되었습니다." }; } catch (err) { console.error("updateGeneralEvaluation error", err); return { success: false, message: "수정 중 오류가 발생했습니다." }; } } export async function deleteGeneralEvaluations(ids: number[]) { if (ids.length === 0) return { success: false, message: "삭제할 항목이 없습니다." }; try { await db.delete(generalEvaluations).where(inArray(generalEvaluations.id, ids)); return { success: true, message: `${ids.length}개의 체크리스트가 삭제되었습니다.` }; } catch (err) { console.error("deleteGeneralEvaluations error", err); return { success: false, message: "삭제 중 오류가 발생했습니다." }; } } // ───────────────────────────────────────────────────────────────────────────── // Pagination Search (기존 getGeneralCheckList → getGeneralEvaluations) // ───────────────────────────────────────────────────────────────────────────── export async function getGeneralEvaluations(input: GetGeneralEvaluationsSchema) { const offset = (input.page - 1) * input.perPage; // 고급 필터 처리 const advFilters = input.filters ?? []; const advOperator = input.joinOperator ?? "and"; const advWhere = advFilters.length ? filterColumns({ table: generalEvaluations, filters: advFilters, joinOperator: advOperator }) : undefined; // 전역 검색 const globalWhere = input.search ? or( ilike(generalEvaluations.serialNumber, `%${input.search}%`), ilike(generalEvaluations.category, `%${input.search}%`), ilike(generalEvaluations.inspectionItem, `%${input.search}%`), ilike(generalEvaluations.remarks, `%${input.search}%`) ) : undefined; const whereAll = [advWhere, globalWhere].filter(Boolean); const finalWhere = whereAll.length ? and(...whereAll) : undefined; // 정렬 const orderBy = input.sort && input.sort.length > 0 ? input.sort.map((item) => item.desc ? desc(generalEvaluations[item.id]) : asc(generalEvaluations[item.id]) ) : [desc(generalEvaluations.updatedAt)] // 트랜잭션 const { data, total } = await db.transaction(async (tx) => { const data = await tx .select() .from(generalEvaluations) .where(finalWhere ?? undefined) .orderBy(...orderBy) .limit(input.perPage) .offset(offset); const [{ count }] = await tx .select({ count: sql`count(*)` }) .from(generalEvaluations) .where(finalWhere ?? undefined); return { data, total: count }; }); return { data, pageCount: Math.ceil(total / input.perPage) }; }