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}`;
}
}