Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e953d0fd42 | |||
| ab1d8bc677 |
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M23 11C23.4 9.8 24.5 9 25.8 9H38.2C39.5 9 40.6 9.8 41 11L44.4 22.7C44.8 24.2 45 25.8 45 27.3C45 33.9 39.8 39.3 33.3 39.8V51H39.5C40.9 51 42 52.1 42 53.5C42 54.9 40.9 56 39.5 56H24.5C23.1 56 22 54.9 22 53.5C22 52.1 23.1 51 24.5 51H30.7V39.8C24.2 39.3 19 33.9 19 27.3C19 25.8 19.2 24.2 19.6 22.7L23 11Z" fill="#EFF7FF"/>
|
||||
<path d="M22.4 20H41.6L41.2 22C41 22.9 41 23.8 41 24.7C41 29.8 36.8 34 31.7 34H32.3C37.2 34 41.2 30 41.2 25.1C41.2 24 41.1 23 40.8 22L40.3 20H22.4Z" fill="#8BE4FF" opacity="0.95"/>
|
||||
<path d="M22 21.5C22 20.7 22.7 20 23.5 20H40.5C41.3 20 42 20.7 42 21.5C42 27.3 37.3 32 31.5 32C25.7 32 21 27.3 21 21.5H22Z" fill="#7FF3BB" opacity="0.8"/>
|
||||
<path d="M24 15H40" stroke="#8BE4FF" stroke-width="2.8" stroke-linecap="round"/>
|
||||
<path d="M27.5 44H36.5" stroke="#8BE4FF" stroke-width="3" stroke-linecap="round"/>
|
||||
<path d="M25 53H39" stroke="#7FF3BB" stroke-width="3.2" stroke-linecap="round" opacity="0.9"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
+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>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<h3><?= e(format_display_date($entry['date'])) ?></h3>
|
||||
</div>
|
||||
<div class="section-head__actions">
|
||||
<a class="ghost-link" href="/archive?date=<?= e(rawurlencode($entry['date'])) ?>">Im Archiv ansehen</a>
|
||||
<a class="ghost-link" href="/archive?view=days&date=<?= e(rawurlencode($entry['date'])) ?>">Im Archiv ansehen</a>
|
||||
<a class="ghost-link" href="/track?date=<?= e(today()) ?>">Heute</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -45,24 +45,32 @@
|
||||
<input type="range" min="1" max="10" step="1" name="pain" value="<?= e((string) $entry['pain']) ?>">
|
||||
</label>
|
||||
|
||||
<label class="checkbox-row checkbox-row--panel checkbox-row--tall">
|
||||
<div class="sport-choice-field sport-choice-field--single">
|
||||
<div class="sport-choice-list sport-choice-list--single">
|
||||
<label class="sport-choice">
|
||||
<input type="checkbox" name="alcohol" value="1" <?= !empty($entry['alcohol']) ? 'checked' : '' ?>>
|
||||
<span>
|
||||
<strong>Alkohol</strong>
|
||||
<small>Wenn du heute Alkohol getrunken hast, werden 5 Punkte abgezogen.</small>
|
||||
<span class="sport-choice__card sport-choice__card--toggle">
|
||||
<img src="<?= e(icon_path('alcohol')) ?>" alt="">
|
||||
<strong>Alkohol</strong>Heute was getrunken?
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="field-grid field-grid--single">
|
||||
<label class="checkbox-row checkbox-row--panel">
|
||||
<div class="sport-choice-field sport-choice-field--single">
|
||||
<div class="sport-choice-list sport-choice-list--single">
|
||||
<label class="sport-choice">
|
||||
<input type="checkbox" name="alcohol" value="1" <?= !empty($entry['alcohol']) ? 'checked' : '' ?>>
|
||||
<span>
|
||||
<strong>Alkohol</strong>
|
||||
<small>Wenn du heute Alkohol getrunken hast, werden 5 Punkte abgezogen.</small>
|
||||
<span class="sport-choice__card sport-choice__card--toggle">
|
||||
<img src="<?= e(icon_path('alcohol')) ?>" alt="">
|
||||
<strong>Alkohol</strong>Heute was getrunken?
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="field-grid field-grid--two">
|
||||
|
||||
Reference in New Issue
Block a user