refactor(archive): redesign segmented archive experience
This commit is contained in:
@@ -48,6 +48,30 @@
|
||||
--control-soft-bg: rgba(255, 255, 255, 0.08);
|
||||
--control-soft-border: rgba(255, 255, 255, 0.16);
|
||||
--brand-shadow: 0 10px 24px rgba(9, 25, 40, 0.22);
|
||||
--archive-shell-bg:
|
||||
linear-gradient(180deg, rgba(22, 38, 58, 0.94), rgba(10, 25, 41, 0.92)),
|
||||
radial-gradient(circle at top right, rgba(59, 173, 212, 0.12), transparent 42%);
|
||||
--archive-shell-border: rgba(148, 198, 228, 0.18);
|
||||
--archive-toolbar-bg:
|
||||
linear-gradient(180deg, rgba(34, 57, 79, 0.82), rgba(22, 40, 58, 0.76)),
|
||||
radial-gradient(circle at top right, rgba(75, 203, 223, 0.1), transparent 48%);
|
||||
--archive-toolbar-border: rgba(148, 198, 228, 0.14);
|
||||
--archive-switcher-bg: rgba(12, 24, 38, 0.34);
|
||||
--archive-switcher-border: rgba(148, 198, 228, 0.12);
|
||||
--archive-switcher-active-bg: rgba(173, 213, 245, 0.14);
|
||||
--archive-row-bg: rgba(255, 255, 255, 0.06);
|
||||
--archive-row-border: rgba(255, 255, 255, 0.05);
|
||||
--archive-row-active-bg: rgba(255, 255, 255, 0.09);
|
||||
--archive-detail-bg:
|
||||
linear-gradient(180deg, rgba(40, 62, 86, 0.88), rgba(24, 41, 60, 0.82)),
|
||||
radial-gradient(circle at top left, rgba(135, 217, 255, 0.12), transparent 42%);
|
||||
--archive-select-bg: rgba(30, 51, 72, 0.84);
|
||||
--archive-select-border: rgba(148, 198, 228, 0.18);
|
||||
--archive-select-focus-bg: rgba(35, 59, 83, 0.94);
|
||||
--archive-mobile-overlay-bg: rgba(6, 14, 24, 0.36);
|
||||
--archive-mobile-top-bg:
|
||||
linear-gradient(180deg, rgba(17, 33, 50, 0.96), rgba(17, 33, 50, 0.78)),
|
||||
radial-gradient(circle at top left, rgba(135, 217, 255, 0.1), transparent 42%);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
@@ -95,6 +119,30 @@
|
||||
--control-soft-bg: rgba(255, 255, 255, 0.58);
|
||||
--control-soft-border: rgba(123, 153, 182, 0.22);
|
||||
--brand-shadow: 0 10px 24px rgba(82, 111, 138, 0.14);
|
||||
--archive-shell-bg:
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.82), rgba(247, 252, 255, 0.72)),
|
||||
radial-gradient(circle at top right, rgba(123, 190, 255, 0.16), transparent 46%);
|
||||
--archive-shell-border: rgba(120, 146, 172, 0.2);
|
||||
--archive-toolbar-bg:
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.72), rgba(245, 251, 255, 0.64)),
|
||||
radial-gradient(circle at top right, rgba(106, 203, 219, 0.12), transparent 48%);
|
||||
--archive-toolbar-border: rgba(120, 146, 172, 0.16);
|
||||
--archive-switcher-bg: rgba(255, 255, 255, 0.34);
|
||||
--archive-switcher-border: rgba(120, 146, 172, 0.18);
|
||||
--archive-switcher-active-bg: rgba(255, 255, 255, 0.72);
|
||||
--archive-row-bg: rgba(255, 255, 255, 0.38);
|
||||
--archive-row-border: rgba(120, 146, 172, 0.14);
|
||||
--archive-row-active-bg: rgba(255, 255, 255, 0.56);
|
||||
--archive-detail-bg:
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.8), rgba(244, 250, 255, 0.72)),
|
||||
radial-gradient(circle at top left, rgba(141, 205, 255, 0.16), transparent 42%);
|
||||
--archive-select-bg: rgba(255, 255, 255, 0.62);
|
||||
--archive-select-border: rgba(123, 153, 182, 0.2);
|
||||
--archive-select-focus-bg: rgba(255, 255, 255, 0.84);
|
||||
--archive-mobile-overlay-bg: rgba(236, 243, 249, 0.42);
|
||||
--archive-mobile-top-bg:
|
||||
linear-gradient(180deg, rgba(255, 255, 255, 0.96), rgba(246, 251, 255, 0.84)),
|
||||
radial-gradient(circle at top left, rgba(141, 205, 255, 0.16), transparent 42%);
|
||||
}
|
||||
|
||||
html {
|
||||
@@ -104,6 +152,31 @@
|
||||
select {
|
||||
color-scheme: light;
|
||||
}
|
||||
|
||||
.mobile-nav .nav-icon {
|
||||
opacity: 1;
|
||||
filter:
|
||||
saturate(2.1)
|
||||
contrast(1.16)
|
||||
brightness(0.76)
|
||||
drop-shadow(0 1px 0 rgba(255, 255, 255, 0.5))
|
||||
drop-shadow(0 0 8px rgba(110, 214, 255, 0.18));
|
||||
}
|
||||
|
||||
.mobile-nav a.active .nav-icon {
|
||||
filter:
|
||||
saturate(2.35)
|
||||
contrast(1.2)
|
||||
brightness(0.68)
|
||||
drop-shadow(0 1px 0 rgba(255, 255, 255, 0.56))
|
||||
drop-shadow(0 0 10px rgba(110, 214, 255, 0.24));
|
||||
}
|
||||
|
||||
.detail-grid--archive-day div {
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.72), rgba(244, 249, 255, 0.64));
|
||||
border: 1px solid rgba(126, 156, 184, 0.18);
|
||||
box-shadow: 0 8px 22px rgba(138, 167, 194, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
@@ -851,6 +924,10 @@ button:disabled {
|
||||
gap: 0.7rem;
|
||||
}
|
||||
|
||||
.sport-choice-list--single {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.sport-choice {
|
||||
display: block;
|
||||
}
|
||||
@@ -890,6 +967,11 @@ button:disabled {
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.sport-choice__card--toggle {
|
||||
min-height: 100%;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
.sport-choice input:checked + .sport-choice__card {
|
||||
border-color: rgba(139, 228, 255, 0.44);
|
||||
background: linear-gradient(180deg, rgba(96, 184, 255, 0.18), rgba(255, 255, 255, 0.08));
|
||||
@@ -1156,6 +1238,399 @@ input[type="range"] {
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
|
||||
.archive-page {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.archive-shell {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
padding: 1.2rem;
|
||||
border-radius: var(--radius-xl);
|
||||
overflow: hidden;
|
||||
background: var(--archive-shell-bg);
|
||||
border-color: var(--archive-shell-border);
|
||||
}
|
||||
|
||||
.archive-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.archive-header__meta {
|
||||
display: flex;
|
||||
gap: 0.6rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.archive-toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
padding: 0.85rem 0.95rem;
|
||||
border-radius: 18px;
|
||||
background: var(--archive-toolbar-bg);
|
||||
border: 1px solid var(--archive-toolbar-border);
|
||||
}
|
||||
|
||||
.archive-toolbar--compact {
|
||||
margin-bottom: 0.15rem;
|
||||
}
|
||||
|
||||
.archive-switcher {
|
||||
display: inline-grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0.35rem;
|
||||
padding: 0.25rem;
|
||||
border-radius: 999px;
|
||||
background: var(--archive-switcher-bg);
|
||||
border: 1px solid var(--archive-switcher-border);
|
||||
}
|
||||
|
||||
.archive-switcher__item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 2.45rem;
|
||||
padding: 0.45rem 0.9rem;
|
||||
border-radius: 999px;
|
||||
color: var(--muted);
|
||||
transition: background 180ms ease, color 180ms ease, transform 180ms ease;
|
||||
}
|
||||
|
||||
.archive-switcher__item.active {
|
||||
color: var(--text);
|
||||
background: var(--archive-switcher-active-bg);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.archive-filter {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.archive-filter label {
|
||||
min-width: 13rem;
|
||||
}
|
||||
|
||||
.archive-filter select {
|
||||
background: var(--archive-select-bg);
|
||||
border-color: var(--archive-select-border);
|
||||
}
|
||||
|
||||
.archive-filter select:focus {
|
||||
background: var(--archive-select-focus-bg);
|
||||
}
|
||||
|
||||
.archive-workspace {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.35fr) minmax(300px, 0.78fr);
|
||||
gap: 1rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.archive-main {
|
||||
display: grid;
|
||||
gap: 0.85rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.archive-list-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.archive-rows {
|
||||
display: grid;
|
||||
gap: 0.7rem;
|
||||
}
|
||||
|
||||
.archive-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.2fr) auto auto;
|
||||
align-items: center;
|
||||
gap: 0.85rem;
|
||||
padding: 0.95rem 1rem;
|
||||
border-radius: 18px;
|
||||
background: var(--archive-row-bg);
|
||||
border: 1px solid var(--archive-row-border);
|
||||
transition: transform 180ms ease, border-color 180ms ease, background 180ms ease;
|
||||
}
|
||||
|
||||
.archive-row:hover,
|
||||
.archive-row.active {
|
||||
transform: translateY(-1px);
|
||||
border-color: rgba(139, 228, 255, 0.28);
|
||||
background: var(--archive-row-active-bg);
|
||||
}
|
||||
|
||||
.archive-row__main,
|
||||
.archive-row__meta {
|
||||
display: grid;
|
||||
gap: 0.18rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.archive-row__main strong,
|
||||
.archive-row__title-group strong {
|
||||
font-size: 1rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.archive-row__main span,
|
||||
.archive-row__meta span,
|
||||
.archive-row__title-group span,
|
||||
.archive-row__hint {
|
||||
color: var(--muted);
|
||||
font-size: 0.92rem;
|
||||
overflow-wrap: normal;
|
||||
word-break: normal;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
.archive-row__meta {
|
||||
justify-items: end;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.archive-row__meta--stack {
|
||||
justify-items: start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.archive-row__hint {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.archive-row__title-group span,
|
||||
.archive-row__meta span {
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.archive-row--summary {
|
||||
grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.95fr) auto;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.archive-row--summary .archive-row__main {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.archive-row--summary .status-badge {
|
||||
justify-self: end;
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.archive-row--week .archive-row__main--week {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.archive-row--week .archive-row__main--week .status-badge,
|
||||
.archive-row--month .archive-row__main--month .status-badge {
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.archive-row--month .archive-row__main--month {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.archive-row--summary .archive-row__hint {
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
.archive-row__title-group {
|
||||
display: grid;
|
||||
gap: 0.18rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 1.7rem;
|
||||
padding: 0.2rem 0.65rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.03em;
|
||||
border: 1px solid transparent;
|
||||
max-width: 100%;
|
||||
white-space: normal;
|
||||
line-height: 1.25;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.status-badge--ready {
|
||||
color: #082336;
|
||||
background: rgba(127, 243, 187, 0.92);
|
||||
}
|
||||
|
||||
.status-badge--pending {
|
||||
color: var(--text);
|
||||
background: rgba(139, 228, 255, 0.14);
|
||||
border-color: rgba(139, 228, 255, 0.22);
|
||||
}
|
||||
|
||||
.status-badge--blocked {
|
||||
color: var(--text);
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-color: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
.archive-detail {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.archive-detail__panel {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
padding: 1.15rem;
|
||||
border-radius: var(--radius-xl);
|
||||
position: sticky;
|
||||
top: 1.25rem;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
background: var(--archive-detail-bg);
|
||||
border-color: var(--archive-shell-border);
|
||||
}
|
||||
|
||||
.archive-detail__top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.archive-detail__close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.archive-detail__status-row,
|
||||
.archive-detail__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.7rem;
|
||||
flex-wrap: wrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.archive-detail__single-action form,
|
||||
.archive-detail__actions form {
|
||||
margin: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.archive-detail__single-action {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.archive-detail__week-status {
|
||||
display: grid;
|
||||
gap: 0.7rem;
|
||||
}
|
||||
|
||||
.archive-detail__status-note {
|
||||
display: grid;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.archive-detail__status-note p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.archive-mini-list {
|
||||
display: grid;
|
||||
gap: 0.55rem;
|
||||
}
|
||||
|
||||
.archive-mini-list__row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 0.7rem;
|
||||
align-items: center;
|
||||
padding: 0.72rem 0.85rem;
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 255, 255, 0.07);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.archive-detail__panel > *,
|
||||
.archive-detail__top > *,
|
||||
.archive-detail__status-row > *,
|
||||
.archive-detail__actions > * {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.archive-detail__actions .ghost-link,
|
||||
.archive-detail__actions .ghost-button,
|
||||
.archive-detail__single-action .primary-button {
|
||||
max-width: 100%;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.archive-detail__status-row .chart-chip {
|
||||
max-width: 100%;
|
||||
white-space: normal;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.archive-detail__single-action .primary-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.detail-grid--archive div {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 0.95fr) minmax(0, 1.05fr);
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.detail-grid--archive-day {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 0.8rem;
|
||||
}
|
||||
|
||||
.detail-grid--archive-day div {
|
||||
display: grid;
|
||||
gap: 0.28rem;
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.detail-grid--archive-day dt,
|
||||
.detail-grid--archive-day dd {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.detail-grid--archive-day dd {
|
||||
text-align: left;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.detail-grid--archive dd,
|
||||
.archive-mini-list__row span,
|
||||
.note-box p {
|
||||
min-width: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.detail-grid--archive dd {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.archive-summary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
@@ -1606,6 +2081,75 @@ input[type="range"] {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.archive-header,
|
||||
.archive-toolbar,
|
||||
.archive-workspace,
|
||||
.archive-list-header,
|
||||
.archive-row,
|
||||
.archive-row--summary {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.archive-header,
|
||||
.archive-toolbar,
|
||||
.archive-list-header {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.archive-workspace {
|
||||
gap: 0.85rem;
|
||||
}
|
||||
|
||||
.archive-row__meta {
|
||||
justify-items: start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.archive-row--summary .archive-row__main {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.archive-row--summary .status-badge {
|
||||
justify-self: start;
|
||||
}
|
||||
|
||||
.archive-row--day {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: start;
|
||||
gap: 0.75rem;
|
||||
padding: 0.9rem 0.95rem;
|
||||
}
|
||||
|
||||
.archive-row--day .archive-row__main {
|
||||
gap: 0.12rem;
|
||||
}
|
||||
|
||||
.archive-row--day .archive-row__meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
text-align: right;
|
||||
gap: 0.12rem;
|
||||
}
|
||||
|
||||
.archive-row--day .archive-row__hint {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.archive-row--day .archive-row__main strong {
|
||||
font-size: 1.02rem;
|
||||
}
|
||||
|
||||
.archive-row--day .archive-row__main span,
|
||||
.archive-row--day .archive-row__meta span {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.archive-detail {
|
||||
scroll-margin-top: 1rem;
|
||||
}
|
||||
|
||||
.calendar-detail {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
@@ -1680,4 +2224,19 @@ input[type="range"] {
|
||||
align-items: flex-start;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.archive-shell {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.archive-switcher,
|
||||
.archive-filter,
|
||||
.archive-header__meta {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.archive-filter label {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+27
-1
@@ -70,6 +70,25 @@
|
||||
});
|
||||
}
|
||||
|
||||
function initArchiveMobileDetail() {
|
||||
if (!document.body.classList.contains("page-archive")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isMobileViewport = () => window.matchMedia("(max-width: 820px)").matches;
|
||||
const detail = document.querySelector("#archive-detail-panel[data-detail-open='1']");
|
||||
|
||||
if (!detail || !isMobileViewport()) {
|
||||
return;
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
detail.scrollIntoView({ block: "start", behavior: "smooth" });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function sleepDurationPoints(hours, points) {
|
||||
if (hours < 4) {
|
||||
return Number(points.lt4 || 0);
|
||||
@@ -542,8 +561,12 @@
|
||||
}
|
||||
|
||||
function calendarColor(entry) {
|
||||
const isLightMode = window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches;
|
||||
|
||||
if (!entry) {
|
||||
return "rgba(255, 255, 255, 0.06)";
|
||||
return isLightMode
|
||||
? "rgba(86, 124, 156, 0.11)"
|
||||
: "rgba(255, 255, 255, 0.06)";
|
||||
}
|
||||
|
||||
const ratio = Math.max(0, Math.min(1, Number(entry.score) / Math.max(Number(entry.max || 1), 1)));
|
||||
@@ -644,6 +667,7 @@
|
||||
const yOffset = config.yOffset;
|
||||
const gridHeight = (7 * cellSize) + (6 * verticalGap);
|
||||
const height = yOffset + gridHeight + 8;
|
||||
const sundayLabelY = yOffset + (6 * (cellSize + verticalGap)) + (cellSize * 0.78);
|
||||
const rightPadding = 4;
|
||||
const naturalWidth = xOffset + (totalWeeks * cellSize) + ((totalWeeks - 1) * baseCellGap) + rightPadding;
|
||||
const availableWidth = Math.floor(container.clientWidth || 0);
|
||||
@@ -718,6 +742,7 @@
|
||||
<text class="calendar-tooltip" x="0" y="34">Mo</text>
|
||||
<text class="calendar-tooltip" x="0" y="68">Mi</text>
|
||||
<text class="calendar-tooltip" x="0" y="102">Fr</text>
|
||||
<text class="calendar-tooltip" x="0" y="${sundayLabelY}">So</text>
|
||||
${cells}
|
||||
</svg>
|
||||
<div class="calendar-legend">
|
||||
@@ -1273,6 +1298,7 @@
|
||||
updateRangeOutputs();
|
||||
initHeaderDatePicker();
|
||||
initTrackPreview();
|
||||
initArchiveMobileDetail();
|
||||
initDashboardCharts();
|
||||
initSportTypeManager();
|
||||
initPwaShell();
|
||||
|
||||
+222
-21
@@ -345,17 +345,47 @@ final class App
|
||||
{
|
||||
$user = $this->requireUser();
|
||||
$settings = $this->hydrateSettings($this->settings->forUser($user['username']));
|
||||
$view = $this->normalizeArchiveView((string) ($_GET['view'] ?? 'days'));
|
||||
$filterMonth = trim((string) ($_GET['filter_month'] ?? ''));
|
||||
if ($filterMonth !== '' && preg_match('/^\d{4}-(0[1-9]|1[0-2])$/', $filterMonth) !== 1) {
|
||||
$filterMonth = '';
|
||||
}
|
||||
|
||||
$selectedDate = isset($_GET['date']) ? (string) $_GET['date'] : null;
|
||||
$selectedSummaryKind = isset($_GET['summary_kind']) ? (string) $_GET['summary_kind'] : null;
|
||||
$selectedSummaryKey = isset($_GET['summary_key']) ? (string) $_GET['summary_key'] : null;
|
||||
$selectedWeekKey = isset($_GET['week'] ) ? trim((string) $_GET['week']) : null;
|
||||
$selectedMonthKey = isset($_GET['month_key']) ? trim((string) $_GET['month_key']) : null;
|
||||
$entries = $this->entries->all($user['username']);
|
||||
$archive = array_reverse($this->evaluateEntriesWithContext($entries, $settings));
|
||||
$weeklySummaries = $this->summaries->weekly($user['username']);
|
||||
$monthlySummaries = $this->summaries->monthly($user['username']);
|
||||
$weeklyArchive = $this->buildWeeklyArchiveCards($archive, $weeklySummaries);
|
||||
$monthlyArchive = $this->buildMonthlyArchiveCards($archive, $weeklySummaries, $monthlySummaries, $weeklyArchive);
|
||||
$monthOptions = $this->buildArchiveMonthOptions($archive, $weeklyArchive, $monthlyArchive);
|
||||
|
||||
$filteredDays = $filterMonth === ''
|
||||
? $archive
|
||||
: array_values(array_filter(
|
||||
$archive,
|
||||
static fn (array $entry): bool => month_key((string) ($entry['date'] ?? '')) === $filterMonth
|
||||
));
|
||||
|
||||
$filteredWeeks = $filterMonth === ''
|
||||
? $weeklyArchive
|
||||
: array_values(array_filter(
|
||||
$weeklyArchive,
|
||||
fn (array $week): bool => $this->archiveItemOverlapsMonth($week, $filterMonth)
|
||||
));
|
||||
|
||||
$filteredMonths = $filterMonth === ''
|
||||
? $monthlyArchive
|
||||
: array_values(array_filter(
|
||||
$monthlyArchive,
|
||||
static fn (array $month): bool => (string) ($month['summary_key'] ?? '') === $filterMonth
|
||||
));
|
||||
|
||||
$selectedEntry = null;
|
||||
if ($selectedDate !== null) {
|
||||
foreach ($archive as $entry) {
|
||||
if ($view === 'days' && $selectedDate !== null) {
|
||||
foreach ($filteredDays as $entry) {
|
||||
if ($entry['date'] === $selectedDate) {
|
||||
$selectedEntry = $entry;
|
||||
break;
|
||||
@@ -363,21 +393,40 @@ final class App
|
||||
}
|
||||
}
|
||||
|
||||
$selectedSummary = null;
|
||||
if ($selectedSummaryKind !== null && $selectedSummaryKey !== null) {
|
||||
$selectedSummary = $this->summaries->find($user['username'], $selectedSummaryKind, $selectedSummaryKey);
|
||||
$selectedWeek = null;
|
||||
if ($view === 'weeks' && $selectedWeekKey !== null) {
|
||||
foreach ($filteredWeeks as $week) {
|
||||
if (($week['summary_key'] ?? '') === $selectedWeekKey) {
|
||||
$selectedWeek = $week;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$selectedMonth = null;
|
||||
if ($view === 'months' && $selectedMonthKey !== null) {
|
||||
foreach ($filteredMonths as $month) {
|
||||
if (($month['summary_key'] ?? '') === $selectedMonthKey) {
|
||||
$selectedMonth = $month;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
View::render('archive', [
|
||||
'pageTitle' => 'Archiv',
|
||||
'page' => 'archive',
|
||||
'authUser' => $user,
|
||||
'entries' => $archive,
|
||||
'entries' => $filteredDays,
|
||||
'selectedEntry' => $selectedEntry,
|
||||
'selectedSummary' => $selectedSummary,
|
||||
'selectedWeek' => $selectedWeek,
|
||||
'selectedMonth' => $selectedMonth,
|
||||
'settings' => $settings,
|
||||
'weeklyArchive' => $this->buildWeeklyArchiveCards($archive, $weeklySummaries),
|
||||
'monthlyArchive' => $this->buildMonthlyArchiveCards($archive, $weeklySummaries, $monthlySummaries),
|
||||
'archiveView' => $view,
|
||||
'archiveFilterMonth' => $filterMonth,
|
||||
'archiveMonthOptions' => $monthOptions,
|
||||
'weeklyArchive' => $filteredWeeks,
|
||||
'monthlyArchive' => $filteredMonths,
|
||||
'aiAvailable' => $this->openAi->isAvailable(),
|
||||
]);
|
||||
}
|
||||
@@ -390,6 +439,11 @@ final class App
|
||||
$settings = $this->hydrateSettings($this->settings->forUser($user['username']));
|
||||
$entries = $this->evaluateEntriesWithContext($this->entries->all($user['username']), $settings);
|
||||
$form = (string) ($_POST['form_name'] ?? '');
|
||||
$returnView = $this->normalizeArchiveView((string) ($_POST['view'] ?? 'days'));
|
||||
$returnFilterMonth = trim((string) ($_POST['filter_month'] ?? ''));
|
||||
if ($returnFilterMonth !== '' && preg_match('/^\d{4}-(0[1-9]|1[0-2])$/', $returnFilterMonth) !== 1) {
|
||||
$returnFilterMonth = '';
|
||||
}
|
||||
|
||||
try {
|
||||
if ($form === 'generate_weekly_summary') {
|
||||
@@ -406,7 +460,11 @@ final class App
|
||||
]);
|
||||
|
||||
flash('success', 'Die KI-Wochenzusammenfassung wurde erstellt.');
|
||||
redirect('/archive?summary_kind=weekly&summary_key=' . rawurlencode($weekKey));
|
||||
redirect($this->archivePath([
|
||||
'view' => 'weeks',
|
||||
'filter_month' => $returnFilterMonth !== '' ? $returnFilterMonth : month_key($context['date_to']),
|
||||
'week' => $weekKey,
|
||||
]));
|
||||
}
|
||||
|
||||
if ($form === 'generate_monthly_summary') {
|
||||
@@ -425,14 +483,24 @@ final class App
|
||||
]);
|
||||
|
||||
flash('success', 'Die KI-Monatszusammenfassung wurde erstellt.');
|
||||
redirect('/archive?summary_kind=monthly&summary_key=' . rawurlencode($monthKey));
|
||||
redirect($this->archivePath([
|
||||
'view' => 'months',
|
||||
'filter_month' => $returnFilterMonth !== '' ? $returnFilterMonth : $monthKey,
|
||||
'month_key' => $monthKey,
|
||||
]));
|
||||
}
|
||||
} catch (RuntimeException $exception) {
|
||||
flash('error', $exception->getMessage());
|
||||
redirect('/archive');
|
||||
redirect($this->archivePath([
|
||||
'view' => $returnView,
|
||||
'filter_month' => $returnFilterMonth,
|
||||
]));
|
||||
}
|
||||
|
||||
redirect('/archive');
|
||||
redirect($this->archivePath([
|
||||
'view' => $returnView,
|
||||
'filter_month' => $returnFilterMonth,
|
||||
]));
|
||||
}
|
||||
|
||||
private function showOptions(): void
|
||||
@@ -1107,6 +1175,21 @@ final class App
|
||||
$weekEntries,
|
||||
static fn (array $entry): bool => trim((string) ($entry['note'] ?? '')) !== ''
|
||||
));
|
||||
$canGenerate = count($noteEntries) >= 3;
|
||||
|
||||
if ($summary !== null) {
|
||||
$statusLabel = 'KI vorhanden';
|
||||
$statusTone = 'ready';
|
||||
$statusHint = 'Erstellt am ' . format_compact_datetime((string) ($summary['created_at'] ?? ''));
|
||||
} elseif ($canGenerate) {
|
||||
$statusLabel = 'KI möglich';
|
||||
$statusTone = 'pending';
|
||||
$statusHint = 'KI-Wochenzusammenfassung kann erzeugt werden';
|
||||
} else {
|
||||
$statusLabel = 'KI nicht möglich';
|
||||
$statusTone = 'blocked';
|
||||
$statusHint = 'Mindestens 3 Texteinträge nötig';
|
||||
}
|
||||
|
||||
$cards[] = [
|
||||
'summary_key' => $key,
|
||||
@@ -1115,9 +1198,13 @@ final class App
|
||||
'date_to' => $summary['date_to'] ?? $range['date_to'],
|
||||
'tracked_days' => count($weekEntries),
|
||||
'note_entries_count' => count($noteEntries),
|
||||
'can_generate' => count($noteEntries) >= 3,
|
||||
'can_generate' => $canGenerate,
|
||||
'summary' => $summary,
|
||||
'has_summary' => $summary !== null,
|
||||
'status_label' => $statusLabel,
|
||||
'status_tone' => $statusTone,
|
||||
'status_hint' => $statusHint,
|
||||
'trend_label' => $this->weeklyTrendLabel($weekEntries),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1126,7 +1213,7 @@ final class App
|
||||
return $cards;
|
||||
}
|
||||
|
||||
private function buildMonthlyArchiveCards(array $entries, array $weeklySummaries, array $monthlySummaries): array
|
||||
private function buildMonthlyArchiveCards(array $entries, array $weeklySummaries, array $monthlySummaries, array $weeklyArchive): array
|
||||
{
|
||||
$monthKeys = [];
|
||||
|
||||
@@ -1163,8 +1250,32 @@ final class App
|
||||
$weeklySummaries,
|
||||
fn (array $summary): bool => $this->summaryOverlapsMonth($summary, $monthKey)
|
||||
));
|
||||
$monthWeeks = array_values(array_filter(
|
||||
$weeklyArchive,
|
||||
fn (array $week): bool => $this->archiveItemOverlapsMonth($week, $monthKey)
|
||||
));
|
||||
|
||||
usort($monthWeeklySummaries, static fn (array $left, array $right): int => strcmp((string) ($left['date_from'] ?? ''), (string) ($right['date_from'] ?? '')));
|
||||
usort($monthWeeks, static fn (array $left, array $right): int => strcmp((string) ($left['date_from'] ?? ''), (string) ($right['date_from'] ?? '')));
|
||||
|
||||
$availableWeeklyCount = count($monthWeeklySummaries);
|
||||
$totalWeekCount = count($monthWeeks);
|
||||
$canGenerate = $availableWeeklyCount >= 2;
|
||||
$summary = $monthlySummaryMap[$monthKey] ?? null;
|
||||
|
||||
if ($summary !== null) {
|
||||
$statusLabel = 'KI vorhanden';
|
||||
$statusTone = 'ready';
|
||||
$statusHint = 'Erstellt am ' . format_compact_datetime((string) ($summary['created_at'] ?? ''));
|
||||
} elseif ($canGenerate) {
|
||||
$statusLabel = 'KI möglich';
|
||||
$statusTone = 'pending';
|
||||
$statusHint = 'KI-Monatszusammenfassung kann erzeugt werden';
|
||||
} else {
|
||||
$statusLabel = 'KI nicht möglich';
|
||||
$statusTone = 'blocked';
|
||||
$statusHint = 'Mindestens 2 KI-Wochenzusammenfassungen nötig';
|
||||
}
|
||||
|
||||
$cards[] = [
|
||||
'summary_key' => $monthKey,
|
||||
@@ -1172,10 +1283,22 @@ final class App
|
||||
'date_from' => $range['date_from'],
|
||||
'date_to' => $range['date_to'],
|
||||
'tracked_days' => count($monthEntries),
|
||||
'weekly_summary_count' => count($monthWeeklySummaries),
|
||||
'can_generate' => count($monthWeeklySummaries) >= 2,
|
||||
'summary' => $monthlySummaryMap[$monthKey] ?? null,
|
||||
'has_summary' => isset($monthlySummaryMap[$monthKey]),
|
||||
'weekly_summary_count' => $availableWeeklyCount,
|
||||
'weekly_total_count' => $totalWeekCount,
|
||||
'weekly_progress_label' => $availableWeeklyCount . ' von ' . ($totalWeekCount > 0 ? $totalWeekCount : 0) . ' Wochen mit KI',
|
||||
'can_generate' => $canGenerate,
|
||||
'summary' => $summary,
|
||||
'has_summary' => $summary !== null,
|
||||
'status_label' => $statusLabel,
|
||||
'status_tone' => $statusTone,
|
||||
'status_hint' => $statusHint,
|
||||
'weeks' => array_map(static function (array $week): array {
|
||||
return [
|
||||
'label' => (string) ($week['label'] ?? ''),
|
||||
'summary_key' => (string) ($week['summary_key'] ?? ''),
|
||||
'has_summary' => !empty($week['has_summary']),
|
||||
];
|
||||
}, $monthWeeks),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1444,6 +1567,84 @@ final class App
|
||||
&& $summaryTo >= $range['date_from'];
|
||||
}
|
||||
|
||||
private function buildArchiveMonthOptions(array $entries, array $weeklyArchive, array $monthlyArchive): array
|
||||
{
|
||||
$keys = [];
|
||||
|
||||
foreach ($entries as $entry) {
|
||||
$keys[month_key((string) ($entry['date'] ?? ''))] = true;
|
||||
}
|
||||
|
||||
foreach ($weeklyArchive as $week) {
|
||||
foreach ($this->monthKeysForRange((string) ($week['date_from'] ?? ''), (string) ($week['date_to'] ?? '')) as $monthKey) {
|
||||
$keys[$monthKey] = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($monthlyArchive as $month) {
|
||||
$keys[(string) ($month['summary_key'] ?? '')] = true;
|
||||
}
|
||||
|
||||
$options = array_values(array_filter(array_keys($keys), static fn (string $key): bool => $key !== ''));
|
||||
rsort($options, SORT_STRING);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
private function normalizeArchiveView(string $view): string
|
||||
{
|
||||
$view = trim($view);
|
||||
|
||||
return in_array($view, ['days', 'weeks', 'months'], true) ? $view : 'days';
|
||||
}
|
||||
|
||||
private function weeklyTrendLabel(array $entries): string
|
||||
{
|
||||
if ($entries === []) {
|
||||
return 'Keine Tendenz';
|
||||
}
|
||||
|
||||
$mood = $this->average($entries, 'mood');
|
||||
$stress = $this->average($entries, 'stress');
|
||||
|
||||
if ($mood >= 7 && $stress <= 4) {
|
||||
return 'eher stabil';
|
||||
}
|
||||
|
||||
if ($mood <= 4 || $stress >= 7) {
|
||||
return 'eher belastet';
|
||||
}
|
||||
|
||||
return 'gemischte Tendenz';
|
||||
}
|
||||
|
||||
private function archiveItemOverlapsMonth(array $item, string $monthKey): bool
|
||||
{
|
||||
if (preg_match('/^\d{4}-(0[1-9]|1[0-2])$/', $monthKey) !== 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$dateFrom = (string) ($item['date_from'] ?? '');
|
||||
$dateTo = (string) ($item['date_to'] ?? '');
|
||||
|
||||
if ($dateFrom === '' || $dateTo === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$range = $this->monthRangeFromKey($monthKey);
|
||||
|
||||
return $dateFrom <= $range['date_to'] && $dateTo >= $range['date_from'];
|
||||
}
|
||||
|
||||
private function archivePath(array $params = []): string
|
||||
{
|
||||
$filtered = array_filter($params, static fn (mixed $value): bool => $value !== null && $value !== '');
|
||||
|
||||
return $filtered === []
|
||||
? '/archive'
|
||||
: '/archive?' . http_build_query($filtered);
|
||||
}
|
||||
|
||||
private function sendSecurityHeaders(): void
|
||||
{
|
||||
header('Referrer-Policy: strict-origin-when-cross-origin');
|
||||
|
||||
@@ -197,6 +197,17 @@ function format_display_date(string $date, bool $withWeekday = true): string
|
||||
return $weekdays[(int) $current->format('w')] . ', ' . $label;
|
||||
}
|
||||
|
||||
function format_compact_date(string $date): string
|
||||
{
|
||||
$current = DateTimeImmutable::createFromFormat('Y-m-d', $date);
|
||||
|
||||
if ($current === false) {
|
||||
return $date;
|
||||
}
|
||||
|
||||
return $current->format('d.m.Y');
|
||||
}
|
||||
|
||||
function format_display_datetime(string $value): string
|
||||
{
|
||||
try {
|
||||
@@ -223,6 +234,17 @@ function format_display_datetime(string $value): string
|
||||
return $current->format('j.') . ' ' . $months[(int) $current->format('n')] . ' ' . $current->format('Y') . ' um ' . $current->format('H:i');
|
||||
}
|
||||
|
||||
function format_compact_datetime(string $value): string
|
||||
{
|
||||
try {
|
||||
$current = new DateTimeImmutable($value);
|
||||
} catch (Throwable) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return $current->format('d.m.Y · H:i');
|
||||
}
|
||||
|
||||
function iso_week_key(string $date): string
|
||||
{
|
||||
$current = DateTimeImmutable::createFromFormat('Y-m-d', $date);
|
||||
|
||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
||||
$brandSubtitle = match ($page) {
|
||||
'dashboard' => 'Statistiken und Verlauf',
|
||||
'track' => 'Tag erfassen und bewerten',
|
||||
'archive' => 'Rückblick auf vergangene Tage',
|
||||
'archive' => '',
|
||||
'options' => 'Logik, Erinnerungen, Sicherheit und Accounts',
|
||||
'login' => 'Geschützter Zugang',
|
||||
'setup' => 'Erstkonfiguration',
|
||||
@@ -36,7 +36,7 @@ $brandSubtitle = match ($page) {
|
||||
<link rel="stylesheet" href="/assets/css/app.css">
|
||||
<script defer src="/assets/js/app.js"></script>
|
||||
</head>
|
||||
<body class="app-body page-<?= e($page) ?><?= $authUser !== null ? ' is-authenticated' : '' ?>" data-authenticated="<?= $authUser !== null ? '1' : '0' ?>"<?= isset($trackMood) ? ' data-track-mood="' . e($trackMood) . '"' : '' ?>>
|
||||
<body class="app-body page-<?= e($page) ?><?= $authUser !== null ? ' is-authenticated' : '' ?><?= !empty($pageBodyClass) ? ' ' . e((string) $pageBodyClass) : '' ?>" data-authenticated="<?= $authUser !== null ? '1' : '0' ?>"<?= isset($trackMood) ? ' data-track-mood="' . e($trackMood) . '"' : '' ?>>
|
||||
<div class="aurora aurora-one"></div>
|
||||
<div class="aurora aurora-two"></div>
|
||||
<div class="pull-refresh-indicator glass-panel" data-pull-refresh-indicator aria-hidden="true">Zum Aktualisieren ziehen</div>
|
||||
@@ -89,7 +89,9 @@ $brandSubtitle = match ($page) {
|
||||
<?php if ($authUser !== null): ?>
|
||||
<header class="topbar glass-panel">
|
||||
<div>
|
||||
<?php if ($brandSubtitle !== ''): ?>
|
||||
<p class="eyebrow"><?= e($brandSubtitle) ?></p>
|
||||
<?php endif; ?>
|
||||
<h2><?= e($pageTitle) ?></h2>
|
||||
</div>
|
||||
<div class="topbar__meta">
|
||||
|
||||
+250
-184
@@ -1,190 +1,169 @@
|
||||
<section class="page-grid">
|
||||
<article class="glass-panel archive-list">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<p class="eyebrow">Archiv</p>
|
||||
<h3>KI-Rückblicke und gespeicherte Tage</h3>
|
||||
</div>
|
||||
<span class="chart-chip"><?= e((string) count($entries)) ?> Einträge</span>
|
||||
</div>
|
||||
<?php
|
||||
$baseParams = ['view' => $archiveView];
|
||||
if ($archiveFilterMonth !== '') {
|
||||
$baseParams['filter_month'] = $archiveFilterMonth;
|
||||
}
|
||||
|
||||
<section class="archive-summary-section">
|
||||
<div class="section-head section-head--compact">
|
||||
<div>
|
||||
<p class="eyebrow">KI</p>
|
||||
<h4>Monatszusammenfassungen</h4>
|
||||
</div>
|
||||
<?php if (empty($aiAvailable)): ?>
|
||||
<span class="chart-chip chart-chip--muted">API nicht bereit</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
$archiveUrl = static function (array $params = []) use ($baseParams): string {
|
||||
$query = array_filter(array_merge($baseParams, $params), static fn (mixed $value): bool => $value !== null && $value !== '');
|
||||
|
||||
<?php if ($monthlyArchive === []): ?>
|
||||
<p class="empty-state">Sobald genügend Wochenzusammenfassungen vorliegen, erscheinen hier die Monatsrückblicke.</p>
|
||||
<?php else: ?>
|
||||
<div class="archive-summary-grid">
|
||||
<?php foreach ($monthlyArchive as $month): ?>
|
||||
<article class="archive-summary-card">
|
||||
<div class="archive-summary-card__head">
|
||||
<div>
|
||||
<span class="summary-badge">KI</span>
|
||||
<strong><?= e($month['label']) ?></strong>
|
||||
</div>
|
||||
<?php if (!empty($month['has_summary'])): ?>
|
||||
<span class="chart-chip">vorhanden</span>
|
||||
<?php else: ?>
|
||||
<span class="chart-chip chart-chip--muted">offen</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
return $query === [] ? '/archive' : '/archive?' . http_build_query($query);
|
||||
};
|
||||
|
||||
<p class="helper-text"><?= e($month['date_from']) ?> bis <?= e($month['date_to']) ?></p>
|
||||
<p class="helper-text"><?= e((string) $month['weekly_summary_count']) ?> KI-Wochenzusammenfassungen im Monat verfügbar</p>
|
||||
$detailType = $selectedEntry !== null
|
||||
? 'day'
|
||||
: ($selectedWeek !== null
|
||||
? 'week'
|
||||
: ($selectedMonth !== null ? 'month' : null));
|
||||
|
||||
<?php if (!empty($month['summary'])): ?>
|
||||
<p class="helper-text">Erstellt am <?= e(format_display_datetime((string) $month['summary']['created_at'])) ?></p>
|
||||
<?php else: ?>
|
||||
<p class="helper-text">Mindestens 2 KI-Wochenzusammenfassungen nötig.</p>
|
||||
<?php endif; ?>
|
||||
$detailOpen = $detailType !== null;
|
||||
?>
|
||||
|
||||
<div class="archive-item__actions archive-item__actions--stack">
|
||||
<?php if (!empty($month['summary'])): ?>
|
||||
<a class="ghost-link archive-action" href="/archive?summary_kind=monthly&summary_key=<?= e(rawurlencode((string) $month['summary_key'])) ?>">Öffnen</a>
|
||||
<?php endif; ?>
|
||||
<section class="archive-page">
|
||||
<article class="glass-panel archive-shell">
|
||||
<div class="archive-toolbar archive-toolbar--compact">
|
||||
<nav class="archive-switcher" aria-label="Archivansicht">
|
||||
<a class="archive-switcher__item <?= $archiveView === 'days' ? 'active' : '' ?>" href="<?= e('/archive?view=days' . ($archiveFilterMonth !== '' ? '&filter_month=' . rawurlencode($archiveFilterMonth) : '')) ?>">Tage</a>
|
||||
<a class="archive-switcher__item <?= $archiveView === 'weeks' ? 'active' : '' ?>" href="<?= e('/archive?view=weeks' . ($archiveFilterMonth !== '' ? '&filter_month=' . rawurlencode($archiveFilterMonth) : '')) ?>">Wochen</a>
|
||||
<a class="archive-switcher__item <?= $archiveView === 'months' ? 'active' : '' ?>" href="<?= e('/archive?view=months' . ($archiveFilterMonth !== '' ? '&filter_month=' . rawurlencode($archiveFilterMonth) : '')) ?>">Monate</a>
|
||||
</nav>
|
||||
|
||||
<form method="post" action="/archive">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="form_name" value="generate_monthly_summary">
|
||||
<input type="hidden" name="month_key" value="<?= e((string) $month['summary_key']) ?>">
|
||||
<button class="ghost-button ghost-button--small" type="submit" <?= !$month['can_generate'] || empty($aiAvailable) ? 'disabled' : '' ?>>
|
||||
<?= !empty($month['has_summary']) ? 'Neu generieren' : 'KI-Monatszusammenfassung erzeugen' ?>
|
||||
</button>
|
||||
<form method="get" action="/archive" class="archive-filter">
|
||||
<input type="hidden" name="view" value="<?= e($archiveView) ?>">
|
||||
<label>
|
||||
<span>Zeitraum</span>
|
||||
<select name="filter_month" onchange="this.form.submit()">
|
||||
<option value="">Alle Monate</option>
|
||||
<?php foreach ($archiveMonthOptions as $monthOption): ?>
|
||||
<option value="<?= e($monthOption) ?>" <?= $archiveFilterMonth === $monthOption ? 'selected' : '' ?>><?= e(month_label($monthOption)) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="archive-summary-section">
|
||||
<div class="section-head section-head--compact">
|
||||
<div>
|
||||
<p class="eyebrow">KI</p>
|
||||
<h4>Wochenzusammenfassungen</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($weeklyArchive === []): ?>
|
||||
<p class="empty-state">Noch keine Wochen verfügbar. Sobald Einträge vorliegen, kannst du hier Wochenrückblicke erzeugen.</p>
|
||||
<?php else: ?>
|
||||
<div class="archive-summary-grid">
|
||||
<?php foreach ($weeklyArchive as $week): ?>
|
||||
<article class="archive-summary-card">
|
||||
<div class="archive-summary-card__head">
|
||||
<div>
|
||||
<span class="summary-badge">KI</span>
|
||||
<strong><?= e($week['label']) ?></strong>
|
||||
</div>
|
||||
<?php if (!empty($week['has_summary'])): ?>
|
||||
<span class="chart-chip">vorhanden</span>
|
||||
<?php else: ?>
|
||||
<span class="chart-chip chart-chip--muted">offen</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<p class="helper-text"><?= e($week['date_from']) ?> bis <?= e($week['date_to']) ?></p>
|
||||
<p class="helper-text"><?= e((string) $week['note_entries_count']) ?> Texteinträge · <?= e((string) $week['tracked_days']) ?> getrackte Tage</p>
|
||||
|
||||
<?php if (!empty($week['summary'])): ?>
|
||||
<p class="helper-text">Erstellt am <?= e(format_display_datetime((string) $week['summary']['created_at'])) ?></p>
|
||||
<?php else: ?>
|
||||
<p class="helper-text">Mindestens 3 Texteinträge nötig.</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="archive-item__actions archive-item__actions--stack">
|
||||
<?php if (!empty($week['summary'])): ?>
|
||||
<a class="ghost-link archive-action" href="/archive?summary_kind=weekly&summary_key=<?= e(rawurlencode((string) $week['summary_key'])) ?>">Öffnen</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" action="/archive">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="form_name" value="generate_weekly_summary">
|
||||
<input type="hidden" name="week_key" value="<?= e((string) $week['summary_key']) ?>">
|
||||
<button class="ghost-button ghost-button--small" type="submit" <?= !$week['can_generate'] || empty($aiAvailable) ? 'disabled' : '' ?>>
|
||||
<?= !empty($week['has_summary']) ? 'Neu generieren' : 'KI-Wochenzusammenfassung erzeugen' ?>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<section class="archive-summary-section">
|
||||
<div class="section-head section-head--compact">
|
||||
<div class="archive-workspace">
|
||||
<section class="archive-main">
|
||||
<?php if ($archiveView === 'days'): ?>
|
||||
<div class="archive-list-header">
|
||||
<div>
|
||||
<p class="eyebrow">Tage</p>
|
||||
<h4>Alle gespeicherten Tage</h4>
|
||||
<h4>Gespeicherte Tage</h4>
|
||||
</div>
|
||||
<span class="chart-chip"><?= e((string) count($entries)) ?> Tage</span>
|
||||
</div>
|
||||
|
||||
<?php if ($entries === []): ?>
|
||||
<p class="empty-state">Noch keine Einträge vorhanden. Auf der Tracking-Seite kannst du den ersten Tag anlegen.</p>
|
||||
<p class="empty-state">Für diesen Zeitraum gibt es noch keine getrackten Tage.</p>
|
||||
<?php else: ?>
|
||||
<div class="archive-items">
|
||||
<div class="archive-rows">
|
||||
<?php foreach ($entries as $entry): ?>
|
||||
<article class="archive-item <?= $selectedEntry !== null && $selectedEntry['date'] === $entry['date'] ? 'active' : '' ?>">
|
||||
<div>
|
||||
<strong><?= e(format_display_date($entry['date'], false)) ?></strong>
|
||||
<a class="archive-row archive-row--day <?= $selectedEntry !== null && $selectedEntry['date'] === $entry['date'] ? 'active' : '' ?>" href="<?= e($archiveUrl(['date' => $entry['date'], 'week' => null, 'month_key' => null])) ?>">
|
||||
<div class="archive-row__main">
|
||||
<strong><?= e(format_compact_date($entry['date'])) ?></strong>
|
||||
<span><?= e($entry['evaluation']['label']) ?></span>
|
||||
<?php if ((int) $entry['sport_minutes'] > 0 && !empty($entry['sport_type_meta'])): ?>
|
||||
<span class="sport-pill-group">
|
||||
<?php foreach ($entry['sport_type_meta'] as $sportType): ?>
|
||||
<span class="sport-pill">
|
||||
<img src="<?= e(sport_icon_path($sportType['icon'])) ?>" alt="">
|
||||
<span><?= e($sportType['label']) ?><?= !empty($sportType['location']) ? ' · ' . e(sport_location_label((string) $sportType['location'])) : '' ?></span>
|
||||
</span>
|
||||
<?php endforeach; ?>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="archive-item__meta">
|
||||
<span><?= e(format_points((float) $entry['evaluation']['total'])) ?></span>
|
||||
<div class="archive-row__meta">
|
||||
<span><?= e(format_points((float) $entry['evaluation']['total'])) ?> Punkte</span>
|
||||
<span>Stimmung <?= e((string) $entry['mood']) ?>/10</span>
|
||||
</div>
|
||||
<div class="archive-item__actions">
|
||||
<a class="ghost-link archive-action" href="/archive?date=<?= e(rawurlencode($entry['date'])) ?>">Ansehen</a>
|
||||
<a class="ghost-link archive-action" href="/track?date=<?= e(rawurlencode($entry['date'])) ?>">Bearbeiten</a>
|
||||
</div>
|
||||
</article>
|
||||
<span class="archive-row__hint">Ansehen</span>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<aside class="stack-column">
|
||||
<?php if ($selectedSummary !== null): ?>
|
||||
<article class="glass-panel detail-card">
|
||||
<p class="eyebrow">KI-Zusammenfassung</p>
|
||||
<h3><?= e($selectedSummary['title']) ?></h3>
|
||||
<p class="hero-label"><?= e($selectedSummary['date_from']) ?> bis <?= e($selectedSummary['date_to']) ?></p>
|
||||
<p class="helper-text">Erstellt am <?= e(format_display_datetime((string) $selectedSummary['created_at'])) ?></p>
|
||||
|
||||
<div class="note-box note-box--summary">
|
||||
<h4>Text</h4>
|
||||
<p><?= e($selectedSummary['text']) ?></p>
|
||||
<?php elseif ($archiveView === 'weeks'): ?>
|
||||
<div class="archive-list-header">
|
||||
<div>
|
||||
<p class="eyebrow">Wochen</p>
|
||||
<h4>Wöchentliche KI-Rückblicke</h4>
|
||||
</div>
|
||||
</article>
|
||||
<?php elseif ($selectedEntry !== null): ?>
|
||||
<article class="glass-panel detail-card">
|
||||
<p class="eyebrow">Ausgewählt</p>
|
||||
<h3><?= e(format_display_date($selectedEntry['date'])) ?></h3>
|
||||
<span class="chart-chip"><?= e((string) count($weeklyArchive)) ?> Wochen</span>
|
||||
</div>
|
||||
|
||||
<?php if ($weeklyArchive === []): ?>
|
||||
<p class="empty-state">Für diesen Zeitraum sind noch keine Wochen im Archiv vorhanden.</p>
|
||||
<?php else: ?>
|
||||
<div class="archive-rows archive-rows--summary">
|
||||
<?php foreach ($weeklyArchive as $week): ?>
|
||||
<a class="archive-row archive-row--summary archive-row--week <?= $selectedWeek !== null && $selectedWeek['summary_key'] === $week['summary_key'] ? 'active' : '' ?>" href="<?= e($archiveUrl(['week' => $week['summary_key'], 'date' => null, 'month_key' => null])) ?>">
|
||||
<div class="archive-row__main archive-row__main--week">
|
||||
<div class="archive-row__title-group">
|
||||
<strong><?= e($week['label']) ?></strong>
|
||||
<span><?= e(format_compact_date((string) $week['date_from'])) ?> bis <?= e(format_compact_date((string) $week['date_to'])) ?></span>
|
||||
</div>
|
||||
<span class="status-badge status-badge--<?= e($week['status_tone']) ?>"><?= e($week['status_label']) ?></span>
|
||||
</div>
|
||||
<div class="archive-row__meta archive-row__meta--stack">
|
||||
<span><?= e((string) $week['note_entries_count']) ?> Texteinträge</span>
|
||||
<span><?= e((string) $week['tracked_days']) ?> getrackte Tage</span>
|
||||
<span><?= e($week['trend_label']) ?></span>
|
||||
</div>
|
||||
<span class="archive-row__hint"><?= !empty($week['has_summary']) ? 'Öffnen' : 'Details' ?></span>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<div class="archive-list-header">
|
||||
<div>
|
||||
<p class="eyebrow">Monate</p>
|
||||
<h4>Monatliche KI-Rückblicke</h4>
|
||||
</div>
|
||||
<span class="chart-chip"><?= e((string) count($monthlyArchive)) ?> Monate</span>
|
||||
</div>
|
||||
|
||||
<?php if ($monthlyArchive === []): ?>
|
||||
<p class="empty-state">Für diesen Zeitraum sind noch keine Monatsobjekte im Archiv vorhanden.</p>
|
||||
<?php else: ?>
|
||||
<div class="archive-rows archive-rows--summary">
|
||||
<?php foreach ($monthlyArchive as $month): ?>
|
||||
<a class="archive-row archive-row--summary archive-row--month <?= $selectedMonth !== null && $selectedMonth['summary_key'] === $month['summary_key'] ? 'active' : '' ?>" href="<?= e($archiveUrl(['month_key' => $month['summary_key'], 'date' => null, 'week' => null])) ?>">
|
||||
<div class="archive-row__main archive-row__main--month">
|
||||
<div class="archive-row__title-group">
|
||||
<strong><?= e($month['label']) ?></strong>
|
||||
<span><?= e(format_compact_date((string) $month['date_from'])) ?> bis <?= e(format_compact_date((string) $month['date_to'])) ?></span>
|
||||
</div>
|
||||
<span class="status-badge status-badge--<?= e($month['status_tone']) ?>"><?= e($month['status_label']) ?></span>
|
||||
</div>
|
||||
<div class="archive-row__meta archive-row__meta--stack">
|
||||
<span><?= e($month['weekly_progress_label']) ?></span>
|
||||
<span><?= e((string) $month['tracked_days']) ?> getrackte Tage</span>
|
||||
</div>
|
||||
<span class="archive-row__hint"><?= !empty($month['has_summary']) ? 'Öffnen' : 'Details' ?></span>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<aside class="archive-detail <?= $detailOpen ? 'is-open' : '' ?>" id="archive-detail-panel" data-detail-open="<?= $detailOpen ? '1' : '0' ?>">
|
||||
<div class="glass-panel archive-detail__panel">
|
||||
<div class="archive-detail__top">
|
||||
<div>
|
||||
<p class="eyebrow">Details</p>
|
||||
<?php if ($detailType === 'day'): ?>
|
||||
<h3><?= e(format_compact_date($selectedEntry['date'])) ?></h3>
|
||||
<?php elseif ($detailType === 'week'): ?>
|
||||
<h3><?= e($selectedWeek['label']) ?></h3>
|
||||
<?php elseif ($detailType === 'month'): ?>
|
||||
<h3><?= e($selectedMonth['label']) ?></h3>
|
||||
<?php else: ?>
|
||||
<h3>Archivansicht</h3>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($detailOpen): ?>
|
||||
<a class="ghost-link archive-detail__close" href="<?= e($archiveUrl(['date' => null, 'week' => null, 'month_key' => null])) ?>">Schließen</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if ($detailType === 'day'): ?>
|
||||
<p class="hero-label"><?= e($selectedEntry['evaluation']['label']) ?> · <?= e(format_points((float) $selectedEntry['evaluation']['total'])) ?> Punkte</p>
|
||||
<a class="primary-button button-link" href="/track?date=<?= e(rawurlencode($selectedEntry['date'])) ?>">Diesen Tag bearbeiten</a>
|
||||
|
||||
<dl class="detail-grid">
|
||||
<dl class="detail-grid detail-grid--archive-day">
|
||||
<div><dt>Stimmung</dt><dd><?= e((string) $selectedEntry['mood']) ?>/10</dd></div>
|
||||
<div><dt>Energie</dt><dd><?= e((string) $selectedEntry['energy']) ?>/10</dd></div>
|
||||
<div><dt>Stress</dt><dd><?= e((string) $selectedEntry['stress']) ?>/10</dd></div>
|
||||
@@ -194,24 +173,6 @@
|
||||
<div><dt>Schlaf</dt><dd><?= e((string) $selectedEntry['sleep_hours']) ?> h</dd></div>
|
||||
<div><dt>Schlafgefühl</dt><dd><?= e((string) $selectedEntry['sleep_feeling']) ?>/5</dd></div>
|
||||
<div><dt>Sport</dt><dd><?= e((string) $selectedEntry['sport_minutes']) ?> min</dd></div>
|
||||
<div>
|
||||
<dt>Sportarten</dt>
|
||||
<dd>
|
||||
<?php if ((int) $selectedEntry['sport_minutes'] > 0 && !empty($selectedEntry['sport_type_meta'])): ?>
|
||||
<span class="sport-pill-group sport-pill-group--inline">
|
||||
<?php foreach ($selectedEntry['sport_type_meta'] as $sportType): ?>
|
||||
<span class="sport-pill sport-pill--inline">
|
||||
<img src="<?= e(sport_icon_path($sportType['icon'])) ?>" alt="">
|
||||
<span><?= e($sportType['label']) ?><?= !empty($sportType['location']) ? ' · ' . e(sport_location_label((string) $sportType['location'])) : '' ?></span>
|
||||
</span>
|
||||
<?php endforeach; ?>
|
||||
</span>
|
||||
<?php else: ?>
|
||||
keine
|
||||
<?php endif; ?>
|
||||
</dd>
|
||||
</div>
|
||||
<div><dt>Sportbonus</dt><dd><?= e(format_points((float) ($selectedEntry['evaluation']['components']['sport_bonus'] ?? 0))) ?></dd></div>
|
||||
<div><dt>Spaziergang</dt><dd><?= e(format_walk_value($selectedEntry)) ?></dd></div>
|
||||
<div><dt>Alkohol</dt><dd><?= !empty($selectedEntry['alcohol']) ? 'ja' : 'nein' ?></dd></div>
|
||||
</dl>
|
||||
@@ -220,13 +181,118 @@
|
||||
<h4>Notiz</h4>
|
||||
<p><?= nl2br(e($selectedEntry['note'] !== '' ? $selectedEntry['note'] : 'Keine Notiz hinterlegt.')) ?></p>
|
||||
</div>
|
||||
</article>
|
||||
<?php else: ?>
|
||||
<article class="glass-panel detail-card">
|
||||
<p class="eyebrow">Details</p>
|
||||
<h3>Archivansicht</h3>
|
||||
<p>Wähle links einen Tag oder eine KI-Zusammenfassung aus. Wochenrückblicke benötigen mindestens 3 Texteinträge, Monatsrückblicke mindestens 2 vorhandene KI-Wochenzusammenfassungen.</p>
|
||||
</article>
|
||||
<?php elseif ($detailType === 'week'): ?>
|
||||
<p class="hero-label"><?= e(format_compact_date((string) $selectedWeek['date_from'])) ?> bis <?= e(format_compact_date((string) $selectedWeek['date_to'])) ?></p>
|
||||
|
||||
<div class="archive-detail__status-row">
|
||||
<span class="status-badge status-badge--<?= e($selectedWeek['status_tone']) ?>"><?= e($selectedWeek['status_label']) ?></span>
|
||||
<span class="chart-chip"><?= e($selectedWeek['trend_label']) ?></span>
|
||||
</div>
|
||||
|
||||
<dl class="detail-grid detail-grid--archive">
|
||||
<div><dt>Texteinträge</dt><dd><?= e((string) $selectedWeek['note_entries_count']) ?></dd></div>
|
||||
<div><dt>Getrackte Tage</dt><dd><?= e((string) $selectedWeek['tracked_days']) ?></dd></div>
|
||||
<?php if (!empty($selectedWeek['summary'])): ?>
|
||||
<div><dt>Erstellt am</dt><dd><?= e(format_compact_datetime((string) $selectedWeek['summary']['created_at'])) ?></dd></div>
|
||||
<?php endif; ?>
|
||||
</dl>
|
||||
|
||||
<div class="note-box archive-detail__status-note">
|
||||
<h4>KI-Status</h4>
|
||||
<p><?= e($selectedWeek['status_hint']) ?></p>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($selectedWeek['summary'])): ?>
|
||||
<div class="archive-detail__actions">
|
||||
<a class="ghost-link archive-action" href="<?= e($archiveUrl(['week' => $selectedWeek['summary_key'], 'date' => null, 'month_key' => null])) ?>">Öffnen</a>
|
||||
<form method="post" action="/archive">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="form_name" value="generate_weekly_summary">
|
||||
<input type="hidden" name="view" value="weeks">
|
||||
<input type="hidden" name="filter_month" value="<?= e($archiveFilterMonth) ?>">
|
||||
<input type="hidden" name="week_key" value="<?= e((string) $selectedWeek['summary_key']) ?>">
|
||||
<button class="ghost-button" type="submit" <?= empty($aiAvailable) ? 'disabled' : '' ?>>Neu generieren</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="note-box note-box--summary">
|
||||
<h4>KI-Wochenzusammenfassung</h4>
|
||||
<p><?= e((string) ($selectedWeek['summary']['text'] ?? '')) ?></p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<form method="post" action="/archive" class="archive-detail__single-action">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="form_name" value="generate_weekly_summary">
|
||||
<input type="hidden" name="view" value="weeks">
|
||||
<input type="hidden" name="filter_month" value="<?= e($archiveFilterMonth) ?>">
|
||||
<input type="hidden" name="week_key" value="<?= e((string) $selectedWeek['summary_key']) ?>">
|
||||
<button class="primary-button" type="submit" <?= !$selectedWeek['can_generate'] || empty($aiAvailable) ? 'disabled' : '' ?>>KI-Wochenzusammenfassung erzeugen</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<?php elseif ($detailType === 'month'): ?>
|
||||
<p class="hero-label"><?= e(format_compact_date((string) $selectedMonth['date_from'])) ?> bis <?= e(format_compact_date((string) $selectedMonth['date_to'])) ?></p>
|
||||
|
||||
<div class="archive-detail__status-row">
|
||||
<span class="status-badge status-badge--<?= e($selectedMonth['status_tone']) ?>"><?= e($selectedMonth['status_label']) ?></span>
|
||||
<span class="chart-chip"><?= e($selectedMonth['weekly_progress_label']) ?></span>
|
||||
</div>
|
||||
|
||||
<dl class="detail-grid detail-grid--archive">
|
||||
<div><dt>KI-Wochen vorhanden</dt><dd><?= e((string) $selectedMonth['weekly_summary_count']) ?> / <?= e((string) ((int) $selectedMonth['weekly_total_count'])) ?></dd></div>
|
||||
<?php if (!empty($selectedMonth['summary'])): ?>
|
||||
<div><dt>Erstellt am</dt><dd><?= e(format_compact_datetime((string) $selectedMonth['summary']['created_at'])) ?></dd></div>
|
||||
<?php endif; ?>
|
||||
</dl>
|
||||
|
||||
<div class="note-box archive-detail__status-note">
|
||||
<h4>Monatsstatus</h4>
|
||||
<p><?= e($selectedMonth['status_hint']) ?></p>
|
||||
</div>
|
||||
|
||||
<div class="note-box archive-detail__week-status">
|
||||
<h4>Wochen in diesem Monat</h4>
|
||||
<div class="archive-mini-list">
|
||||
<?php foreach ($selectedMonth['weeks'] as $week): ?>
|
||||
<div class="archive-mini-list__row">
|
||||
<span><?= e($week['label']) ?></span>
|
||||
<span class="status-badge status-badge--<?= !empty($week['has_summary']) ? 'ready' : 'blocked' ?>"><?= !empty($week['has_summary']) ? 'vorhanden' : 'fehlt' ?></span>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($selectedMonth['summary'])): ?>
|
||||
<div class="archive-detail__actions">
|
||||
<a class="ghost-link archive-action" href="<?= e($archiveUrl(['month_key' => $selectedMonth['summary_key'], 'date' => null, 'week' => null])) ?>">Öffnen</a>
|
||||
<form method="post" action="/archive">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="form_name" value="generate_monthly_summary">
|
||||
<input type="hidden" name="view" value="months">
|
||||
<input type="hidden" name="filter_month" value="<?= e($archiveFilterMonth) ?>">
|
||||
<input type="hidden" name="month_key" value="<?= e((string) $selectedMonth['summary_key']) ?>">
|
||||
<button class="ghost-button" type="submit" <?= empty($aiAvailable) ? 'disabled' : '' ?>>Neu generieren</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="note-box note-box--summary">
|
||||
<h4>KI-Monatszusammenfassung</h4>
|
||||
<p><?= e((string) ($selectedMonth['summary']['text'] ?? '')) ?></p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<form method="post" action="/archive" class="archive-detail__single-action">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="form_name" value="generate_monthly_summary">
|
||||
<input type="hidden" name="view" value="months">
|
||||
<input type="hidden" name="filter_month" value="<?= e($archiveFilterMonth) ?>">
|
||||
<input type="hidden" name="month_key" value="<?= e((string) $selectedMonth['summary_key']) ?>">
|
||||
<button class="primary-button" type="submit" <?= !$selectedMonth['can_generate'] || empty($aiAvailable) ? 'disabled' : '' ?>>KI-Monatszusammenfassung erzeugen</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<p class="helper-text">Wähle links einen Tag, eine Woche oder einen Monat aus.</p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user