glutamatt HF Staff commited on
Commit
58e063c
·
verified ·
1 Parent(s): 58c055f

show big current value

Browse files
Files changed (3) hide show
  1. index.html +6 -6
  2. src/components/charts.ts +53 -7
  3. src/style.css +21 -0
index.html CHANGED
@@ -4,7 +4,7 @@
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>Training Load Data Visualization</title>
8
  <link rel="icon" type="image/webp" href="/icon.webp">
9
  <link rel="stylesheet" href="/src/style.css">
10
  </head>
@@ -14,7 +14,7 @@
14
  <header>
15
  <div class="header-title">
16
  <img src="/icon.webp" alt="Training Load Icon" class="header-icon" />
17
- <h1>Training Load Data Visualization</h1>
18
  </div>
19
  <div class="header-controls">
20
  <div class="ftp-input-container">
@@ -67,25 +67,25 @@
67
 
68
  <section id="charts-section" class="hidden">
69
  <div class="chart-container">
70
- <h2>🗺️ Distance-based ACWR</h2>
71
  <div id="distance-target" class="target-info"></div>
72
  <canvas id="distance-chart"></canvas>
73
  </div>
74
 
75
  <div class="chart-container">
76
- <h2>⏱️ Duration-based ACWR</h2>
77
  <div id="duration-target" class="target-info"></div>
78
  <canvas id="duration-chart"></canvas>
79
  </div>
80
 
81
  <div class="chart-container">
82
- <h2>🥵 TSS-based ACWR</h2>
83
  <div id="tss-target" class="target-info"></div>
84
  <canvas id="tss-chart"></canvas>
85
  </div>
86
 
87
  <div class="chart-container">
88
- <h2>🔋 Calories-based ACWR</h2>
89
  <div id="calories-target" class="target-info"></div>
90
  <canvas id="calories-chart"></canvas>
91
  </div>
 
4
  <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>Training Load</title>
8
  <link rel="icon" type="image/webp" href="/icon.webp">
9
  <link rel="stylesheet" href="/src/style.css">
10
  </head>
 
14
  <header>
15
  <div class="header-title">
16
  <img src="/icon.webp" alt="Training Load Icon" class="header-icon" />
17
+ <h1>Training Load</h1>
18
  </div>
19
  <div class="header-controls">
20
  <div class="ftp-input-container">
 
67
 
68
  <section id="charts-section" class="hidden">
69
  <div class="chart-container">
70
+ <h2>🗺️ Distance-based ACWR <span id="distance-acwr-display" class="acwr-display"></span></h2>
71
  <div id="distance-target" class="target-info"></div>
72
  <canvas id="distance-chart"></canvas>
73
  </div>
74
 
75
  <div class="chart-container">
76
+ <h2>⏱️ Duration-based ACWR <span id="duration-acwr-display" class="acwr-display"></span></h2>
77
  <div id="duration-target" class="target-info"></div>
78
  <canvas id="duration-chart"></canvas>
79
  </div>
80
 
81
  <div class="chart-container">
82
+ <h2>🥵 TSS-based ACWR <span id="tss-acwr-display" class="acwr-display"></span></h2>
83
  <div id="tss-target" class="target-info"></div>
84
  <canvas id="tss-chart"></canvas>
85
  </div>
86
 
87
  <div class="chart-container">
88
+ <h2>🔋 Calories-based ACWR <span id="calories-acwr-display" class="acwr-display"></span></h2>
89
  <div id="calories-target" class="target-info"></div>
90
  <canvas id="calories-chart"></canvas>
91
  </div>
src/components/charts.ts CHANGED
@@ -10,7 +10,7 @@ let tssChart: Chart | null = null;
10
  let caloriesChart: Chart | null = null;
11
 
12
  // ACWR color zones - gradual HSV-based color
13
- function getACWRColor(value: number | null): string {
14
  if (value === null || value === undefined) return 'rgba(148, 163, 184, 0.3)';
15
 
16
  // ACWR thresholds for color zones
@@ -49,7 +49,37 @@ function getACWRColor(value: number | null): string {
49
  hue = HUE_RED;
50
  }
51
 
52
- return `hsla(${hue}, 85%, 65%, 0.99)`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  }
54
 
55
  // Plugin to draw gradient-colored ACWR line
