From 97cecb8b50522a264fc484c370c7650632445ee6 Mon Sep 17 00:00:00 2001 From: JooWangi Date: Mon, 15 Jun 2026 13:12:07 +0900 Subject: [PATCH] =?UTF-8?q?feat:=205=EB=93=B1=EA=B8=89=20PC=20=EB=B6=84?= =?UTF-8?q?=EB=A5=98=20=EC=B2=B4=EA=B3=84=20=EB=8F=84=EC=9E=85=20(?= =?UTF-8?q?=EA=B5=90=EC=B2=B4=20=EB=8C=80=EC=83=81=20PC=20=EB=93=B1?= =?UTF-8?q?=EA=B8=89=20=EC=8B=A0=EC=84=A4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/utils.ts | 5 ++-- src/views/Dashboard/HwDashboard.ts | 39 +++++++++++++++++++----------- src/views/List/PcListView.ts | 5 ++-- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/core/utils.ts b/src/core/utils.ts index 8993e3c..27dcab0 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -288,11 +288,12 @@ export function calculatePcScoreDeductive(cpu: string, ram: string, gpu: string, /** * 성능 점수 기준 등급 뱃지 메타 정보 가져오기 */ -export function getPcGrade(score: number): { name: string; class: string; color: string } { +export function getPcGrade(score: number, isWin11Incompatible?: boolean): { name: string; class: string; color: string } { if (score >= 85) return { name: '최상급', class: 'b-purple', color: '#7C3AED' }; if (score >= 70) return { name: '상급', class: 'b-primary', color: '#4F46E5' }; if (score >= 40) return { name: '중급', class: 'b-green', color: '#10B981' }; - return { name: '보급', class: 'b-yellow', color: '#F59E0B' }; + if (score >= 20 && !isWin11Incompatible) return { name: '보급', class: 'b-yellow', color: '#F59E0B' }; + return { name: '교체 대상', class: 'badge-danger', color: '#EF4444' }; } /** diff --git a/src/views/Dashboard/HwDashboard.ts b/src/views/Dashboard/HwDashboard.ts index b8208a5..45507d0 100644 --- a/src/views/Dashboard/HwDashboard.ts +++ b/src/views/Dashboard/HwDashboard.ts @@ -182,9 +182,13 @@ export function renderHwDashboard(container: HTMLElement) { 중급
- + 보급
+
+ + 교체 대상 +
@@ -285,7 +289,8 @@ function updateDashboardData(pcs: any[], selectedDept: string) { premium: { total: 0, active: 0, stock: 0, under: 0, pcs: [] as any[], activePcs: [] as any[], stockPcs: [] as any[], underPcs: [] as any[] }, high: { total: 0, active: 0, stock: 0, under: 0, pcs: [] as any[], activePcs: [] as any[], stockPcs: [] as any[], underPcs: [] as any[] }, normal: { total: 0, active: 0, stock: 0, under: 0, pcs: [] as any[], activePcs: [] as any[], stockPcs: [] as any[], underPcs: [] as any[] }, - entry: { total: 0, active: 0, stock: 0, under: 0, pcs: [] as any[], activePcs: [] as any[], stockPcs: [] as any[], underPcs: [] as any[] } + entry: { total: 0, active: 0, stock: 0, under: 0, pcs: [] as any[], activePcs: [] as any[], stockPcs: [] as any[], underPcs: [] as any[] }, + replace: { total: 0, active: 0, stock: 0, under: 0, pcs: [] as any[], activePcs: [] as any[], stockPcs: [] as any[], underPcs: [] as any[] } }; let scoreSum = 0; @@ -298,6 +303,7 @@ function updateDashboardData(pcs: any[], selectedDept: string) { const score = p._pc_score; scoreSum += score; const stockYn = isStock(p); + const win11Incompatible = isWindows11Incompatible(p.cpu, p.ram); let target: typeof matrix.premium; if (score >= 85) { @@ -306,8 +312,10 @@ function updateDashboardData(pcs: any[], selectedDept: string) { target = matrix.high; } else if (score >= 40) { target = matrix.normal; - } else { + } else if (score >= 20 && !win11Incompatible) { target = matrix.entry; + } else { + target = matrix.replace; } target.pcs.push(p); @@ -324,7 +332,6 @@ function updateDashboardData(pcs: any[], selectedDept: string) { const job = p[ASSET_SCHEMA.USER_POSITION.key] || '미분류'; const avg = jobScores[job]?.avg || 0; - const win11Incompatible = isWindows11Incompatible(p.cpu, p.ram); let isUnder = false; if (avg > 0 && job !== '재고PC') { @@ -392,14 +399,15 @@ function updateDashboardData(pcs: any[], selectedDept: string) { }; const totalPcs = filtered.length; - const totalActive = matrix.premium.active + matrix.high.active + matrix.normal.active + matrix.entry.active; - const totalStock = matrix.premium.stock + matrix.high.stock + matrix.normal.stock + matrix.entry.stock; + const totalActive = matrix.premium.active + matrix.high.active + matrix.normal.active + matrix.entry.active + matrix.replace.active; + const totalStock = matrix.premium.stock + matrix.high.stock + matrix.normal.stock + matrix.entry.stock + matrix.replace.stock; const premiumShortage = Math.max(0, matrix.premium.under - matrix.premium.stock); const highShortage = Math.max(0, matrix.high.under - matrix.high.stock); const normalShortage = Math.max(0, matrix.normal.under - matrix.normal.stock); const entryShortage = Math.max(0, matrix.entry.under - matrix.entry.stock); - const totalShortage = premiumShortage + highShortage + normalShortage + entryShortage; + const replaceShortage = Math.max(0, matrix.replace.under - matrix.replace.stock); + const totalShortage = premiumShortage + highShortage + normalShortage + entryShortage + replaceShortage; const cellStyleHeader = `padding: 14px 12px; text-align: center; font-weight: 800; cursor: pointer; transition: background 0.2s; background: #F8FAFC; font-size: 1.25rem;`; const hoverEventsHeader = `onmouseover="this.style.background='#EEF2F6'" onmouseout="this.style.background='#F8FAFC'"`; @@ -408,7 +416,8 @@ function updateDashboardData(pcs: any[], selectedDept: string) { ${renderMatrixRow('premium', '최상급 PC (85점 이상)', '#11302B')} ${renderMatrixRow('high', '상급 PC (70점 ~ 85점)', '#1E8E7C')} ${renderMatrixRow('normal', '중급 PC (40점 ~ 70점)', '#10B981')} - ${renderMatrixRow('entry', '보급 PC (40점 미만)', '#64748B')} + ${renderMatrixRow('entry', '보급 PC (20점 ~ 40점 & Win11 가능)', '#F59E0B')} + ${renderMatrixRow('replace', '교체 대상 PC (20점 미만 또는 Win11 불가)', '#EF4444')} 합계 (Total) ${totalPcs}대 (100%) @@ -432,6 +441,7 @@ function updateDashboardData(pcs: any[], selectedDept: string) { if (g === 'high') return '상급 PC'; if (g === 'normal') return '중급 PC'; if (g === 'entry') return '보급 PC'; + if (g === 'replace') return '교체 대상 PC'; return '전체 PC'; }; @@ -588,7 +598,7 @@ function updateDashboardData(pcs: any[], selectedDept: string) { // 10. 차트들 렌더링 호출 renderChart(activeJobs, underData, normalData, overData, filtered); - renderDonutChart(matrix.premium.total, matrix.high.total, matrix.normal.total, matrix.entry.total); + renderDonutChart(matrix.premium.total, matrix.high.total, matrix.normal.total, matrix.entry.total, matrix.replace.total); // 전역 상태 등록 state.activeCharts = [jobChartInstance, donutChartInstance]; @@ -837,7 +847,7 @@ function renderChart(labels: string[], underData: number[], normalData: number[] /** * 실시간 사양 적정률 원형 도넛 그래프 (Active Spec Rate) */ -function renderDonutChart(premium: number, high: number, normal: number, entry: number) { +function renderDonutChart(premium: number, high: number, normal: number, entry: number, replace: number) { const ctx = document.getElementById('chart-overall-donut') as HTMLCanvasElement; if (!ctx || typeof Chart === 'undefined') return; @@ -846,19 +856,20 @@ function renderDonutChart(premium: number, high: number, normal: number, entry: donutChartInstance = null; } - const total = premium + high + normal + entry; + const total = premium + high + normal + entry + replace; donutChartInstance = new Chart(ctx, { type: 'doughnut', data: { - labels: ['최상급', '상급', '중급', '보급'], + labels: ['최상급', '상급', '중급', '보급', '교체 대상'], datasets: [{ - data: [premium, high, normal, entry], + data: [premium, high, normal, entry, replace], backgroundColor: [ '#11302B', // premium (Hanmac Dark Green) '#1E8E7C', // high (Hanmac Teal) '#10B981', // normal (Hanmac Mint) - '#94A3B8' // entry (Slate Gray) + '#F59E0B', // entry (Yellow-Orange) + '#EF4444' // replace (Red) ], borderColor: '#ffffff', borderWidth: 2 diff --git a/src/views/List/PcListView.ts b/src/views/List/PcListView.ts index 02a2e69..2409af1 100644 --- a/src/views/List/PcListView.ts +++ b/src/views/List/PcListView.ts @@ -1,6 +1,6 @@ import { state } from '../../core/state'; import { openHwModal } from '../../components/Modal/HWModal'; -import { sortAssets, formatInline, calculatePcScoreDeductive, getPcGrade } from '../../core/utils'; +import { sortAssets, formatInline, calculatePcScoreDeductive, getPcGrade, isWindows11Incompatible } from '../../core/utils'; import { ASSET_SCHEMA } from '../../core/schema'; import { createListView } from './ListFactory'; import { SortState } from '../../core/tableHandler'; @@ -104,7 +104,8 @@ export function renderPcList(container: HTMLElement) { width: '8%', render: a => { const score = a._pc_score !== undefined ? a._pc_score : calculatePcScoreDeductive(a[ASSET_SCHEMA.CPU.key], a[ASSET_SCHEMA.RAM.key], a[ASSET_SCHEMA.GPU.key], a.purchase_date); - const grade = getPcGrade(score); + const isWin11Incompatible = isWindows11Incompatible(a[ASSET_SCHEMA.CPU.key], a[ASSET_SCHEMA.RAM.key]); + const grade = getPcGrade(score, isWin11Incompatible); return `${grade.name}`; } }