diff --git a/frontend/src/app.css b/frontend/src/app.css
index 207e750..2969738 100644
--- a/frontend/src/app.css
+++ b/frontend/src/app.css
@@ -2706,6 +2706,21 @@ a.analysis-split__nav-item {
gap: 10px;
margin-top: 12px;
}
+.exercise-search-bar__actions--split {
+ width: 100%;
+}
+.exercise-search-bar__actions-main {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 10px;
+}
+.exercise-mine-toggle--active {
+ border-color: var(--accent);
+ background: var(--accent-light);
+ color: var(--accent-dark);
+ font-weight: 600;
+}
.exercise-filter-trigger {
display: inline-flex;
align-items: center;
@@ -3607,13 +3622,11 @@ a.analysis-split__nav-item {
margin: 0 0 10px;
font-size: 0.9rem;
color: var(--danger);
- grid-column: 1 / -1;
}
.dashboard-phase0-kpis__loading {
font-size: 0.9rem;
- margin: 0;
- grid-column: 1 / -1;
+ margin: 0 0 10px;
}
.dashboard-kpi-card {
@@ -3685,6 +3698,55 @@ a.analysis-split__nav-item {
margin-top: auto;
}
+@media (max-width: 1023px) {
+ .dashboard-phase0-kpis {
+ display: flex;
+ flex-wrap: nowrap;
+ gap: 8px;
+ overflow-x: auto;
+ overflow-y: hidden;
+ -webkit-overflow-scrolling: touch;
+ scroll-snap-type: x proximity;
+ scrollbar-width: none;
+ padding: 2px 0 4px;
+ margin-left: calc(-1 * max(12px, env(safe-area-inset-left, 0px)));
+ margin-right: calc(-1 * max(12px, env(safe-area-inset-right, 0px)));
+ padding-left: max(12px, env(safe-area-inset-left, 0px));
+ padding-right: max(12px, env(safe-area-inset-right, 0px));
+ }
+ .dashboard-phase0-kpis::-webkit-scrollbar {
+ display: none;
+ }
+ .dashboard-kpi-card {
+ flex: 0 0 auto;
+ scroll-snap-align: start;
+ width: min(132px, 38vw);
+ min-height: 0;
+ padding: 10px 10px 8px;
+ gap: 2px;
+ }
+ .dashboard-kpi-card__icon {
+ width: 32px;
+ height: 32px;
+ margin-bottom: 0;
+ }
+ .dashboard-kpi-card__icon svg {
+ width: 18px;
+ height: 18px;
+ }
+ .dashboard-kpi-card__value {
+ font-size: 1.35rem;
+ }
+ .dashboard-kpi-card__label {
+ font-size: 0.72rem;
+ line-height: 1.25;
+ }
+ .dashboard-kpi-card__hint {
+ font-size: 0.6rem;
+ letter-spacing: 0.04em;
+ }
+}
+
.dashboard-training-preview-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx
index 4e75cc2..031231a 100644
--- a/frontend/src/pages/Dashboard.jsx
+++ b/frontend/src/pages/Dashboard.jsx
@@ -191,49 +191,48 @@ function Dashboard() {
-
- {phase0Err ? (
-
- {phase0Err}
-
- ) : null}
- {!phase0Err && phase0Stats ? (
- <>
-
-
-
-
-
- {formatCappedCount(phase0Stats.draftCount, phase0Stats.draftCapped)}
-
-
Übungs-Entwürfe
-
finalisieren
-
-
-
-
-
-
- {formatCappedCount(phase0Stats.mineCount, phase0Stats.mineCapped)}
-
-
Meine Übungen
-
alle Status
-
-
-
-
-
-
- {formatCappedCount(phase0Stats.ytdCompletedCount, phase0Stats.ytdCapped)}
-
- Gehalten {phase0Stats.year}
- abrechnungsnah
-
- >
- ) : !phase0Err ? (
-
Zahlen werden geladen…
- ) : null}
-
+ {phase0Err ? (
+
+ {phase0Err}
+
+ ) : null}
+ {!phase0Err && !phase0Stats ? (
+ Zahlen werden geladen…
+ ) : null}
+ {!phase0Err && phase0Stats ? (
+
+
+
+
+
+
+ {formatCappedCount(phase0Stats.draftCount, phase0Stats.draftCapped)}
+
+
Übungs-Entwürfe
+
finalisieren
+
+
+
+
+
+
+ {formatCappedCount(phase0Stats.mineCount, phase0Stats.mineCapped)}
+
+
Meine Übungen
+
alle Status
+
+
+
+
+
+
+ {formatCappedCount(phase0Stats.ytdCompletedCount, phase0Stats.ytdCapped)}
+
+ Gehalten {phase0Stats.year}
+ abrechnungsnah
+
+
+ ) : null}
diff --git a/frontend/src/pages/ExercisesListPage.jsx b/frontend/src/pages/ExercisesListPage.jsx
index 2695e8d..2be9270 100644
--- a/frontend/src/pages/ExercisesListPage.jsx
+++ b/frontend/src/pages/ExercisesListPage.jsx
@@ -123,16 +123,29 @@ function levelOptionShort(levelStr) {
return o ? String(o.level) : String(levelStr)
}
-function exerciseListFiltersWithDashboardUrl(mergedFilters) {
+function applyDashboardExerciseListUrl(mergedFromPrefs) {
try {
const sp = new URLSearchParams(window.location.search)
- if (sp.get('status') !== 'draft') return mergedFilters
- return {
- ...mergedFilters,
- status_rules: [{ key: 'url-dashboard-draft', id: 'draft', mode: 'require' }],
+ const mine = sp.get('mine') === '1' || sp.get('created_by_me') === '1'
+ const statusDraft = sp.get('status') === 'draft'
+
+ if (mine) {
+ const next = { ...INITIAL_EXERCISE_LIST_FILTERS }
+ if (statusDraft) {
+ next.status_rules = [{ key: 'url-dashboard-draft', id: 'draft', mode: 'require' }]
+ }
+ return next
}
+
+ if (statusDraft) {
+ return {
+ ...mergedFromPrefs,
+ status_rules: [{ key: 'url-dashboard-draft', id: 'draft', mode: 'require' }],
+ }
+ }
+ return mergedFromPrefs
} catch {
- return mergedFilters
+ return mergedFromPrefs
}
}
@@ -192,7 +205,7 @@ function ExercisesListPage() {
if (!user?.id) return
if (prefsAppliedRef.current) return
const merged = mergeExerciseListPrefsFromApi(user.exercise_list_prefs)
- setFilters(exerciseListFiltersWithDashboardUrl(merged))
+ setFilters(applyDashboardExerciseListUrl(merged))
try {
const sp = new URLSearchParams(window.location.search)
if (sp.get('mine') === '1' || sp.get('created_by_me') === '1') setMineOnly(true)
@@ -270,6 +283,14 @@ function ExercisesListPage() {
const filterChips = useMemo(() => {
const chips = []
+ if (mineOnly) {
+ chips.push({
+ key: 'mine-only',
+ label: 'Nur von mir erstellt',
+ onRemove: () => setMineOnly(false),
+ })
+ }
+
pushCatalogRuleFilterChips(chips, 'focus_rules', filters.focus_rules, focusOptions, 'Fokus', setFilters)
if (filters.focus_only_without) {
@@ -410,6 +431,7 @@ function ExercisesListPage() {
return chips
}, [
+ mineOnly,
filters,
focusOptions,
styleOptions,
@@ -625,7 +647,10 @@ function ExercisesListPage() {
}
}
- const resetAllFilters = useCallback(() => setFilters({ ...INITIAL_EXERCISE_LIST_FILTERS }), [])
+ const resetAllFilters = useCallback(() => {
+ setMineOnly(false)
+ setFilters({ ...INITIAL_EXERCISE_LIST_FILTERS })
+ }, [])
const handleSaveExerciseFilterPrefs = useCallback(async () => {
const uid = user?.id
@@ -833,20 +858,30 @@ function ExercisesListPage() {
list="exercise-search-titles"
enterKeyHint="search"
/>
-
-
- {filterChips.length > 0 ? (
-