Compare commits
2 Commits
297f63c7d5
...
e953d0fd42
| Author | SHA1 | Date | |
|---|---|---|---|
| e953d0fd42 | |||
| ab1d8bc677 |
@@ -48,6 +48,30 @@
|
|||||||
--control-soft-bg: rgba(255, 255, 255, 0.08);
|
--control-soft-bg: rgba(255, 255, 255, 0.08);
|
||||||
--control-soft-border: rgba(255, 255, 255, 0.16);
|
--control-soft-border: rgba(255, 255, 255, 0.16);
|
||||||
--brand-shadow: 0 10px 24px rgba(9, 25, 40, 0.22);
|
--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) {
|
@media (prefers-color-scheme: light) {
|
||||||
@@ -95,6 +119,30 @@
|
|||||||
--control-soft-bg: rgba(255, 255, 255, 0.58);
|
--control-soft-bg: rgba(255, 255, 255, 0.58);
|
||||||
--control-soft-border: rgba(123, 153, 182, 0.22);
|
--control-soft-border: rgba(123, 153, 182, 0.22);
|
||||||
--brand-shadow: 0 10px 24px rgba(82, 111, 138, 0.14);
|
--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 {
|
html {
|
||||||
@@ -104,6 +152,31 @@
|
|||||||
select {
|
select {
|
||||||
color-scheme: light;
|
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;
|
gap: 0.7rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sport-choice-list--single {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
.sport-choice {
|
.sport-choice {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@@ -890,6 +967,11 @@ button:disabled {
|
|||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sport-choice__card--toggle {
|
||||||
|
min-height: 100%;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.sport-choice input:checked + .sport-choice__card {
|
.sport-choice input:checked + .sport-choice__card {
|
||||||
border-color: rgba(139, 228, 255, 0.44);
|
border-color: rgba(139, 228, 255, 0.44);
|
||||||
background: linear-gradient(180deg, rgba(96, 184, 255, 0.18), rgba(255, 255, 255, 0.08));
|
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;
|
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 {
|
.archive-summary-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
@@ -1606,6 +2081,75 @@ input[type="range"] {
|
|||||||
grid-template-columns: 1fr;
|
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 {
|
.calendar-detail {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
@@ -1680,4 +2224,19 @@ input[type="range"] {
|
|||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 0.4rem;
|
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) {
|
function sleepDurationPoints(hours, points) {
|
||||||
if (hours < 4) {
|
if (hours < 4) {
|
||||||
return Number(points.lt4 || 0);
|
return Number(points.lt4 || 0);
|
||||||
@@ -542,8 +561,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function calendarColor(entry) {
|
function calendarColor(entry) {
|
||||||
|
const isLightMode = window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches;
|
||||||
|
|
||||||
if (!entry) {
|
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)));
|
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 yOffset = config.yOffset;
|
||||||
const gridHeight = (7 * cellSize) + (6 * verticalGap);
|
const gridHeight = (7 * cellSize) + (6 * verticalGap);
|
||||||
const height = yOffset + gridHeight + 8;
|
const height = yOffset + gridHeight + 8;
|
||||||
|
const sundayLabelY = yOffset + (6 * (cellSize + verticalGap)) + (cellSize * 0.78);
|
||||||
const rightPadding = 4;
|
const rightPadding = 4;
|
||||||
const naturalWidth = xOffset + (totalWeeks * cellSize) + ((totalWeeks - 1) * baseCellGap) + rightPadding;
|
const naturalWidth = xOffset + (totalWeeks * cellSize) + ((totalWeeks - 1) * baseCellGap) + rightPadding;
|
||||||
const availableWidth = Math.floor(container.clientWidth || 0);
|
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="34">Mo</text>
|
||||||
<text class="calendar-tooltip" x="0" y="68">Mi</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="102">Fr</text>
|
||||||
|
<text class="calendar-tooltip" x="0" y="${sundayLabelY}">So</text>
|
||||||
${cells}
|
${cells}
|
||||||
</svg>
|
</svg>
|
||||||
<div class="calendar-legend">
|
<div class="calendar-legend">
|
||||||
@@ -1273,6 +1298,7 @@
|
|||||||
updateRangeOutputs();
|
updateRangeOutputs();
|
||||||
initHeaderDatePicker();
|
initHeaderDatePicker();
|
||||||
initTrackPreview();
|
initTrackPreview();
|
||||||
|
initArchiveMobileDetail();
|
||||||
initDashboardCharts();
|
initDashboardCharts();
|
||||||
initSportTypeManager();
|
initSportTypeManager();
|
||||||
initPwaShell();
|
initPwaShell();
|
||||||
|
|||||||
+222
-21
@@ -345,17 +345,47 @@ final class App
|
|||||||
{
|
{
|
||||||
$user = $this->requireUser();
|
$user = $this->requireUser();
|
||||||
$settings = $this->hydrateSettings($this->settings->forUser($user['username']));
|
$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;
|
$selectedDate = isset($_GET['date']) ? (string) $_GET['date'] : null;
|
||||||
$selectedSummaryKind = isset($_GET['summary_kind']) ? (string) $_GET['summary_kind'] : null;
|
$selectedWeekKey = isset($_GET['week'] ) ? trim((string) $_GET['week']) : null;
|
||||||
$selectedSummaryKey = isset($_GET['summary_key']) ? (string) $_GET['summary_key'] : null;
|
$selectedMonthKey = isset($_GET['month_key']) ? trim((string) $_GET['month_key']) : null;
|
||||||
$entries = $this->entries->all($user['username']);
|
$entries = $this->entries->all($user['username']);
|
||||||
$archive = array_reverse($this->evaluateEntriesWithContext($entries, $settings));
|
$archive = array_reverse($this->evaluateEntriesWithContext($entries, $settings));
|
||||||
$weeklySummaries = $this->summaries->weekly($user['username']);
|
$weeklySummaries = $this->summaries->weekly($user['username']);
|
||||||
$monthlySummaries = $this->summaries->monthly($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;
|
$selectedEntry = null;
|
||||||
if ($selectedDate !== null) {
|
if ($view === 'days' && $selectedDate !== null) {
|
||||||
foreach ($archive as $entry) {
|
foreach ($filteredDays as $entry) {
|
||||||
if ($entry['date'] === $selectedDate) {
|
if ($entry['date'] === $selectedDate) {
|
||||||
$selectedEntry = $entry;
|
$selectedEntry = $entry;
|
||||||
break;
|
break;
|
||||||
@@ -363,21 +393,40 @@ final class App
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$selectedSummary = null;
|
$selectedWeek = null;
|
||||||
if ($selectedSummaryKind !== null && $selectedSummaryKey !== null) {
|
if ($view === 'weeks' && $selectedWeekKey !== null) {
|
||||||
$selectedSummary = $this->summaries->find($user['username'], $selectedSummaryKind, $selectedSummaryKey);
|
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', [
|
View::render('archive', [
|
||||||
'pageTitle' => 'Archiv',
|
'pageTitle' => 'Archiv',
|
||||||
'page' => 'archive',
|
'page' => 'archive',
|
||||||
'authUser' => $user,
|
'authUser' => $user,
|
||||||
'entries' => $archive,
|
'entries' => $filteredDays,
|
||||||
'selectedEntry' => $selectedEntry,
|
'selectedEntry' => $selectedEntry,
|
||||||
'selectedSummary' => $selectedSummary,
|
'selectedWeek' => $selectedWeek,
|
||||||
|
'selectedMonth' => $selectedMonth,
|
||||||
'settings' => $settings,
|
'settings' => $settings,
|
||||||
'weeklyArchive' => $this->buildWeeklyArchiveCards($archive, $weeklySummaries),
|
'archiveView' => $view,
|
||||||
'monthlyArchive' => $this->buildMonthlyArchiveCards($archive, $weeklySummaries, $monthlySummaries),
|
'archiveFilterMonth' => $filterMonth,
|
||||||
|
'archiveMonthOptions' => $monthOptions,
|
||||||
|
'weeklyArchive' => $filteredWeeks,
|
||||||
|
'monthlyArchive' => $filteredMonths,
|
||||||
'aiAvailable' => $this->openAi->isAvailable(),
|
'aiAvailable' => $this->openAi->isAvailable(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -390,6 +439,11 @@ final class App
|
|||||||
$settings = $this->hydrateSettings($this->settings->forUser($user['username']));
|
$settings = $this->hydrateSettings($this->settings->forUser($user['username']));
|
||||||
$entries = $this->evaluateEntriesWithContext($this->entries->all($user['username']), $settings);
|
$entries = $this->evaluateEntriesWithContext($this->entries->all($user['username']), $settings);
|
||||||
$form = (string) ($_POST['form_name'] ?? '');
|
$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 {
|
try {
|
||||||
if ($form === 'generate_weekly_summary') {
|
if ($form === 'generate_weekly_summary') {
|
||||||
@@ -406,7 +460,11 @@ final class App
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
flash('success', 'Die KI-Wochenzusammenfassung wurde erstellt.');
|
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') {
|
if ($form === 'generate_monthly_summary') {
|
||||||
@@ -425,14 +483,24 @@ final class App
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
flash('success', 'Die KI-Monatszusammenfassung wurde erstellt.');
|
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) {
|
} catch (RuntimeException $exception) {
|
||||||
flash('error', $exception->getMessage());
|
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
|
private function showOptions(): void
|
||||||
@@ -1107,6 +1175,21 @@ final class App
|
|||||||
$weekEntries,
|
$weekEntries,
|
||||||
static fn (array $entry): bool => trim((string) ($entry['note'] ?? '')) !== ''
|
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[] = [
|
$cards[] = [
|
||||||
'summary_key' => $key,
|
'summary_key' => $key,
|
||||||
@@ -1115,9 +1198,13 @@ final class App
|
|||||||
'date_to' => $summary['date_to'] ?? $range['date_to'],
|
'date_to' => $summary['date_to'] ?? $range['date_to'],
|
||||||
'tracked_days' => count($weekEntries),
|
'tracked_days' => count($weekEntries),
|
||||||
'note_entries_count' => count($noteEntries),
|
'note_entries_count' => count($noteEntries),
|
||||||
'can_generate' => count($noteEntries) >= 3,
|
'can_generate' => $canGenerate,
|
||||||
'summary' => $summary,
|
'summary' => $summary,
|
||||||
'has_summary' => $summary !== null,
|
'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;
|
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 = [];
|
$monthKeys = [];
|
||||||
|
|
||||||
@@ -1163,8 +1250,32 @@ final class App
|
|||||||
$weeklySummaries,
|
$weeklySummaries,
|
||||||
fn (array $summary): bool => $this->summaryOverlapsMonth($summary, $monthKey)
|
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($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[] = [
|
$cards[] = [
|
||||||
'summary_key' => $monthKey,
|
'summary_key' => $monthKey,
|
||||||
@@ -1172,10 +1283,22 @@ final class App
|
|||||||
'date_from' => $range['date_from'],
|
'date_from' => $range['date_from'],
|
||||||
'date_to' => $range['date_to'],
|
'date_to' => $range['date_to'],
|
||||||
'tracked_days' => count($monthEntries),
|
'tracked_days' => count($monthEntries),
|
||||||
'weekly_summary_count' => count($monthWeeklySummaries),
|
'weekly_summary_count' => $availableWeeklyCount,
|
||||||
'can_generate' => count($monthWeeklySummaries) >= 2,
|
'weekly_total_count' => $totalWeekCount,
|
||||||
'summary' => $monthlySummaryMap[$monthKey] ?? null,
|
'weekly_progress_label' => $availableWeeklyCount . ' von ' . ($totalWeekCount > 0 ? $totalWeekCount : 0) . ' Wochen mit KI',
|
||||||
'has_summary' => isset($monthlySummaryMap[$monthKey]),
|
'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'];
|
&& $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
|
private function sendSecurityHeaders(): void
|
||||||
{
|
{
|
||||||
header('Referrer-Policy: strict-origin-when-cross-origin');
|
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;
|
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
|
function format_display_datetime(string $value): string
|
||||||
{
|
{
|
||||||
try {
|
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');
|
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
|
function iso_week_key(string $date): string
|
||||||
{
|
{
|
||||||
$current = DateTimeImmutable::createFromFormat('Y-m-d', $date);
|
$current = DateTimeImmutable::createFromFormat('Y-m-d', $date);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ declare(strict_types=1);
|
|||||||
$brandSubtitle = match ($page) {
|
$brandSubtitle = match ($page) {
|
||||||
'dashboard' => 'Statistiken und Verlauf',
|
'dashboard' => 'Statistiken und Verlauf',
|
||||||
'track' => 'Tag erfassen und bewerten',
|
'track' => 'Tag erfassen und bewerten',
|
||||||
'archive' => 'Rückblick auf vergangene Tage',
|
'archive' => '',
|
||||||
'options' => 'Logik, Erinnerungen, Sicherheit und Accounts',
|
'options' => 'Logik, Erinnerungen, Sicherheit und Accounts',
|
||||||
'login' => 'Geschützter Zugang',
|
'login' => 'Geschützter Zugang',
|
||||||
'setup' => 'Erstkonfiguration',
|
'setup' => 'Erstkonfiguration',
|
||||||
@@ -36,7 +36,7 @@ $brandSubtitle = match ($page) {
|
|||||||
<link rel="stylesheet" href="/assets/css/app.css">
|
<link rel="stylesheet" href="/assets/css/app.css">
|
||||||
<script defer src="/assets/js/app.js"></script>
|
<script defer src="/assets/js/app.js"></script>
|
||||||
</head>
|
</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-one"></div>
|
||||||
<div class="aurora aurora-two"></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>
|
<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): ?>
|
<?php if ($authUser !== null): ?>
|
||||||
<header class="topbar glass-panel">
|
<header class="topbar glass-panel">
|
||||||
<div>
|
<div>
|
||||||
|
<?php if ($brandSubtitle !== ''): ?>
|
||||||
<p class="eyebrow"><?= e($brandSubtitle) ?></p>
|
<p class="eyebrow"><?= e($brandSubtitle) ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
<h2><?= e($pageTitle) ?></h2>
|
<h2><?= e($pageTitle) ?></h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="topbar__meta">
|
<div class="topbar__meta">
|
||||||
|
|||||||
+250
-184
@@ -1,190 +1,169 @@
|
|||||||
<section class="page-grid">
|
<?php
|
||||||
<article class="glass-panel archive-list">
|
$baseParams = ['view' => $archiveView];
|
||||||
<div class="section-head">
|
if ($archiveFilterMonth !== '') {
|
||||||
<div>
|
$baseParams['filter_month'] = $archiveFilterMonth;
|
||||||
<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>
|
|
||||||
|
|
||||||
<section class="archive-summary-section">
|
$archiveUrl = static function (array $params = []) use ($baseParams): string {
|
||||||
<div class="section-head section-head--compact">
|
$query = array_filter(array_merge($baseParams, $params), static fn (mixed $value): bool => $value !== null && $value !== '');
|
||||||
<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>
|
|
||||||
|
|
||||||
<?php if ($monthlyArchive === []): ?>
|
return $query === [] ? '/archive' : '/archive?' . http_build_query($query);
|
||||||
<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>
|
|
||||||
|
|
||||||
<p class="helper-text"><?= e($month['date_from']) ?> bis <?= e($month['date_to']) ?></p>
|
$detailType = $selectedEntry !== null
|
||||||
<p class="helper-text"><?= e((string) $month['weekly_summary_count']) ?> KI-Wochenzusammenfassungen im Monat verfügbar</p>
|
? 'day'
|
||||||
|
: ($selectedWeek !== null
|
||||||
|
? 'week'
|
||||||
|
: ($selectedMonth !== null ? 'month' : null));
|
||||||
|
|
||||||
<?php if (!empty($month['summary'])): ?>
|
$detailOpen = $detailType !== null;
|
||||||
<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; ?>
|
|
||||||
|
|
||||||
<div class="archive-item__actions archive-item__actions--stack">
|
<section class="archive-page">
|
||||||
<?php if (!empty($month['summary'])): ?>
|
<article class="glass-panel archive-shell">
|
||||||
<a class="ghost-link archive-action" href="/archive?summary_kind=monthly&summary_key=<?= e(rawurlencode((string) $month['summary_key'])) ?>">Öffnen</a>
|
<div class="archive-toolbar archive-toolbar--compact">
|
||||||
<?php endif; ?>
|
<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">
|
<form method="get" action="/archive" class="archive-filter">
|
||||||
<?= csrf_field() ?>
|
<input type="hidden" name="view" value="<?= e($archiveView) ?>">
|
||||||
<input type="hidden" name="form_name" value="generate_monthly_summary">
|
<label>
|
||||||
<input type="hidden" name="month_key" value="<?= e((string) $month['summary_key']) ?>">
|
<span>Zeitraum</span>
|
||||||
<button class="ghost-button ghost-button--small" type="submit" <?= !$month['can_generate'] || empty($aiAvailable) ? 'disabled' : '' ?>>
|
<select name="filter_month" onchange="this.form.submit()">
|
||||||
<?= !empty($month['has_summary']) ? 'Neu generieren' : 'KI-Monatszusammenfassung erzeugen' ?>
|
<option value="">Alle Monate</option>
|
||||||
</button>
|
<?php foreach ($archiveMonthOptions as $monthOption): ?>
|
||||||
|
<option value="<?= e($monthOption) ?>" <?= $archiveFilterMonth === $monthOption ? 'selected' : '' ?>><?= e(month_label($monthOption)) ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="archive-summary-section">
|
<div class="archive-workspace">
|
||||||
<div class="section-head section-head--compact">
|
<section class="archive-main">
|
||||||
<div>
|
<?php if ($archiveView === 'days'): ?>
|
||||||
<p class="eyebrow">KI</p>
|
<div class="archive-list-header">
|
||||||
<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>
|
<div>
|
||||||
<p class="eyebrow">Tage</p>
|
<p class="eyebrow">Tage</p>
|
||||||
<h4>Alle gespeicherten Tage</h4>
|
<h4>Gespeicherte Tage</h4>
|
||||||
</div>
|
</div>
|
||||||
|
<span class="chart-chip"><?= e((string) count($entries)) ?> Tage</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php if ($entries === []): ?>
|
<?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: ?>
|
<?php else: ?>
|
||||||
<div class="archive-items">
|
<div class="archive-rows">
|
||||||
<?php foreach ($entries as $entry): ?>
|
<?php foreach ($entries as $entry): ?>
|
||||||
<article class="archive-item <?= $selectedEntry !== null && $selectedEntry['date'] === $entry['date'] ? 'active' : '' ?>">
|
<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>
|
<div class="archive-row__main">
|
||||||
<strong><?= e(format_display_date($entry['date'], false)) ?></strong>
|
<strong><?= e(format_compact_date($entry['date'])) ?></strong>
|
||||||
<span><?= e($entry['evaluation']['label']) ?></span>
|
<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>
|
||||||
<div class="archive-item__meta">
|
<div class="archive-row__meta">
|
||||||
<span><?= e(format_points((float) $entry['evaluation']['total'])) ?></span>
|
<span><?= e(format_points((float) $entry['evaluation']['total'])) ?> Punkte</span>
|
||||||
<span>Stimmung <?= e((string) $entry['mood']) ?>/10</span>
|
<span>Stimmung <?= e((string) $entry['mood']) ?>/10</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="archive-item__actions">
|
<span class="archive-row__hint">Ansehen</span>
|
||||||
<a class="ghost-link archive-action" href="/archive?date=<?= e(rawurlencode($entry['date'])) ?>">Ansehen</a>
|
</a>
|
||||||
<a class="ghost-link archive-action" href="/track?date=<?= e(rawurlencode($entry['date'])) ?>">Bearbeiten</a>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</section>
|
<?php elseif ($archiveView === 'weeks'): ?>
|
||||||
</article>
|
<div class="archive-list-header">
|
||||||
|
<div>
|
||||||
<aside class="stack-column">
|
<p class="eyebrow">Wochen</p>
|
||||||
<?php if ($selectedSummary !== null): ?>
|
<h4>Wöchentliche KI-Rückblicke</h4>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</article>
|
<span class="chart-chip"><?= e((string) count($weeklyArchive)) ?> Wochen</span>
|
||||||
<?php elseif ($selectedEntry !== null): ?>
|
</div>
|
||||||
<article class="glass-panel detail-card">
|
|
||||||
<p class="eyebrow">Ausgewählt</p>
|
<?php if ($weeklyArchive === []): ?>
|
||||||
<h3><?= e(format_display_date($selectedEntry['date'])) ?></h3>
|
<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>
|
<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>
|
<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>Stimmung</dt><dd><?= e((string) $selectedEntry['mood']) ?>/10</dd></div>
|
||||||
<div><dt>Energie</dt><dd><?= e((string) $selectedEntry['energy']) ?>/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>
|
<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>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>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>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>Spaziergang</dt><dd><?= e(format_walk_value($selectedEntry)) ?></dd></div>
|
||||||
<div><dt>Alkohol</dt><dd><?= !empty($selectedEntry['alcohol']) ? 'ja' : 'nein' ?></dd></div>
|
<div><dt>Alkohol</dt><dd><?= !empty($selectedEntry['alcohol']) ? 'ja' : 'nein' ?></dd></div>
|
||||||
</dl>
|
</dl>
|
||||||
@@ -220,13 +181,118 @@
|
|||||||
<h4>Notiz</h4>
|
<h4>Notiz</h4>
|
||||||
<p><?= nl2br(e($selectedEntry['note'] !== '' ? $selectedEntry['note'] : 'Keine Notiz hinterlegt.')) ?></p>
|
<p><?= nl2br(e($selectedEntry['note'] !== '' ? $selectedEntry['note'] : 'Keine Notiz hinterlegt.')) ?></p>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
<?php elseif ($detailType === 'week'): ?>
|
||||||
<?php else: ?>
|
<p class="hero-label"><?= e(format_compact_date((string) $selectedWeek['date_from'])) ?> bis <?= e(format_compact_date((string) $selectedWeek['date_to'])) ?></p>
|
||||||
<article class="glass-panel detail-card">
|
|
||||||
<p class="eyebrow">Details</p>
|
<div class="archive-detail__status-row">
|
||||||
<h3>Archivansicht</h3>
|
<span class="status-badge status-badge--<?= e($selectedWeek['status_tone']) ?>"><?= e($selectedWeek['status_label']) ?></span>
|
||||||
<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>
|
<span class="chart-chip"><?= e($selectedWeek['trend_label']) ?></span>
|
||||||
</article>
|
</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; ?>
|
<?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>
|
</aside>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<h3><?= e(format_display_date($entry['date'])) ?></h3>
|
<h3><?= e(format_display_date($entry['date'])) ?></h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="section-head__actions">
|
<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>
|
<a class="ghost-link" href="/track?date=<?= e(today()) ?>">Heute</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -45,24 +45,32 @@
|
|||||||
<input type="range" min="1" max="10" step="1" name="pain" value="<?= e((string) $entry['pain']) ?>">
|
<input type="range" min="1" max="10" step="1" name="pain" value="<?= e((string) $entry['pain']) ?>">
|
||||||
</label>
|
</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' : '' ?>>
|
<input type="checkbox" name="alcohol" value="1" <?= !empty($entry['alcohol']) ? 'checked' : '' ?>>
|
||||||
<span>
|
<span class="sport-choice__card sport-choice__card--toggle">
|
||||||
<strong>Alkohol</strong>
|
<img src="<?= e(icon_path('alcohol')) ?>" alt="">
|
||||||
<small>Wenn du heute Alkohol getrunken hast, werden 5 Punkte abgezogen.</small>
|
<strong>Alkohol</strong>Heute was getrunken?
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<div class="field-grid field-grid--single">
|
<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' : '' ?>>
|
<input type="checkbox" name="alcohol" value="1" <?= !empty($entry['alcohol']) ? 'checked' : '' ?>>
|
||||||
<span>
|
<span class="sport-choice__card sport-choice__card--toggle">
|
||||||
<strong>Alkohol</strong>
|
<img src="<?= e(icon_path('alcohol')) ?>" alt="">
|
||||||
<small>Wenn du heute Alkohol getrunken hast, werden 5 Punkte abgezogen.</small>
|
<strong>Alkohol</strong>Heute was getrunken?
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="field-grid field-grid--two">
|
<div class="field-grid field-grid--two">
|
||||||
|
|||||||
Reference in New Issue
Block a user