Files
egbim_homepage/egbim/skin/sales/html/main_sub/individual_chart.php
T
2026-07-23 16:15:57 +09:00

276 lines
11 KiB
PHP

<section class="card individual">
<div class="chart-wrap">
<div class="card-header">
<h3>연간 개인별 판매 실적<span class="tail"></span></h3>
<div class="legend">
<span>
<span class="legend-dot line-area01"></span>
권혁진 수석
<span class="badge-individual01"></span>
</span>
<span>
<span class="legend-dot line-area02"></span>
염승호 수석
<span class="badge-individual02"></span>
</span>
<span class="unit">(단위:copy) </span>
</div>
</div>
<div class="chart-body">
<!-- <div class="badge-box">
</div> -->
<canvas id="individual_chart"></canvas>
</div>
</div>
<div class="card-body">
<div class="person-box">
<div class="person-name"><span>권혁진</span> <small>수석</small></div>
<div class="stat-row individual01">
<div class="label-box">
<span class="label">누적 판매수</span>
<!-- <div class="change-down">전월대비 <em>27</em></div> -->
</div>
<div>
<span class="big-num">162</span><span class="unit copy">copy</span>
</div>
</div>
<div class="stat-row">
<div class="label-box">
<span class="label">목표 달성률</span>
<!-- <div class="change-up">전월대비 <em>15p</em></div> -->
</div>
<div><span class="big-num">81</span><span class="unit">%</span></div>
</div>
</div>
<div class="person-box">
<div class="person-name">
<span>염승호</span>
<small>수석</small>
</div>
<div class="stat-row individual02">
<div class="label-box">
<span class="label">누적 판매수</span>
<!-- <div class="change-up">전월대비 <em>105</em></div> -->
</div>
<div>
<span class="big-num">508</span><span class="unit copy">copy</span>
</div>
</div>
<div class="stat-row">
<div class="label-box">
<span class="label">목표 달성률</span>
<!-- <div class="change-up">전월대비 <em>63p</em></div> -->
</div>
<div><span class="big-num">169.3</span><span class="unit">%</span></div>
</div>
</div>
</div>
</section>
<script>
(() => {
let INDIVIDUAL_DATA = null;
// -----------------------------
// 1) UI 숫자 반영
// -----------------------------
function updateIndividualUI() {
if (!INDIVIDUAL_DATA || INDIVIDUAL_DATA.length < 2) return;
const p1 = INDIVIDUAL_DATA[0];
const p2 = INDIVIDUAL_DATA[1];
// 이름
document.querySelector('.person-box:nth-child(1) .person-name span').textContent = p1.name;
document.querySelector('.person-box:nth-child(2) .person-name span').textContent = p2.name;
// 누적 판매수
document.querySelector('.individual01 .big-num').textContent = p1.total;
document.querySelector('.individual02 .big-num').textContent = p2.total;
// 달성률
document.querySelector('.person-box:nth-child(1) .stat-row:nth-child(3) .big-num').textContent = p1.rate;
document.querySelector('.person-box:nth-child(2) .stat-row:nth-child(3) .big-num').textContent = p2.rate;
// 개인별 목표(배지)
document.querySelector('.badge-individual01').textContent = `목표 ${p1.target_qty}`;
document.querySelector('.badge-individual02').textContent = `목표 ${p2.target_qty}`;
}
// -----------------------------
// 2) 차트 생성
// -----------------------------
const labels12 = Array.from({ length: 12 }, (_, i) => String(i + 1));
const currentMonth = new Date().getMonth() + 1;
const currentMonthStr = String(currentMonth);
let individualInstance = null;
function createIndividualChart() {
const ctx = document.getElementById("individual_chart");
if (!ctx || !INDIVIDUAL_DATA || INDIVIDUAL_DATA.length < 2) return;
if (individualInstance) individualInstance.destroy();
individualInstance = new Chart(ctx, {
type: "line",
data: {
labels: labels12,
datasets: [
{
label: "권혁진 수석",
data: [...INDIVIDUAL_DATA[0].monthly, null],
fill: true,
backgroundColor: (ctx) => {
const a = ctx.chart.chartArea;
return !a
? cssVar("--bg-chart-area01")
: safeGrad(
ctx.chart.ctx,
a,
[
{ stop: 0, color: cssVar("--bg-chart-area01") },
{ stop: 1, color: "rgba(255, 255, 255, 0.3)" },
],
true
);
},
borderColor: cssVar("--bg-chart-linearea01"),
borderWidth: 1,
pointRadius: 4,
tension: 0,
},
{
label: "염승호 수석",
data: [...INDIVIDUAL_DATA[1].monthly, null],
fill: true,
backgroundColor: (ctx) => {
const a = ctx.chart.chartArea;
return !a
? cssVar("--bg-chart-area02")
: safeGrad(
ctx.chart.ctx,
a,
[
{ stop: 0, color: cssVar("--bg-chart-area02") },
{ stop: 1, color: "rgba(255, 255, 255, 0.3)" },
],
true
);
},
borderColor: cssVar("--bg-chart-linearea02"),
borderWidth: 1,
pointRadius: 4,
tension: 0,
},
],
},
options: {
responsive: true,
maintainAspectRatio: false,
layout: { padding: { top: 16 } },
plugins: {
legend: { display: false },
tooltip: { enabled: false },
},
scales: {
y: {
afterBuildTicks: (scale) => {
const maxValue = scale.chart.data.datasets
.flatMap((d) => d.data)
.filter((n) => typeof n === "number");
const realMax = Math.max(...maxValue);
const rawMax = realMax + 50;
const targetMax = Math.ceil(rawMax / 100) * 100;
const mid = Math.round(targetMax / 2);
scale.max = targetMax;
scale.min = 0;
scale.ticks = [
{ value: 0 },
{ value: mid },
{ value: targetMax },
];
scale._midIndex = 1;
},
grid: {
drawTicks: false,
color: (ctx) =>
ctx.index === ctx.scale._midIndex
? cssVar("--grid-border-dashed")
: "rgba(0,0,0,0)",
lineWidth: (ctx) =>
ctx.index === ctx.scale._midIndex ? 1 : 0,
},
border: { display: false },
ticks: {
padding: 4,
color: cssVar("--chart-base"),
},
},
x: {
offset: true,
grid: {
drawTicks: false,
display: true,
color: cssVar("--grid-border") || "#e0e0e0",
drawOnChartArea: true,
z: -1,
},
ticks: {
color: (ctx) =>
ctx.tick.label === currentMonthStr
? cssVar("--chart-point")
: cssVar("--chart-base"),
font: (ctx) =>
ctx.tick.label === currentMonthStr
? { weight: "bold", size: 18 }
: { weight: "normal", size: 18 },
padding: 10,
callback: (_, index) => labels12[index],
},
border: {
display: true,
dash: [3, 3],
color: cssVar("--grid-border-dashed"),
width: 1,
},
},
},
},
plugins: [
highlightXFactory(currentMonthStr),
hideNullPointsPlugin,
createPointGradientPluginWithBorder(),
],
});
}
// -----------------------------
// 3) 개인별 데이터 로딩 함수 (🔥 핵심)
// -----------------------------
function loadIndividualDashboardData() {
return fetch(`/egbim/bbs/sales_dashboard.php?year=${window.APP_YEAR}&_=${Date.now()}`)
.then(r => r.json())
.then(json => {
INDIVIDUAL_DATA = json.individual || [];
updateIndividualUI();
createIndividualChart();
});
}
// -----------------------------
// 4) 전역 등록 + 최초 실행
// -----------------------------
window.loadIndividualDashboardData = loadIndividualDashboardData;
loadIndividualDashboardData();
})();
</script>