|
502 | 502 | }) |
503 | 503 | .filter(Boolean); |
504 | 504 | } |
| 505 | + function formatHoursAdaptive(hours) { |
| 506 | + if (!Number.isFinite(hours)) return '-'; |
| 507 | + const H_PER_DAY = 24; |
| 508 | + const D_PER_MONTH = 30; |
| 509 | + |
| 510 | + const abs = Math.abs(hours); |
| 511 | + if (abs >= H_PER_DAY * D_PER_MONTH) { |
| 512 | + const months = hours / (H_PER_DAY * D_PER_MONTH); // 30일=1개월 |
| 513 | + const sig = Math.abs(months) >= 10 ? 0 : 1; |
| 514 | + return `${months.toFixed(sig)}개월`; |
| 515 | + } |
| 516 | + if (abs >= H_PER_DAY) { |
| 517 | + const days = hours / H_PER_DAY; |
| 518 | + const sig = Math.abs(days) >= 10 ? 0 : 1; |
| 519 | + return `${days.toFixed(sig)}일`; |
| 520 | + } |
| 521 | + return `${hours.toFixed(2)}시간`; |
| 522 | + } |
| 523 | + |
505 | 524 | function formatValue(v, unit){ |
506 | 525 | if (!Number.isFinite(v)) return '-'; |
507 | 526 | if (unit==='RATIO') return `${v.toFixed(1)}%`; |
508 | | - if (unit==='HOURS') return `${v.toFixed(2)}시간`; |
| 527 | + if (unit==='HOURS') return formatHoursAdaptive(v); |
509 | 528 | return Math.round(v).toLocaleString(); |
510 | 529 | } |
| 530 | + |
511 | 531 | function normalizeValue(num, unit){ |
512 | 532 | const v = typeof num === 'string' ? Number(num) : Number(num); |
513 | 533 | if (!Number.isFinite(v)) return null; |
|
550 | 570 | const base = ds.label || ''; |
551 | 571 | const v = ctx.parsed.y; |
552 | 572 |
|
553 | | - if (ds._boxplot && ds._boxIndex) { |
| 573 | + if (ds._boxIndex) { |
554 | 574 | const x = ctx.raw?.x instanceof Date ? ctx.raw.x.getTime() : new Date(ctx.raw?.x).getTime(); |
555 | 575 | const st = ds._boxIndex.get(String(x)); |
556 | 576 | if (st) { |
557 | 577 | const mean = (typeof v === 'number') ? v : null; |
558 | 578 | const lo = (mean!=null && Number.isFinite(st.std)) ? (mean - st.std) : null; |
559 | 579 | const hi = (mean!=null && Number.isFinite(st.std)) ? (mean + st.std) : null; |
560 | | - const fmt = (n)=>formatValue(n,'HOURS'); |
| 580 | + const fmt = (n)=>formatValue(n,'HOURS'); // ← 여기서 시간/일/개월 자동 환산 |
561 | 581 | if (mean!=null && lo!=null && hi!=null) { |
| 582 | + // 기존 문구 스타일 유지 |
562 | 583 | return `${base}: 평균적으로 ${fmt(lo)} ~ ${fmt(hi)} 정도 (최소: ${fmt(st.min)}, 최대: ${fmt(st.max)})`; |
563 | 584 | } |
564 | 585 | } |
|
684 | 705 | _id: id, |
685 | 706 | label: (opt.label || '동정 해결 시간'), |
686 | 707 | data: meanPoints, |
687 | | - parsing: { xAxisKey: 'x', yAxisKey: 'y' }, // ✅ 명시 파싱 |
| 708 | + parsing: { xAxisKey: 'x', yAxisKey: 'y' }, |
688 | 709 | borderColor: color, |
689 | 710 | backgroundColor: color, |
690 | 711 | tension: 0.25, |
|
694 | 715 | spanGaps: true, |
695 | 716 | yAxisID: 'hours', |
696 | 717 | _saUnit: 'HOURS', |
697 | | - _boxplot: merged, // { _time: Date(KST 00:00), min,max,mean,std } |
698 | | - _boxIndex: indexMap |
| 718 | + _boxIndex: indexMap // ← 툴팁 계산에만 사용 |
699 | 719 | }; |
| 720 | + |
700 | 721 | p.chart.data.datasets.push(ds); |
701 | 722 | p.datasets.add(id); |
702 | 723 | groupSet.add(id); |
|
0 commit comments