summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/evaluation/service.ts81
-rw-r--r--lib/evaluation/table/evaluation-details-dialog.tsx74
-rw-r--r--lib/evaluation/vendor-submission-service.ts66
-rw-r--r--lib/vendor-evaluation-submit/table/esg-evaluation-form-sheet.tsx7
4 files changed, 221 insertions, 7 deletions
diff --git a/lib/evaluation/service.ts b/lib/evaluation/service.ts
index b958e371..122d0777 100644
--- a/lib/evaluation/service.ts
+++ b/lib/evaluation/service.ts
@@ -1283,10 +1283,91 @@ export async function getEvaluationDetails(periodicEvaluationId: number): Promis
questionsWithAttachments: new Set(attachmentsData.map(att => att.criteriaId).filter(Boolean)).size,
}
+ // ๐Ÿ”„ 7. ์กฐ์„ /ํ•ด์–‘ ์ทจํ•ฉ ์ •๋ณด ๊ณ„์‚ฐ (๋™์‹œ ํ‰๊ฐ€์ธ ๊ฒฝ์šฐ)
+ let consolidatedInfo: {
+ shipbuildingScore: number | null
+ shipbuildingGrade: string | null
+ offshoreScore: number | null
+ offshoreGrade: string | null
+ consolidatedScore: number | null
+ consolidatedGrade: string | null
+ } | undefined = undefined
+
+ // ํ˜„์žฌ ํ‰๊ฐ€์˜ evaluationTarget ์ •๋ณด ๊ฐ€์ ธ์˜ค๊ธฐ
+ const currentEvaluationTarget = await db
+ .select({
+ id: evaluationTargets.id,
+ vendorId: evaluationTargets.vendorId,
+ evaluationYear: evaluationTargets.evaluationYear,
+ evaluationRound: evaluationTargets.evaluationRound,
+ division: evaluationTargets.division,
+ })
+ .from(evaluationTargets)
+ .innerJoin(periodicEvaluations, eq(periodicEvaluations.evaluationTargetId, evaluationTargets.id))
+ .where(eq(periodicEvaluations.id, periodicEvaluationId))
+ .limit(1)
+
+ if (currentEvaluationTarget.length > 0) {
+ const target = currentEvaluationTarget[0]
+
+ // ๊ฐ™์€ ์—…์ฒด, ๊ฐ™์€ ๋…„๋„, ๊ฐ™์€ ๋ผ์šด๋“œ์˜ ๋‹ค๋ฅธ division ํ‰๊ฐ€๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ
+ const siblingEvaluations = await db
+ .select({
+ periodicEvaluationId: periodicEvaluations.id,
+ division: evaluationTargets.division,
+ finalScore: periodicEvaluations.finalScore,
+ finalGrade: periodicEvaluations.finalGrade,
+ })
+ .from(evaluationTargets)
+ .innerJoin(periodicEvaluations, eq(periodicEvaluations.evaluationTargetId, evaluationTargets.id))
+ .where(
+ and(
+ eq(evaluationTargets.vendorId, target.vendorId),
+ eq(evaluationTargets.evaluationYear, target.evaluationYear),
+ eq(evaluationTargets.evaluationRound, target.evaluationRound || "")
+ )
+ )
+
+ // ์กฐ์„ ๊ณผ ํ•ด์–‘ ํ‰๊ฐ€๊ฐ€ ๋ชจ๋‘ ์žˆ๋Š”์ง€ ํ™•์ธ
+ const shipbuilding = siblingEvaluations.find(e => e.division === "SHIPBUILDING")
+ const offshore = siblingEvaluations.find(e => e.division === "PLANT")
+
+ if (shipbuilding && offshore) {
+ // ๋‘ ํ‰๊ฐ€๊ฐ€ ๋ชจ๋‘ ์žˆ์œผ๋ฉด ์ทจํ•ฉ ์ •๋ณด ๊ณ„์‚ฐ
+ const shipScore = shipbuilding.finalScore ? Number(shipbuilding.finalScore) : null
+ const offScore = offshore.finalScore ? Number(offshore.finalScore) : null
+
+ let consolidatedScore: number | null = null
+ let consolidatedGrade: string | null = null
+
+ // ๋‘˜ ๋‹ค ์ ์ˆ˜๊ฐ€ ์žˆ์œผ๋ฉด 50% ๋ฐ˜์˜ํ•˜์—ฌ ์ทจํ•ฉ
+ if (shipScore !== null && offScore !== null) {
+ consolidatedScore = Math.round((shipScore * 0.5 + offScore * 0.5) * 10) / 10
+
+ // ์ทจํ•ฉ ๋“ฑ๊ธ‰ ๊ณ„์‚ฐ (์ ์ˆ˜ ๊ธฐ์ค€)
+ if (consolidatedScore >= 90) consolidatedGrade = "S"
+ else if (consolidatedScore >= 80) consolidatedGrade = "A"
+ else if (consolidatedScore >= 70) consolidatedGrade = "B"
+ else if (consolidatedScore >= 60) consolidatedGrade = "C"
+ else consolidatedGrade = "D"
+ }
+
+ consolidatedInfo = {
+ shipbuildingScore: shipScore,
+ shipbuildingGrade: shipbuilding.finalGrade,
+ offshoreScore: offScore,
+ offshoreGrade: offshore.finalGrade,
+ consolidatedScore,
+ consolidatedGrade,
+ }
+ }
+ }
+
return {
evaluationInfo: evaluationInfo[0],
reviewerDetails: Array.from(reviewerDetailsMap.values()),
attachmentStats,
+ consolidatedInfo,
}
} catch (error) {
diff --git a/lib/evaluation/table/evaluation-details-dialog.tsx b/lib/evaluation/table/evaluation-details-dialog.tsx
index 2f682402..fe7c204a 100644
--- a/lib/evaluation/table/evaluation-details-dialog.tsx
+++ b/lib/evaluation/table/evaluation-details-dialog.tsx
@@ -228,6 +228,80 @@ export function EvaluationDetailsDialog({
</div>
</div>
+ {/* ๐Ÿ”„ ์กฐ์„ /ํ•ด์–‘ ์ทจํ•ฉ ์ •๋ณด (๋™์‹œ ํ‰๊ฐ€์ธ ๊ฒฝ์šฐ) */}
+ {evaluationDetails?.consolidatedInfo && (
+ <div className="mt-4 p-4 bg-blue-50 border border-blue-200 rounded-md">
+ <div className="flex items-center gap-2 mb-3">
+ <BarChart3 className="h-5 w-5 text-blue-600" />
+ <span className="font-semibold text-blue-900">์กฐ์„ /ํ•ด์–‘ ์ทจํ•ฉ ๊ฒฐ๊ณผ</span>
+ </div>
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm">
+ {/* ์กฐ์„  ์ ์ˆ˜ */}
+ <div className="space-y-1">
+ <div className="text-muted-foreground">์กฐ์„  ํ™•์ •์ ์ˆ˜/๋“ฑ๊ธ‰</div>
+ <div className="flex items-center gap-1">
+ {evaluationDetails.consolidatedInfo.shipbuildingScore !== null ? (
+ <>
+ <span className="font-bold text-blue-700">
+ {evaluationDetails.consolidatedInfo.shipbuildingScore.toFixed(1)}์ 
+ </span>
+ {evaluationDetails.consolidatedInfo.shipbuildingGrade && (
+ <Badge variant="outline" className="text-xs h-5">
+ {evaluationDetails.consolidatedInfo.shipbuildingGrade}
+ </Badge>
+ )}
+ </>
+ ) : (
+ <span className="text-muted-foreground">-</span>
+ )}
+ </div>
+ </div>
+
+ {/* ํ•ด์–‘ ์ ์ˆ˜ */}
+ <div className="space-y-1">
+ <div className="text-muted-foreground">ํ•ด์–‘ ํ™•์ •์ ์ˆ˜/๋“ฑ๊ธ‰</div>
+ <div className="flex items-center gap-1">
+ {evaluationDetails.consolidatedInfo.offshoreScore !== null ? (
+ <>
+ <span className="font-bold text-blue-700">
+ {evaluationDetails.consolidatedInfo.offshoreScore.toFixed(1)}์ 
+ </span>
+ {evaluationDetails.consolidatedInfo.offshoreGrade && (
+ <Badge variant="outline" className="text-xs h-5">
+ {evaluationDetails.consolidatedInfo.offshoreGrade}
+ </Badge>
+ )}
+ </>
+ ) : (
+ <span className="text-muted-foreground">-</span>
+ )}
+ </div>
+ </div>
+
+ {/* ์ทจํ•ฉ ์ ์ˆ˜ (50% ๋ฐ˜์˜) */}
+ <div className="space-y-1">
+ <div className="text-muted-foreground">์ทจํ•ฉ ์ ์ˆ˜/๋“ฑ๊ธ‰ (50% ๋ฐ˜์˜)</div>
+ <div className="flex items-center gap-1">
+ {evaluationDetails.consolidatedInfo.consolidatedScore !== null ? (
+ <>
+ <span className="font-bold text-purple-700 text-base">
+ {evaluationDetails.consolidatedInfo.consolidatedScore.toFixed(1)}์ 
+ </span>
+ {evaluationDetails.consolidatedInfo.consolidatedGrade && (
+ <Badge variant="default" className="bg-purple-600 text-xs h-5">
+ {evaluationDetails.consolidatedInfo.consolidatedGrade}
+ </Badge>
+ )}
+ </>
+ ) : (
+ <span className="text-muted-foreground">-</span>
+ )}
+ </div>
+ </div>
+ </div>
+ </div>
+ )}
+
</CardContent>
</Card>
</DialogHeader>
diff --git a/lib/evaluation/vendor-submission-service.ts b/lib/evaluation/vendor-submission-service.ts
index 388f382a..c06a9d2a 100644
--- a/lib/evaluation/vendor-submission-service.ts
+++ b/lib/evaluation/vendor-submission-service.ts
@@ -129,6 +129,7 @@ export async function getVendorSubmissionDetails(periodicEvaluationId: number):
// ํ˜‘๋ ฅ์—…์ฒด ์ •๋ณด
vendorId: vendors.id,
+ companyId: evaluationSubmissions.companyId,
vendorCode: vendors.vendorCode,
vendorName: vendors.vendorName,
vendorEmail: vendors.email,
@@ -149,7 +150,7 @@ export async function getVendorSubmissionDetails(periodicEvaluationId: number):
}
const submission = submissionResult[0]
- const submissionId = submission.id // evaluationSubmissions.id (integer)
+ let submissionId = submission.id // evaluationSubmissions.id (integer)
const submissionUuid = submission.submissionId // evaluationSubmissions.submissionId (UUID)
console.log("=== ํ˜‘๋ ฅ์—…์ฒด ์ œ์ถœ ์ƒ์„ธ ์กฐํšŒ ์‹œ์ž‘ ===")
@@ -157,6 +158,58 @@ export async function getVendorSubmissionDetails(periodicEvaluationId: number):
console.log("submissionUuid:", submissionUuid)
console.log("submission:", submission)
+ // ๐Ÿ” ์กฐ์„ /ํ•ด์–‘ ๋™์‹œ ์ œ์ถœ ์ผ€์ด์Šค ์ฒ˜๋ฆฌ: ํ˜„์žฌ submission์— ์‘๋‹ต์ด ์—†์œผ๋ฉด ๊ฐ™์€ ๊ทธ๋ฃน์—์„œ ์‘๋‹ต์ด ์žˆ๋Š” submission์„ ์ฐพ์•„์„œ ์‚ฌ์šฉ
+ const hasResponses = await db
+ .select({ count: sql<number>`COUNT(*)::int` })
+ .from(generalEvaluationResponses)
+ .where(
+ and(
+ eq(generalEvaluationResponses.submissionId, submissionId),
+ eq(generalEvaluationResponses.isActive, true)
+ )
+ )
+ .then(result => (result[0]?.count || 0) > 0)
+
+ if (!hasResponses) {
+ console.log("ํ˜„์žฌ submission์— ์‘๋‹ต ์—†์Œ. ๊ฐ™์€ ๊ทธ๋ฃน์—์„œ ์‘๋‹ต์ด ์žˆ๋Š” submission ์ฐพ๊ธฐ...")
+
+ // ๊ฐ™์€ companyId, evaluationYear, evaluationRound๋ฅผ ๊ฐ€์ง„ ๋‹ค๋ฅธ submission ์ค‘ ์‘๋‹ต์ด ์žˆ๋Š” ๊ฒƒ์„ ์ฐพ์Œ
+ const siblingSubmissions = await db
+ .select({
+ id: evaluationSubmissions.id,
+ submissionId: evaluationSubmissions.submissionId,
+ })
+ .from(evaluationSubmissions)
+ .where(
+ and(
+ eq(evaluationSubmissions.companyId, submission.companyId),
+ eq(evaluationSubmissions.evaluationYear, submission.evaluationYear),
+ eq(evaluationSubmissions.evaluationRound, submission.evaluationRound || ""),
+ eq(evaluationSubmissions.isActive, true)
+ )
+ )
+
+ // ๊ฐ sibling submission์— ์‘๋‹ต์ด ์žˆ๋Š”์ง€ ํ™•์ธ
+ for (const sibling of siblingSubmissions) {
+ const siblingHasResponses = await db
+ .select({ count: sql<number>`COUNT(*)::int` })
+ .from(generalEvaluationResponses)
+ .where(
+ and(
+ eq(generalEvaluationResponses.submissionId, sibling.id),
+ eq(generalEvaluationResponses.isActive, true)
+ )
+ )
+ .then(result => (result[0]?.count || 0) > 0)
+
+ if (siblingHasResponses) {
+ console.log(`์‘๋‹ต์ด ์žˆ๋Š” submission ๋ฐœ๊ฒฌ: ${sibling.id}`)
+ submissionId = sibling.id
+ break
+ }
+ }
+ }
+
// 2. ์ผ๋ฐ˜ํ‰๊ฐ€ ํ•ญ๋ชฉ๊ณผ ์‘๋‹ต ์กฐํšŒ
const generalEvaluationsResult = await db
.select({
@@ -281,7 +334,7 @@ export async function getVendorSubmissionDetails(periodicEvaluationId: number):
fileSize: row.fileSize,
mimeType: row.mimeType,
uploadedBy: row.uploadedBy,
- createdAt: new Date(row.attachmentCreatedAt)
+ createdAt: row.attachmentCreatedAt ? new Date(row.attachmentCreatedAt) : new Date()
})
}
})
@@ -324,10 +377,10 @@ export async function getVendorSubmissionDetails(periodicEvaluationId: number):
// 5. ์ฒจ๋ถ€ํŒŒ์ผ ํ†ต๊ณ„ ๊ณ„์‚ฐ
const allAttachments = generalEvaluationsResult
- .filter(row => row.attachmentId)
+ .filter(row => row.attachmentId && row.fileSize !== null)
.map(row => ({
id: row.attachmentId,
- fileSize: row.fileSize
+ fileSize: row.fileSize || 0
}))
const attachmentStats = {
@@ -348,6 +401,11 @@ export async function getVendorSubmissionDetails(periodicEvaluationId: number):
reviewComments: submission.reviewComments,
averageEsgScore: submission.averageEsgScore ? Number(submission.averageEsgScore) : null,
+ // ์ง„ํ–‰๋ฅ  ํ†ต๊ณ„
+ totalGeneralItems: submission.totalGeneralItems,
+ completedGeneralItems: submission.completedGeneralItems,
+ totalEsgItems: submission.totalEsgItems,
+ completedEsgItems: submission.completedEsgItems,
vendor: {
id: submission.vendorId,
diff --git a/lib/vendor-evaluation-submit/table/esg-evaluation-form-sheet.tsx b/lib/vendor-evaluation-submit/table/esg-evaluation-form-sheet.tsx
index 0ebe1f8c..f3778f26 100644
--- a/lib/vendor-evaluation-submit/table/esg-evaluation-form-sheet.tsx
+++ b/lib/vendor-evaluation-submit/table/esg-evaluation-form-sheet.tsx
@@ -115,7 +115,8 @@ export function EsgEvaluationFormSheet({
additionalComments: item.response?.additionalComments || '',
})
- if (item.response?.selectedScore) {
+ // 0์ ๋„ ์œ ํšจํ•œ ์‘๋‹ต์ด๋ฏ€๋กœ undefined/null๋งŒ ์ฒดํฌ
+ if (item.response?.selectedScore !== undefined && item.response?.selectedScore !== null) {
scores[item.item.id] = item.response.selectedScore
}
})
@@ -468,7 +469,7 @@ const handleExportData = async () => {
<BarChart3Icon className="h-4 w-4" />
<span className="text-sm">
{evaluation.items.filter(item =>
- currentScores[item.item.id] >= 0
+ currentScores[item.item.id] !== undefined
).length}/{evaluation.items.length}
</span>
</div>
@@ -490,7 +491,7 @@ const handleExportData = async () => {
<CardHeader className="pb-3">
<CardTitle className="text-sm flex items-center justify-between">
<span>{item.item.evaluationItem}</span>
- {currentScores[item.item.id] > 0 && (
+ {currentScores[item.item.id] !== undefined && currentScores[item.item.id] !== null && (
<Badge variant="default" className="bg-green-100 text-green-800">
{currentScores[item.item.id]}์ 
</Badge>