Compare commits
2 Commits
V1.2.2
...
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>
|
||||||
<p class="eyebrow"><?= e($brandSubtitle) ?></p>
|
<?php if ($brandSubtitle !== ''): ?>
|
||||||
|
<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">
|
||||||
|
|||||||
+273
-207
@@ -1,232 +1,298 @@
|
|||||||
<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>
|
$archiveUrl = static function (array $params = []) use ($baseParams): string {
|
||||||
<span class="chart-chip"><?= e((string) count($entries)) ?> Einträge</span>
|
$query = array_filter(array_merge($baseParams, $params), static fn (mixed $value): bool => $value !== null && $value !== '');
|
||||||
|
|
||||||
|
return $query === [] ? '/archive' : '/archive?' . http_build_query($query);
|
||||||
|
};
|
||||||
|
|
||||||
|
$detailType = $selectedEntry !== null
|
||||||
|
? 'day'
|
||||||
|
: ($selectedWeek !== null
|
||||||
|
? 'week'
|
||||||
|
: ($selectedMonth !== null ? 'month' : null));
|
||||||
|
|
||||||
|
$detailOpen = $detailType !== null;
|
||||||
|
?>
|
||||||
|
|
||||||
|
<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="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>
|
</div>
|
||||||
|
|
||||||
<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>Monatszusammenfassungen</h4>
|
<div>
|
||||||
</div>
|
<p class="eyebrow">Tage</p>
|
||||||
<?php if (empty($aiAvailable)): ?>
|
<h4>Gespeicherte Tage</h4>
|
||||||
<span class="chart-chip chart-chip--muted">API nicht bereit</span>
|
</div>
|
||||||
|
<span class="chart-chip"><?= e((string) count($entries)) ?> Tage</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($entries === []): ?>
|
||||||
|
<p class="empty-state">Für diesen Zeitraum gibt es noch keine getrackten Tage.</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="archive-rows">
|
||||||
|
<?php foreach ($entries as $entry): ?>
|
||||||
|
<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>
|
||||||
|
</div>
|
||||||
|
<div class="archive-row__meta">
|
||||||
|
<span><?= e(format_points((float) $entry['evaluation']['total'])) ?> Punkte</span>
|
||||||
|
<span>Stimmung <?= e((string) $entry['mood']) ?>/10</span>
|
||||||
|
</div>
|
||||||
|
<span class="archive-row__hint">Ansehen</span>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php elseif ($archiveView === 'weeks'): ?>
|
||||||
|
<div class="archive-list-header">
|
||||||
|
<div>
|
||||||
|
<p class="eyebrow">Wochen</p>
|
||||||
|
<h4>Wöchentliche KI-Rückblicke</h4>
|
||||||
|
</div>
|
||||||
|
<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; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</section>
|
||||||
|
|
||||||
<?php if ($monthlyArchive === []): ?>
|
<aside class="archive-detail <?= $detailOpen ? 'is-open' : '' ?>" id="archive-detail-panel" data-detail-open="<?= $detailOpen ? '1' : '0' ?>">
|
||||||
<p class="empty-state">Sobald genügend Wochenzusammenfassungen vorliegen, erscheinen hier die Monatsrückblicke.</p>
|
<div class="glass-panel archive-detail__panel">
|
||||||
<?php else: ?>
|
<div class="archive-detail__top">
|
||||||
<div class="archive-summary-grid">
|
<div>
|
||||||
<?php foreach ($monthlyArchive as $month): ?>
|
<p class="eyebrow">Details</p>
|
||||||
<article class="archive-summary-card">
|
<?php if ($detailType === 'day'): ?>
|
||||||
<div class="archive-summary-card__head">
|
<h3><?= e(format_compact_date($selectedEntry['date'])) ?></h3>
|
||||||
<div>
|
<?php elseif ($detailType === 'week'): ?>
|
||||||
<span class="summary-badge">KI</span>
|
<h3><?= e($selectedWeek['label']) ?></h3>
|
||||||
<strong><?= e($month['label']) ?></strong>
|
<?php elseif ($detailType === 'month'): ?>
|
||||||
</div>
|
<h3><?= e($selectedMonth['label']) ?></h3>
|
||||||
<?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>
|
|
||||||
<p class="helper-text"><?= e((string) $month['weekly_summary_count']) ?> KI-Wochenzusammenfassungen im Monat verfügbar</p>
|
|
||||||
|
|
||||||
<?php if (!empty($month['summary'])): ?>
|
|
||||||
<p class="helper-text">Erstellt am <?= e(format_display_datetime((string) $month['summary']['created_at'])) ?></p>
|
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<p class="helper-text">Mindestens 2 KI-Wochenzusammenfassungen nötig.</p>
|
<h3>Archivansicht</h3>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="archive-item__actions archive-item__actions--stack">
|
<?php if ($detailOpen): ?>
|
||||||
<?php if (!empty($month['summary'])): ?>
|
<a class="ghost-link archive-detail__close" href="<?= e($archiveUrl(['date' => null, 'week' => null, 'month_key' => null])) ?>">Schließen</a>
|
||||||
<a class="ghost-link archive-action" href="/archive?summary_kind=monthly&summary_key=<?= e(rawurlencode((string) $month['summary_key'])) ?>">Öffnen</a>
|
<?php endif; ?>
|
||||||
<?php endif; ?>
|
</div>
|
||||||
|
|
||||||
<form method="post" action="/archive">
|
<?php if ($detailType === 'day'): ?>
|
||||||
<?= csrf_field() ?>
|
<p class="hero-label"><?= e($selectedEntry['evaluation']['label']) ?> · <?= e(format_points((float) $selectedEntry['evaluation']['total'])) ?> Punkte</p>
|
||||||
<input type="hidden" name="form_name" value="generate_monthly_summary">
|
<a class="primary-button button-link" href="/track?date=<?= e(rawurlencode($selectedEntry['date'])) ?>">Diesen Tag bearbeiten</a>
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="archive-summary-section">
|
<dl class="detail-grid detail-grid--archive-day">
|
||||||
<div class="section-head section-head--compact">
|
<div><dt>Stimmung</dt><dd><?= e((string) $selectedEntry['mood']) ?>/10</dd></div>
|
||||||
<div>
|
<div><dt>Energie</dt><dd><?= e((string) $selectedEntry['energy']) ?>/10</dd></div>
|
||||||
<p class="eyebrow">KI</p>
|
<div><dt>Stress</dt><dd><?= e((string) $selectedEntry['stress']) ?>/10</dd></div>
|
||||||
<h4>Wochenzusammenfassungen</h4>
|
<?php if (!empty($settings['tracking']['pain_enabled'])): ?>
|
||||||
</div>
|
<div><dt>Schmerzen</dt><dd><?= e((string) $selectedEntry['pain']) ?>/10</dd></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; ?>
|
<?php endif; ?>
|
||||||
|
<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>Spaziergang</dt><dd><?= e(format_walk_value($selectedEntry)) ?></dd></div>
|
||||||
|
<div><dt>Alkohol</dt><dd><?= !empty($selectedEntry['alcohol']) ? 'ja' : 'nein' ?></dd></div>
|
||||||
|
</dl>
|
||||||
|
|
||||||
<div class="archive-item__actions archive-item__actions--stack">
|
<div class="note-box">
|
||||||
<?php if (!empty($week['summary'])): ?>
|
<h4>Notiz</h4>
|
||||||
<a class="ghost-link archive-action" href="/archive?summary_kind=weekly&summary_key=<?= e(rawurlencode((string) $week['summary_key'])) ?>">Öffnen</a>
|
<p><?= nl2br(e($selectedEntry['note'] !== '' ? $selectedEntry['note'] : 'Keine Notiz hinterlegt.')) ?></p>
|
||||||
<?php endif; ?>
|
</div>
|
||||||
|
<?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">
|
<form method="post" action="/archive">
|
||||||
<?= csrf_field() ?>
|
<?= csrf_field() ?>
|
||||||
<input type="hidden" name="form_name" value="generate_weekly_summary">
|
<input type="hidden" name="form_name" value="generate_weekly_summary">
|
||||||
<input type="hidden" name="week_key" value="<?= e((string) $week['summary_key']) ?>">
|
<input type="hidden" name="view" value="weeks">
|
||||||
<button class="ghost-button ghost-button--small" type="submit" <?= !$week['can_generate'] || empty($aiAvailable) ? 'disabled' : '' ?>>
|
<input type="hidden" name="filter_month" value="<?= e($archiveFilterMonth) ?>">
|
||||||
<?= !empty($week['has_summary']) ? 'Neu generieren' : 'KI-Wochenzusammenfassung erzeugen' ?>
|
<input type="hidden" name="week_key" value="<?= e((string) $selectedWeek['summary_key']) ?>">
|
||||||
</button>
|
<button class="ghost-button" type="submit" <?= empty($aiAvailable) ? 'disabled' : '' ?>>Neu generieren</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="archive-summary-section">
|
<div class="note-box note-box--summary">
|
||||||
<div class="section-head section-head--compact">
|
<h4>KI-Wochenzusammenfassung</h4>
|
||||||
<div>
|
<p><?= e((string) ($selectedWeek['summary']['text'] ?? '')) ?></p>
|
||||||
<p class="eyebrow">Tage</p>
|
|
||||||
<h4>Alle gespeicherten Tage</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<?php if ($entries === []): ?>
|
|
||||||
<p class="empty-state">Noch keine Einträge vorhanden. Auf der Tracking-Seite kannst du den ersten Tag anlegen.</p>
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="archive-items">
|
|
||||||
<?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>
|
|
||||||
<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">
|
<?php else: ?>
|
||||||
<span><?= e(format_points((float) $entry['evaluation']['total'])) ?></span>
|
<form method="post" action="/archive" class="archive-detail__single-action">
|
||||||
<span>Stimmung <?= e((string) $entry['mood']) ?>/10</span>
|
<?= csrf_field() ?>
|
||||||
</div>
|
<input type="hidden" name="form_name" value="generate_weekly_summary">
|
||||||
<div class="archive-item__actions">
|
<input type="hidden" name="view" value="weeks">
|
||||||
<a class="ghost-link archive-action" href="/archive?date=<?= e(rawurlencode($entry['date'])) ?>">Ansehen</a>
|
<input type="hidden" name="filter_month" value="<?= e($archiveFilterMonth) ?>">
|
||||||
<a class="ghost-link archive-action" href="/track?date=<?= e(rawurlencode($entry['date'])) ?>">Bearbeiten</a>
|
<input type="hidden" name="week_key" value="<?= e((string) $selectedWeek['summary_key']) ?>">
|
||||||
</div>
|
<button class="primary-button" type="submit" <?= !$selectedWeek['can_generate'] || empty($aiAvailable) ? 'disabled' : '' ?>>KI-Wochenzusammenfassung erzeugen</button>
|
||||||
</article>
|
</form>
|
||||||
<?php endforeach; ?>
|
<?php endif; ?>
|
||||||
</div>
|
<?php elseif ($detailType === 'month'): ?>
|
||||||
<?php endif; ?>
|
<p class="hero-label"><?= e(format_compact_date((string) $selectedMonth['date_from'])) ?> bis <?= e(format_compact_date((string) $selectedMonth['date_to'])) ?></p>
|
||||||
</section>
|
|
||||||
</article>
|
|
||||||
|
|
||||||
<aside class="stack-column">
|
<div class="archive-detail__status-row">
|
||||||
<?php if ($selectedSummary !== null): ?>
|
<span class="status-badge status-badge--<?= e($selectedMonth['status_tone']) ?>"><?= e($selectedMonth['status_label']) ?></span>
|
||||||
<article class="glass-panel detail-card">
|
<span class="chart-chip"><?= e($selectedMonth['weekly_progress_label']) ?></span>
|
||||||
<p class="eyebrow">KI-Zusammenfassung</p>
|
</div>
|
||||||
<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">
|
<dl class="detail-grid detail-grid--archive">
|
||||||
<h4>Text</h4>
|
<div><dt>KI-Wochen vorhanden</dt><dd><?= e((string) $selectedMonth['weekly_summary_count']) ?> / <?= e((string) ((int) $selectedMonth['weekly_total_count'])) ?></dd></div>
|
||||||
<p><?= e($selectedSummary['text']) ?></p>
|
<?php if (!empty($selectedMonth['summary'])): ?>
|
||||||
</div>
|
<div><dt>Erstellt am</dt><dd><?= e(format_compact_datetime((string) $selectedMonth['summary']['created_at'])) ?></dd></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>
|
|
||||||
<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">
|
|
||||||
<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>
|
|
||||||
<?php if (!empty($settings['tracking']['pain_enabled'])): ?>
|
|
||||||
<div><dt>Schmerzen</dt><dd><?= e((string) $selectedEntry['pain']) ?>/10</dd></div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<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; ?>
|
<?php endif; ?>
|
||||||
</dd>
|
</dl>
|
||||||
</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>
|
|
||||||
|
|
||||||
<div class="note-box">
|
<div class="note-box archive-detail__status-note">
|
||||||
<h4>Notiz</h4>
|
<h4>Monatsstatus</h4>
|
||||||
<p><?= nl2br(e($selectedEntry['note'] !== '' ? $selectedEntry['note'] : 'Keine Notiz hinterlegt.')) ?></p>
|
<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>
|
</div>
|
||||||
</article>
|
</aside>
|
||||||
<?php else: ?>
|
</div>
|
||||||
<article class="glass-panel detail-card">
|
</article>
|
||||||
<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 endif; ?>
|
|
||||||
</aside>
|
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
+23
-15
@@ -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,23 +45,31 @@
|
|||||||
<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">
|
||||||
<input type="checkbox" name="alcohol" value="1" <?= !empty($entry['alcohol']) ? 'checked' : '' ?>>
|
<div class="sport-choice-list sport-choice-list--single">
|
||||||
<span>
|
<label class="sport-choice">
|
||||||
<strong>Alkohol</strong>
|
<input type="checkbox" name="alcohol" value="1" <?= !empty($entry['alcohol']) ? 'checked' : '' ?>>
|
||||||
<small>Wenn du heute Alkohol getrunken hast, werden 5 Punkte abgezogen.</small>
|
<span class="sport-choice__card sport-choice__card--toggle">
|
||||||
</span>
|
<img src="<?= e(icon_path('alcohol')) ?>" alt="">
|
||||||
</label>
|
<strong>Alkohol</strong>Heute was getrunken?
|
||||||
|
</span>
|
||||||
|
</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">
|
||||||
<input type="checkbox" name="alcohol" value="1" <?= !empty($entry['alcohol']) ? 'checked' : '' ?>>
|
<div class="sport-choice-list sport-choice-list--single">
|
||||||
<span>
|
<label class="sport-choice">
|
||||||
<strong>Alkohol</strong>
|
<input type="checkbox" name="alcohol" value="1" <?= !empty($entry['alcohol']) ? 'checked' : '' ?>>
|
||||||
<small>Wenn du heute Alkohol getrunken hast, werden 5 Punkte abgezogen.</small>
|
<span class="sport-choice__card sport-choice__card--toggle">
|
||||||
</span>
|
<img src="<?= e(icon_path('alcohol')) ?>" alt="">
|
||||||
</label>
|
<strong>Alkohol</strong>Heute was getrunken?
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user