@@ -207,13 +237,13 @@ const mondayGridPlugin = {
207
  const ctx = chart.ctx;
208
  const chartArea = chart.chartArea;
209
  const xScale = chart.scales['x'];
210
-
211
  if (!xScale || !chartArea) return;
212
-
213
  ctx.save();
214
  ctx.strokeStyle = 'rgba(148, 163, 184, 0.3)';
215
  ctx.lineWidth = 1;
216
-
217
  // Draw a line for each Monday in the data
218
  const labels = chart.data.labels || [];
219
  labels.forEach((label, index) => {
@@ -221,7 +251,7 @@ const mondayGridPlugin = {
221
  const [year, month, day] = label.split('-').map(Number);
222
  const date = new Date(year, month - 1, day);
223
  const dayOfWeek = date.getDay();
224
-
225
  // If it's Monday, draw a vertical line
226
  if (dayOfWeek === 1) {
227
  const x = xScale.getPixelForValue(index);
@@ -234,7 +264,7 @@ const mondayGridPlugin = {
234
  }
235
  }
236
  });
237
-
238
  ctx.restore();
239
  },
240
  };
@@ -672,6 +702,10 @@ export function createDistanceChart(data: MetricACWRData): void {
672
  'rgba(234, 179, 8, 0.8)'
673
  );
674
  updateTargetInfo('distance-target', data.targetTomorrowValue, 'km', data.targetACWR, data.restTomorrowACWR);
 
 
 
 
675
  }
676
 
677
  export function createDurationChart(data: MetricACWRData): void {
@@ -686,6 +720,10 @@ export function createDurationChart(data: MetricACWRData): void {
686
  'rgba(234, 179, 8, 0.8)'
687
  );
688
  updateTargetInfo('duration-target', data.targetTomorrowValue, 'minutes', data.targetACWR, data.restTomorrowACWR);
 
 
 
 
689
  }
690
 
691
  export function createTSSChart(data: MetricACWRData): void {
@@ -700,6 +738,10 @@ export function createTSSChart(data: MetricACWRData): void {
700
  'rgba(234, 179, 8, 0.8)'
701
  );
702
  updateTargetInfo('tss-target', data.targetTomorrowValue, 'TSS', data.targetACWR, data.restTomorrowACWR);
 
 
 
 
703
  }
704
 
705
  export function createCaloriesChart(data: MetricACWRData): void {
@@ -714,6 +756,10 @@ export function createCaloriesChart(data: MetricACWRData): void {
714
  'rgba(234, 179, 8, 0.8)'
715
  );
716
  updateTargetInfo('calories-target', data.targetTomorrowValue, 'kcal', data.targetACWR, data.restTomorrowACWR);
 
 
 
 
717
  }
718
 
719
  function updateTargetInfo(elementId: string, targetValue: number | null | undefined, unit: string, targetACWR: number | undefined, restTomorrowACWR: number | null | undefined): void {
 
10
  let caloriesChart: Chart | null = null;
11
 
12
  // ACWR color zones - gradual HSV-based color
13
+ function getACWRColor(value: number | null, alpha: number = .99): string {
14
  if (value === null || value === undefined) return 'rgba(148, 163, 184, 0.3)';
15
 
16
  // ACWR thresholds for color zones
 
49
  hue = HUE_RED;
50
  }
51
 
52
+ return `hsla(${hue}, 85%, 65%, ${alpha})`;
53
+ }
54
+
55
+ // Get ACWR range name
56
+ function getACWRRangeName(value: number | null): string {
57
+ if (value === null || value === undefined) return '';
58
+
59
+ if (value < 0.8) return 'Detraining risk';
60
+ if (value <= 1.3) return 'Optimal';
61
+ if (value <= 1.5) return 'Warning';
62
+ return 'Injury risk';
63
+ }
64
+
65
+ // Update ACWR display in chart title
66
+ function updateACWRDisplay(elementId: string, acwrValue: number | null): void {
67
+ const element = document.getElementById(elementId);
68
+ if (!element) return;
69
+
70
+ if (acwrValue === null || acwrValue === undefined) {
71
+ element.innerHTML = '';
72
+ return;
73
+ }
74
+
75
+ const color = getACWRColor(acwrValue);
76
+ const bgColor = getACWRColor(acwrValue, .12);
77
+ const rangeName = getACWRRangeName(acwrValue);
78
+
79
+ element.innerHTML = `
80
+ <span style="color: ${color}; font-weight: bold; background-color: ${bgColor}">${acwrValue.toFixed(2)}</span>
81
+ <span style="font-size: 0.7em; opacity: 0.8;">(${rangeName})</span>
82
+ `;
83
  }
