diff --git a/assets/css/app.css b/assets/css/app.css
index 9ec37fc..f555f29 100644
--- a/assets/css/app.css
+++ b/assets/css/app.css
@@ -404,6 +404,60 @@ button {
overflow-x: auto;
}
+.calendar-detail {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 1rem;
+ margin-bottom: 1rem;
+ padding: 0.9rem 1rem;
+ border-radius: 18px;
+ background: rgba(255, 255, 255, 0.08);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.calendar-detail__meta {
+ display: grid;
+ gap: 0.18rem;
+}
+
+.calendar-detail__eyebrow {
+ color: rgba(239, 247, 255, 0.58);
+ font-size: 0.78rem;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.calendar-detail__meta strong {
+ font-size: 1.05rem;
+ line-height: 1.2;
+}
+
+.calendar-detail__label,
+.calendar-detail__subtle {
+ color: var(--muted);
+}
+
+.calendar-detail__score {
+ display: grid;
+ justify-items: end;
+ line-height: 1;
+}
+
+.calendar-detail__score span {
+ font-size: 1.5rem;
+ font-weight: 800;
+}
+
+.calendar-detail__score small {
+ color: var(--muted);
+ margin-top: 0.2rem;
+}
+
+.calendar-detail__link {
+ white-space: nowrap;
+}
+
.calendar-legend {
display: flex;
justify-content: flex-end;
@@ -479,6 +533,12 @@ button {
stroke-width: 1.1;
}
+.calendar-cell--selected {
+ stroke: rgba(255, 255, 255, 0.9);
+ stroke-width: 1.2;
+ filter: brightness(1.08);
+}
+
.line-chart {
min-height: 10.25rem;
}
@@ -1182,6 +1242,15 @@ input[type="range"] {
align-items: flex-start;
}
+ .calendar-detail {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .calendar-detail__score {
+ justify-items: start;
+ }
+
.line-chart {
min-height: 9.5rem;
}
diff --git a/assets/js/app.js b/assets/js/app.js
index da571eb..61bc90a 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -347,6 +347,7 @@
}
const seriesName = container.dataset.series || "";
+ const invertScale = seriesName === "stress";
const values = items.map(item => Number(item.value));
const width = 760;
const height = 196;
@@ -377,7 +378,8 @@
const only = items[0];
const chartTop = padding.top;
const chartBottom = height - padding.bottom;
- const ratio = (Number(only.value) - minValue) / Math.max(maxValue - minValue, 1);
+ const baseRatio = (Number(only.value) - minValue) / Math.max(maxValue - minValue, 1);
+ const ratio = invertScale ? (1 - baseRatio) : baseRatio;
const cx = width / 2;
const cy = chartTop + ((1 - ratio) * (chartBottom - chartTop));
@@ -398,7 +400,8 @@
const step = items.length > 1 ? (width - padding.left - padding.right) / (items.length - 1) : 0;
const points = items.map((item, index) => {
- const ratio = (Number(item.value) - minValue) / Math.max(maxValue - minValue, 1);
+ const baseRatio = (Number(item.value) - minValue) / Math.max(maxValue - minValue, 1);
+ const ratio = invertScale ? (1 - baseRatio) : baseRatio;
return {
x: padding.left + (index * step),
y: padding.top + ((1 - ratio) * (height - padding.top - padding.bottom)),
@@ -516,10 +519,49 @@
return `rgba(112, 240, 182, ${0.38 + (ratio * 0.52)})`;
}
+ function calendarRangeConfig() {
+ const viewport = window.innerWidth || container.clientWidth || 1200;
+
+ if (viewport <= 640) {
+ return {
+ rangeDays: 119,
+ label: "letzte 4 Monate",
+ cellSize: 14,
+ columnGap: 6,
+ rowGap: 6,
+ xOffset: 36,
+ yOffset: 24,
+ };
+ }
+
+ if (viewport <= 980) {
+ return {
+ rangeDays: 181,
+ label: "letzte 6 Monate",
+ cellSize: 13,
+ columnGap: 5,
+ rowGap: 5,
+ xOffset: 36,
+ yOffset: 24,
+ };
+ }
+
+ return {
+ rangeDays: 364,
+ label: "letzte 12 Monate",
+ cellSize: 12,
+ columnGap: 5,
+ rowGap: 5,
+ xOffset: 34,
+ yOffset: 22,
+ };
+ }
+
const map = new Map(items.map(item => [item.date, item]));
+ const config = calendarRangeConfig();
const end = new Date();
const start = new Date(end);
- start.setDate(end.getDate() - 364);
+ start.setDate(end.getDate() - config.rangeDays);
const gridStart = new Date(start);
const startWeekday = gridStart.getDay();
@@ -547,11 +589,11 @@
}
const totalWeeks = Math.floor((days.length - 1) / 7) + 1;
- const cellSize = 12;
- const baseCellGap = 5;
- const verticalGap = 5;
- const xOffset = 34;
- const yOffset = 22;
+ const cellSize = config.cellSize;
+ const baseCellGap = config.columnGap;
+ const verticalGap = config.rowGap;
+ const xOffset = config.xOffset;
+ const yOffset = config.yOffset;
const gridHeight = (7 * cellSize) + (6 * verticalGap);
const height = yOffset + gridHeight + 8;
const rightPadding = 4;
@@ -563,6 +605,9 @@
: 0;
const monthLabels = [];
let lastMonth = -1;
+ const visibleEntries = days.filter(item => item.entry !== null);
+ const latestVisibleEntry = visibleEntries.length ? visibleEntries[visibleEntries.length - 1].entry : null;
+ let activeDate = latestVisibleEntry ? latestVisibleEntry.date : null;
const cells = days.map((item, index) => {
const week = Math.floor(index / 7);
@@ -571,7 +616,7 @@
const y = yOffset + (row * (cellSize + verticalGap));
const fill = calendarColor(item.entry);
- if (item.day <= 7 && item.month !== lastMonth && item.entry !== null) {
+ if (item.day <= 7 && item.month !== lastMonth) {
monthLabels.push({
x,
label: new Intl.DateTimeFormat("de-DE", { month: "short" }).format(new Date(`${item.date}T12:00:00`)),
@@ -588,14 +633,38 @@
}
return `
-
-