84
 
85
  // Plugin to draw gradient-colored ACWR line
 
237
  const ctx = chart.ctx;
238
  const chartArea = chart.chartArea;
239
  const xScale = chart.scales['x'];
240
+
241
  if (!xScale || !chartArea) return;
242
+
243
  ctx.save();
244
  ctx.strokeStyle = 'rgba(148, 163, 184, 0.3)';
245
  ctx.lineWidth = 1;
246
+
247
  // Draw a line for each Monday in the data
248
  const labels = chart.data.labels || [];
249
  labels.forEach((label, index) => {
 
251
  const [year, month, day] = label.split('-').map(Number);
252
  const date = new Date(year, month - 1, day);
253
  const dayOfWeek = date.getDay();
254
+
255
  // If it's Monday, draw a vertical line
256
  if (dayOfWeek === 1) {
257
  const x = xScale.getPixelForValue(index);
 
264
  }
265
  }
266
  });
267
+
268
  ctx.restore();
269
  },
270
  };
 
702
  'rgba(234, 179, 8, 0.8)'
703
  );
704
  updateTargetInfo('distance-target', data.targetTomorrowValue, 'km', data.targetACWR, data.restTomorrowACWR);
705
+
706
+ // Update ACWR display in title
707
+ const currentACWR = data.acwr.length > 0 ? data.acwr[data.acwr.length - 1] : null;
708
+ updateACWRDisplay('distance-acwr-display', currentACWR);
709
  }
710
 
711
  export function createDurationChart(data: MetricACWRData): void {
 
720
  'rgba(234, 179, 8, 0.8)'
721
  );
722
  updateTargetInfo('duration-target', data.targetTomorrowValue, 'minutes', data.targetACWR, data.restTomorrowACWR);
723
+
724
+ // Update ACWR display in title
725
+ const currentACWR = data.acwr.length > 0 ? data.acwr[data.acwr.length - 1] : null;
726
+ updateACWRDisplay('duration-acwr-display', currentACWR);
727
  }
728
 
729
  export function createTSSChart(data: MetricACWRData): void {
 
738
  'rgba(234, 179, 8, 0.8)'
739
  );
740
  updateTargetInfo('tss-target', data.targetTomorrowValue, 'TSS', data.targetACWR, data.restTomorrowACWR);
741
+
742
+ // Update ACWR display in title
743
+ const currentACWR = data.acwr.length > 0 ? data.acwr[data.acwr.length - 1] : null;
744
+ updateACWRDisplay('tss-acwr-display', currentACWR);
745
  }
746
 
747
  export function createCaloriesChart(data: MetricACWRData): void {
 
756
  'rgba(234, 179, 8, 0.8)'
757
  );
758
  updateTargetInfo('calories-target', data.targetTomorrowValue, 'kcal', data.targetACWR, data.restTomorrowACWR);
759
+
760
+ // Update ACWR display in title
761
+ const currentACWR = data.acwr.length > 0 ? data.acwr[data.acwr.length - 1] : null;
762
+ updateACWRDisplay('calories-acwr-display', currentACWR);
763
  }
764
 
765
  function updateTargetInfo(elementId: string, targetValue: number | null | undefined, unit: string, targetACWR: number | undefined, restTomorrowACWR: number | null | undefined): void {
src/style.css CHANGED
@@ -473,6 +473,27 @@ h2 {
473
  .chart-container h2 {
474
  color: #fbfefe;
475
  margin-bottom: 1rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
  }
477
 
478
  .chart-container canvas {
 
473
  .chart-container h2 {
474
  color: #fbfefe;
475
  margin-bottom: 1rem;
476
+ display: flex;
477
+ align-items: center;
478
+ gap: 0.5rem;
479
+ }
480
+
481
+ .acwr-display {
482
+ font-size: 0.85em;
483
+ font-weight: normal;
484
+ display: inline-flex;
485
+ align-items: center;
486
+ gap: 0.3rem;
487
+ }
488
+
489
+ .acwr-display span:first-child {
490
+ padding: 0.2rem 0.6rem;
491
+ border-radius: 6px;
492
+ border: 2px solid currentColor;
493
+ background: rgba(0, 0, 0, 0.2);
494
+ font-weight: 600;
495
+ letter-spacing: 0.02em;
496
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
497
  }
498
 
499
  .chart-container canvas {