feat: remove deprecated demo route and enhance dashboard widget registration
- Removed outdated visualization demo route and fixed demo layout in the frontend. - Updated widget registration logic in `frontend/src/widgetSystem/registerDashboardWidgets.js` to ensure proper integration of core widgets. - Adjusted documentation in `.claude/docs/technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md` and comments in `backend/widget_catalog.py` to reflect changes. - Added new dashboard widgets for activity and body overview, enhancing user experience and data visualization capabilities. - Bumped application version to reflect these changes.
This commit is contained in:
parent
725e7ffe4b
commit
ddc87ba5ae
|
|
@ -42,7 +42,7 @@ Kontext: **Dashboard-Lab** unter geschützten Endpoints `GET/PUT /api/app/...` (
|
|||
1. **`backend/widget_catalog.py`** – `WIDGET_CATALOG`: erlaubte Widget-IDs, Reihenfolge, Titel/Beschreibung für API und Default-Layout.
|
||||
2. **`backend/dashboard_layout_schema.py`** – `DashboardLayoutPayload`: jede Zeile hat `id`, `enabled`, optional `config`. IDs müssen in `ALLOWED_WIDGET_IDS` sein (aus dem Katalog abgeleitet).
|
||||
3. **`backend/dashboard_widget_config.py`** – `validate_widget_entry_config`: **nur** Widgets in `WIDGETS_ALLOWING_CONFIG` dürfen **nicht-leere** `config` haben; Keys werden streng validiert (unbekannte Keys → Fehler).
|
||||
4. **Frontend** – `ensurePilotLabWidgetsRegistered()` in `frontend/src/widgetSystem/registerPilotLabWidgets.js`: verbindet jede Katalog-ID mit einer React-Komponente und mappt `ctx.layoutEntry.config` auf Props.
|
||||
4. **Frontend** – `ensureDashboardWidgetsRegistered()` in `frontend/src/widgetSystem/registerDashboardWidgets.js`: verbindet jede Katalog-ID mit einer React-Komponente und mappt `ctx.layoutEntry.config` auf Props.
|
||||
5. **Dashboard-Lab-UI** – `frontend/src/pages/DashboardLabPage.jsx`: Umsortieren, Ein/Aus, Speichern; **zusätzliche** UI nur nötig, wenn das Widget konfigurierbare Felder braucht.
|
||||
|
||||
---
|
||||
|
|
@ -53,8 +53,8 @@ Kontext: **Dashboard-Lab** unter geschützten Endpoints `GET/PUT /api/app/...` (
|
|||
|--------|--------|--------|
|
||||
| A | `backend/widget_catalog.py` | Neuen Eintrag `{ "id", "title", "description" }` in `WIDGET_CATALOG` einfügen (Reihenfolge = Default-Reihenfolge im Layout). Optional `"requires_feature": "<features.id>"` für Tarif-Gating (`dashboard_widget_entitlements`). |
|
||||
| B | `backend/widget_catalog.py` | Optional: ID zu `DEFAULT_LAB_WIDGET_IDS` hinzufügen, wenn es im Standard-Lab **aktiv** sein soll. |
|
||||
| C | `frontend/src/components/dashboard-widgets/MyWidget.jsx` (oder Pilot-Komponente) | React-Komponente implementieren; typischerweise `refreshTick` aus `mapProps` nutzen, um Daten neu zu laden. |
|
||||
| D | `frontend/src/widgetSystem/registerPilotLabWidgets.js` | `import` + `registerDashboardWidget({ id, Component, mapProps })` – `id` **exakt** wie im Katalog. |
|
||||
| C | `frontend/src/components/dashboard-widgets/MyWidget.jsx` (oder Legacy-Widget unter `dashboard-widgets-legacy/`) | React-Komponente implementieren; typischerweise `refreshTick` aus `mapProps` nutzen, um Daten neu zu laden. |
|
||||
| D | `frontend/src/widgetSystem/registerDashboardWidgets.js` | `import` + `registerDashboardWidget({ id, Component, mapProps })` – `id` **exakt** wie im Katalog. |
|
||||
| E | `backend/tests/test_widget_catalog.py` | Läuft implizit mit; bei Strukturänderungen Katalog-Tests beachten. |
|
||||
| F | `backend/version.py` | `MODULE_VERSIONS["app_dashboard"]` MINOR erhöhen und kurz kommentieren. |
|
||||
| G | Build/Tests | `pytest` (z. B. `tests/test_dashboard_layout_schema.py`, `test_widget_catalog.py`); `npm run build` im `frontend`. |
|
||||
|
|
@ -159,5 +159,5 @@ Nach Speichern ruft die Seite `api.putAppDashboardLayout(layout)` auf; das Backe
|
|||
| Layout-Pydantic | `backend/dashboard_layout_schema.py` |
|
||||
| HTTP | `backend/routers/app_dashboard.py` |
|
||||
| Registry + Render | `frontend/src/widgetSystem/dashboardWidgetRegistry.jsx` |
|
||||
| Pilot/Lab-Registrierung | `frontend/src/widgetSystem/registerPilotLabWidgets.js` |
|
||||
| Dashboard-Widget-Registrierung | `frontend/src/widgetSystem/registerDashboardWidgets.js` |
|
||||
| Lab-UI | `frontend/src/pages/DashboardLabPage.jsx` |
|
||||
|
|
|
|||
|
|
@ -100,6 +100,11 @@ frontend/src/
|
|||
**Branch:** develop
|
||||
**Nächster Schritt:** Frontend Chart Integration → Testing → Prod Deploy v0.9i
|
||||
|
||||
### Updates (23.04.2026 - Dashboard: veraltete Demo-Route entfernt, klare Produkt-Registry)
|
||||
|
||||
- **Frontend:** Veraltete Visualisierungs-Demo-Route und festes Demo-Layout entfernt; Widget-Registrierung in `frontend/src/widgetSystem/registerDashboardWidgets.js` (`ensureDashboardWidgetsRegistered`). Kern-Widgets unter `frontend/src/components/dashboard-widgets-legacy/`. Chart-Hilfen in `frontend/src/widgetSystem/dashboardChartUtils.js`.
|
||||
- **Doku:** `.claude/docs/technical/DASHBOARD_WIDGETS_AGENT_GUIDE.md` und Kommentar in `backend/widget_catalog.py` angepasst.
|
||||
|
||||
### Updates (09.04.2026 - Universal CSV Import, Prod-Migration abgeschlossen)
|
||||
|
||||
- **Agent-Leitfaden:** `.claude/docs/technical/UNIVERSAL_CSV_IMPORT_AGENT_GUIDE.md` (Checkliste für neue Import-Module, Executor, Vorlagen, `source=csv`, SAVEPOINT-/Cursor-Regeln)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
Öffentlicher Widget-Katalog (Dashboard-Lab / später Produkt-Dashboard).
|
||||
|
||||
Single Source für: erlaubte IDs, Standard-Reihenfolge, Anzeige-Metadaten für API/GUI.
|
||||
Frontend-Komponenten registrieren dieselben IDs lokal (siehe widgetSystem/registerPilotLabWidgets).
|
||||
Frontend-Komponenten registrieren dieselben IDs lokal (siehe widgetSystem/registerDashboardWidgets.js, Funktion ensureDashboardWidgetsRegistered).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import Analysis from './pages/Analysis'
|
|||
import SettingsPage from './pages/SettingsPage'
|
||||
import SettingsShell from './layouts/SettingsShell'
|
||||
import ProfileReferenceValuesPage from './pages/ProfileReferenceValuesPage'
|
||||
import PilotVizPage from './pages/PilotVizPage'
|
||||
import DashboardLabPage from './pages/DashboardLabPage'
|
||||
import DashboardConfigurePage from './pages/DashboardConfigurePage'
|
||||
import GuidePage from './pages/GuidePage'
|
||||
|
|
@ -271,7 +270,6 @@ function AppShell() {
|
|||
</Route>
|
||||
<Route path="/workflow-editor/:id" element={<WorkflowEditorPage/>}/>
|
||||
<Route path="/subscription" element={<SubscriptionPage/>}/>
|
||||
<Route path="/pilot/viz" element={<PilotVizPage />} />
|
||||
<Route path="/app/dashboard-lab" element={<DashboardLabPage />} />
|
||||
</Routes>
|
||||
</main>
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import {
|
|||
BODY_CHART_DAYS_DEFAULT,
|
||||
normalizeBodyChartDays,
|
||||
} from '../../widgetSystem/bodyChartDays'
|
||||
import PilotRuleCard from './PilotRuleCard'
|
||||
import DashboardRuleCard from './DashboardRuleCard'
|
||||
|
||||
export default function PilotActivitySection({ refreshTick = 0, chartDays = BODY_CHART_DAYS_DEFAULT }) {
|
||||
export default function ActivityOverviewWidget({ refreshTick = 0, chartDays = BODY_CHART_DAYS_DEFAULT }) {
|
||||
const periodDays = normalizeBodyChartDays(chartDays)
|
||||
const { activeProfile } = useProfile()
|
||||
const globalQualityLevel = activeProfile?.quality_filter_level
|
||||
|
|
@ -124,7 +124,7 @@ export default function PilotActivitySection({ refreshTick = 0, chartDays = BODY
|
|||
</Link>
|
||||
</p>
|
||||
) : (
|
||||
actRules.map((item, i) => <PilotRuleCard key={i} item={item} />)
|
||||
actRules.map((item, i) => <DashboardRuleCard key={i} item={item} />)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -15,14 +15,14 @@ import dayjs from 'dayjs'
|
|||
import { api } from '../../utils/api'
|
||||
import { useProfile } from '../../context/ProfileContext'
|
||||
import { getInterpretation } from '../../utils/interpret'
|
||||
import { rollingAvg, fmtDate } from '../../pilot/pilotChartUtils'
|
||||
import { rollingAvg, fmtDate } from '../../widgetSystem/dashboardChartUtils'
|
||||
import {
|
||||
BODY_CHART_DAYS_DEFAULT,
|
||||
normalizeBodyChartDays,
|
||||
} from '../../widgetSystem/bodyChartDays'
|
||||
import PilotRuleCard from './PilotRuleCard'
|
||||
import DashboardRuleCard from './DashboardRuleCard'
|
||||
|
||||
export default function PilotBodySection({ refreshTick = 0, chartDays = BODY_CHART_DAYS_DEFAULT }) {
|
||||
export default function BodyOverviewWidget({ refreshTick = 0, chartDays = BODY_CHART_DAYS_DEFAULT }) {
|
||||
const windowDays = normalizeBodyChartDays(chartDays)
|
||||
const { activeProfile } = useProfile()
|
||||
const [weights, setWeights] = useState([])
|
||||
|
|
@ -221,7 +221,7 @@ export default function PilotBodySection({ refreshTick = 0, chartDays = BODY_CHA
|
|||
Körperfett, Magermasse (FFMI), BMI – gleiche Logik wie auf der Verlauf-Seite (Körper).
|
||||
</p>
|
||||
{rules.map((item, i) => (
|
||||
<PilotRuleCard key={i} item={item} />
|
||||
<DashboardRuleCard key={i} item={item} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -2,7 +2,7 @@ import { useState } from 'react'
|
|||
import { ChevronDown, ChevronUp } from 'lucide-react'
|
||||
import { getStatusColor, getStatusBg } from '../../utils/interpret'
|
||||
|
||||
export default function PilotRuleCard({ item }) {
|
||||
export default function DashboardRuleCard({ item }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const color = getStatusColor(item.status)
|
||||
return (
|
||||
|
|
@ -38,7 +38,7 @@ function buildAutoTileIds(refTiles, hasBf, hasKcal) {
|
|||
* @param {{ refreshTick?: number, kpiConfig?: Record<string, unknown> }} props
|
||||
* kpiConfig.tiles: geordnete Kachel-ids; fehlend = automatische Belegung (wie bisher).
|
||||
*/
|
||||
export default function PilotKpiBoard({ refreshTick = 0, kpiConfig }) {
|
||||
export default function KpiBoardWidget({ refreshTick = 0, kpiConfig }) {
|
||||
const manualOrder = useMemo(() => kpiTileOrderFromConfig(kpiConfig), [kpiConfig])
|
||||
|
||||
const { activeProfile } = useProfile()
|
||||
|
|
@ -9,7 +9,7 @@ import { api } from '../../utils/api'
|
|||
* @param {{ onSaved?: () => void, captureConfig?: Record<string, unknown> }} props
|
||||
* captureConfig: show_weight, show_resting_hr, show_hrv, show_vo2_max (false = ausblenden; fehlend = true)
|
||||
*/
|
||||
export default function PilotQuickCapture({ onSaved, captureConfig }) {
|
||||
export default function QuickCaptureWidget({ onSaved, captureConfig }) {
|
||||
const cfgRaw = captureConfig && typeof captureConfig === 'object' ? captureConfig : {}
|
||||
const showWeight = cfgRaw.show_weight !== false
|
||||
const showRestingHr = cfgRaw.show_resting_hr !== false
|
||||
|
|
@ -200,7 +200,7 @@ export default function PilotQuickCapture({ onSaved, captureConfig }) {
|
|||
{showRestingHr && (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="pqc-resting-hr"
|
||||
htmlFor="qcw-resting-hr"
|
||||
className="form-label"
|
||||
style={{ display: 'block', marginBottom: 4, fontSize: 11, fontWeight: 600, color: 'var(--text2)' }}
|
||||
>
|
||||
|
|
@ -208,7 +208,7 @@ export default function PilotQuickCapture({ onSaved, captureConfig }) {
|
|||
<span style={{ fontWeight: 400, color: 'var(--text3)' }}> (bpm)</span>
|
||||
</label>
|
||||
<input
|
||||
id="pqc-resting-hr"
|
||||
id="qcw-resting-hr"
|
||||
type="number"
|
||||
className="form-input"
|
||||
style={{ width: '100%' }}
|
||||
|
|
@ -221,7 +221,7 @@ export default function PilotQuickCapture({ onSaved, captureConfig }) {
|
|||
{showHrv && (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="pqc-hrv"
|
||||
htmlFor="qcw-hrv"
|
||||
className="form-label"
|
||||
style={{ display: 'block', marginBottom: 4, fontSize: 11, fontWeight: 600, color: 'var(--text2)' }}
|
||||
>
|
||||
|
|
@ -229,7 +229,7 @@ export default function PilotQuickCapture({ onSaved, captureConfig }) {
|
|||
<span style={{ fontWeight: 400, color: 'var(--text3)' }}> (ms)</span>
|
||||
</label>
|
||||
<input
|
||||
id="pqc-hrv"
|
||||
id="qcw-hrv"
|
||||
type="number"
|
||||
className="form-input"
|
||||
style={{ width: '100%' }}
|
||||
|
|
@ -242,14 +242,14 @@ export default function PilotQuickCapture({ onSaved, captureConfig }) {
|
|||
{showVo2 && (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="pqc-vo2"
|
||||
htmlFor="qcw-vo2"
|
||||
className="form-label"
|
||||
style={{ display: 'block', marginBottom: 4, fontSize: 11, fontWeight: 600, color: 'var(--text2)' }}
|
||||
>
|
||||
VO₂max
|
||||
</label>
|
||||
<input
|
||||
id="pqc-vo2"
|
||||
id="qcw-vo2"
|
||||
type="number"
|
||||
className="form-input"
|
||||
style={{ width: '100%' }}
|
||||
|
|
@ -4,7 +4,7 @@ import { useProfile } from '../../context/ProfileContext'
|
|||
|
||||
dayjs.locale('de')
|
||||
|
||||
export default function PilotWelcome() {
|
||||
export default function WelcomeWidget() {
|
||||
const { activeProfile } = useProfile()
|
||||
return (
|
||||
<div className="card section-gap" style={{ marginBottom: 16 }}>
|
||||
|
|
@ -12,7 +12,7 @@ export default function PilotWelcome() {
|
|||
Hallo, {activeProfile?.name || 'Nutzer'} 👋
|
||||
</h2>
|
||||
<p style={{ fontSize: 12, color: 'var(--text3)', margin: '6px 0 0' }}>
|
||||
{dayjs().format('dddd, DD. MMMM YYYY')} · Pilot-Übersicht
|
||||
{dayjs().format('dddd, DD. MMMM YYYY')} · Übersicht
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
|
|
@ -5,7 +5,7 @@ import { api } from '../utils/api'
|
|||
import { useProfile } from '../context/ProfileContext'
|
||||
import TrialBanner from '../components/TrialBanner'
|
||||
import EmailVerificationBanner from '../components/EmailVerificationBanner'
|
||||
import { ensurePilotLabWidgetsRegistered } from '../widgetSystem/registerPilotLabWidgets'
|
||||
import { ensureDashboardWidgetsRegistered } from '../widgetSystem/registerDashboardWidgets'
|
||||
import { WidgetRenderer } from '../widgetSystem/dashboardWidgetRegistry'
|
||||
|
||||
function catalogMetaById(catalog) {
|
||||
|
|
@ -27,7 +27,7 @@ export default function Dashboard() {
|
|||
const requestRefresh = () => setRefreshTick((t) => t + 1)
|
||||
|
||||
useEffect(() => {
|
||||
ensurePilotLabWidgetsRegistered()
|
||||
ensureDashboardWidgetsRegistered()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|||
import { Link } from 'react-router-dom'
|
||||
import { ChevronDown, ChevronUp, GripVertical, LayoutDashboard, Plus, Search, X } from 'lucide-react'
|
||||
import { api, formatFastApiDetail } from '../utils/api'
|
||||
import { ensurePilotLabWidgetsRegistered } from '../widgetSystem/registerPilotLabWidgets'
|
||||
import { ensureDashboardWidgetsRegistered } from '../widgetSystem/registerDashboardWidgets'
|
||||
import {
|
||||
BODY_CHART_DAYS_DEFAULT,
|
||||
BODY_CHART_DAYS_MAX,
|
||||
|
|
@ -46,7 +46,7 @@ function catalogMetaById(catalog) {
|
|||
* @param {{ adminMode?: boolean }} [props]
|
||||
*/
|
||||
export default function DashboardConfigurePage({ adminMode = false } = {}) {
|
||||
ensurePilotLabWidgetsRegistered()
|
||||
ensureDashboardWidgetsRegistered()
|
||||
|
||||
const [bundle, setBundle] = useState(null)
|
||||
const [adminFromDatabase, setAdminFromDatabase] = useState(null)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { ChevronDown, ChevronUp, LayoutGrid } from 'lucide-react'
|
|||
import { Link } from 'react-router-dom'
|
||||
import { api, formatFastApiDetail } from '../utils/api'
|
||||
import { WidgetRenderer } from '../widgetSystem/dashboardWidgetRegistry'
|
||||
import { ensurePilotLabWidgetsRegistered } from '../widgetSystem/registerPilotLabWidgets'
|
||||
import { ensureDashboardWidgetsRegistered } from '../widgetSystem/registerDashboardWidgets'
|
||||
import {
|
||||
BODY_CHART_DAYS_DEFAULT,
|
||||
BODY_CHART_DAYS_MAX,
|
||||
|
|
@ -38,7 +38,7 @@ function catalogMetaById(catalog) {
|
|||
}
|
||||
|
||||
export default function DashboardLabPage() {
|
||||
ensurePilotLabWidgetsRegistered()
|
||||
ensureDashboardWidgetsRegistered()
|
||||
|
||||
const [refreshTick, setRefreshTick] = useState(0)
|
||||
const requestRefresh = () => setRefreshTick((t) => t + 1)
|
||||
|
|
@ -198,13 +198,11 @@ export default function DashboardLabPage() {
|
|||
<p style={{ fontSize: 13, color: 'var(--text2)', lineHeight: 1.6, marginTop: 8 }}>
|
||||
Widget-System: Katalog, Registry, Renderer; optional pro Widget <code>config</code> (z. B.{' '}
|
||||
<strong>Körper</strong> / <strong>Aktivität</strong>: Zeitraum 7–90 Tage; <strong>KPI</strong>: Kacheln
|
||||
wählen & sortieren). Layout pro Profil in der DB —
|
||||
getrennt vom Produktiv-Dashboard.
|
||||
Vergleich:{' '}
|
||||
<Link to="/pilot/viz" style={{ color: 'var(--accent)' }}>
|
||||
Pilot-Übersicht (festes Standard-Layout)
|
||||
wählen & sortieren). Layout pro Profil in der DB — dieselben Widgets wie auf der{' '}
|
||||
<Link to="/" style={{ color: 'var(--accent)' }}>
|
||||
Produkt-Übersicht
|
||||
</Link>
|
||||
.
|
||||
, hier mit Editor und API-Fokus.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,45 +0,0 @@
|
|||
import { useState } from 'react'
|
||||
import { FlaskConical } from 'lucide-react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { WidgetRenderer } from '../widgetSystem/dashboardWidgetRegistry'
|
||||
import { ensurePilotLabWidgetsRegistered } from '../widgetSystem/registerPilotLabWidgets'
|
||||
import { DEFAULT_LAB_LAYOUT } from '../widgetSystem/defaultLabLayout'
|
||||
|
||||
/**
|
||||
* Pilot-Übersicht nach Product-Spec (festes Standard-Layout).
|
||||
* Nutzt dasselbe Widget-Rendering wie /app/dashboard-lab.
|
||||
*/
|
||||
export default function PilotVizPage() {
|
||||
ensurePilotLabWidgetsRegistered()
|
||||
|
||||
const [refreshTick, setRefreshTick] = useState(0)
|
||||
const requestRefresh = () => setRefreshTick((t) => t + 1)
|
||||
|
||||
return (
|
||||
<div style={{ paddingBottom: 96, textAlign: 'left', maxWidth: 920, margin: '0 auto' }}>
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
<Link
|
||||
to="/settings"
|
||||
className="btn btn-secondary"
|
||||
style={{ display: 'inline-flex', marginBottom: 12, textDecoration: 'none' }}
|
||||
>
|
||||
← Einstellungen
|
||||
</Link>
|
||||
<h1 className="page-title" style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<FlaskConical size={26} color="var(--accent)" />
|
||||
Pilot: Übersicht
|
||||
</h1>
|
||||
<p style={{ fontSize: 13, color: 'var(--text2)', lineHeight: 1.6, marginTop: 8 }}>
|
||||
Konfigurierbare Ziel-Übersicht (Test). Produktives Dashboard und Verlauf unverändert. Nach Speichern von
|
||||
Gewicht oder Vitalwerten werden KPIs und Körperbereich neu geladen.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<WidgetRenderer
|
||||
layout={DEFAULT_LAB_LAYOUT}
|
||||
refreshTick={refreshTick}
|
||||
requestRefresh={requestRefresh}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -463,20 +463,12 @@ export default function SettingsPage() {
|
|||
style={{ borderStyle: 'dashed', borderColor: 'var(--border2)', background: 'var(--surface2)' }}
|
||||
>
|
||||
<div className="card-title" style={{ fontSize: 14 }}>
|
||||
Pilot: Visualisierungs-Module
|
||||
Entwickler: Dashboard-Layout (API)
|
||||
</div>
|
||||
<p style={{ fontSize: 12, color: 'var(--text2)', marginBottom: 12, lineHeight: 1.5 }}>
|
||||
Ziel-Übersicht-Pilot: Schnelleingabe, KPIs, Körper-Chart, Aktivität. Die reguläre Übersicht konfigurierst du
|
||||
unter <strong>Übersicht anpassen</strong> oben.
|
||||
Experimentelles Layout-Lab mit Katalog und API (getrennt von der regulären Übersicht). Die produktive Kachelansicht
|
||||
steuerst du über <strong>Übersicht anpassen</strong> oben.
|
||||
</p>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
<Link
|
||||
to="/pilot/viz"
|
||||
className="btn btn-secondary btn-full"
|
||||
style={{ textAlign: 'center', textDecoration: 'none', boxSizing: 'border-box' }}
|
||||
>
|
||||
Pilot öffnen
|
||||
</Link>
|
||||
<Link
|
||||
to="/app/dashboard-lab"
|
||||
className="btn btn-secondary btn-full"
|
||||
|
|
@ -491,10 +483,9 @@ export default function SettingsPage() {
|
|||
}}
|
||||
>
|
||||
<LayoutGrid size={18} />
|
||||
Dashboard-Lab (Layout API)
|
||||
Dashboard-Lab öffnen
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Auth actions */}
|
||||
<div className="card section-gap">
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
/**
|
||||
* Standard-Layout v1 (nur Pilot `/pilot/viz` ohne API).
|
||||
* API-Nutzer: default_layout aus Backend (alle Katalog-IDs; aktiv = DEFAULT_LAB_WIDGET_IDS).
|
||||
* Diese Datei: kompakte feste 5 Widgets für den Pilot – nicht automatisch alle P1-Widgets.
|
||||
*/
|
||||
export const DEFAULT_LAB_LAYOUT = {
|
||||
version: 1,
|
||||
widgets: [
|
||||
{ id: 'welcome', enabled: true },
|
||||
{ id: 'quick_capture', enabled: true },
|
||||
{ id: 'kpi_board', enabled: true },
|
||||
{ id: 'body_overview', enabled: true },
|
||||
{ id: 'activity_overview', enabled: true },
|
||||
],
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
/**
|
||||
* Pilot/Lab-Widgets registrieren. IDs müssen zu backend/widget_catalog.WIDGET_CATALOG passen.
|
||||
* Dashboard-Widget-Registry: Katalog-IDs aus backend/widget_catalog.WIDGET_CATALOG → React-Komponenten.
|
||||
*/
|
||||
import PilotWelcome from '../components/pilot/PilotWelcome'
|
||||
import PilotQuickCapture from '../components/pilot/PilotQuickCapture'
|
||||
import PilotKpiBoard from '../components/pilot/PilotKpiBoard'
|
||||
import PilotBodySection from '../components/pilot/PilotBodySection'
|
||||
import PilotActivitySection from '../components/pilot/PilotActivitySection'
|
||||
import WelcomeWidget from '../components/dashboard-widgets-legacy/WelcomeWidget'
|
||||
import QuickCaptureWidget from '../components/dashboard-widgets-legacy/QuickCaptureWidget'
|
||||
import KpiBoardWidget from '../components/dashboard-widgets-legacy/KpiBoardWidget'
|
||||
import BodyOverviewWidget from '../components/dashboard-widgets-legacy/BodyOverviewWidget'
|
||||
import ActivityOverviewWidget from '../components/dashboard-widgets-legacy/ActivityOverviewWidget'
|
||||
import DashboardGreetingWidget from '../components/dashboard-widgets/DashboardGreetingWidget'
|
||||
import QuickWeightTodayWidget from '../components/dashboard-widgets/QuickWeightTodayWidget'
|
||||
import BodyStatStripWidget from '../components/dashboard-widgets/BodyStatStripWidget'
|
||||
|
|
@ -34,18 +34,18 @@ import { registerDashboardWidget } from './dashboardWidgetRegistry'
|
|||
|
||||
let _registered = false
|
||||
|
||||
export function ensurePilotLabWidgetsRegistered() {
|
||||
export function ensureDashboardWidgetsRegistered() {
|
||||
if (_registered) return
|
||||
_registered = true
|
||||
|
||||
registerDashboardWidget({
|
||||
id: 'welcome',
|
||||
Component: PilotWelcome,
|
||||
Component: WelcomeWidget,
|
||||
mapProps: () => ({}),
|
||||
})
|
||||
registerDashboardWidget({
|
||||
id: 'quick_capture',
|
||||
Component: PilotQuickCapture,
|
||||
Component: QuickCaptureWidget,
|
||||
mapProps: (ctx) => ({
|
||||
onSaved: ctx.requestRefresh,
|
||||
captureConfig: ctx.layoutEntry?.config || {},
|
||||
|
|
@ -53,7 +53,7 @@ export function ensurePilotLabWidgetsRegistered() {
|
|||
})
|
||||
registerDashboardWidget({
|
||||
id: 'kpi_board',
|
||||
Component: PilotKpiBoard,
|
||||
Component: KpiBoardWidget,
|
||||
mapProps: (ctx) => ({
|
||||
refreshTick: ctx.refreshTick,
|
||||
kpiConfig: ctx.layoutEntry?.config || {},
|
||||
|
|
@ -61,7 +61,7 @@ export function ensurePilotLabWidgetsRegistered() {
|
|||
})
|
||||
registerDashboardWidget({
|
||||
id: 'body_overview',
|
||||
Component: PilotBodySection,
|
||||
Component: BodyOverviewWidget,
|
||||
mapProps: (ctx) => ({
|
||||
refreshTick: ctx.refreshTick,
|
||||
chartDays: normalizeBodyChartDays(ctx.layoutEntry?.config?.chart_days),
|
||||
|
|
@ -77,7 +77,7 @@ export function ensurePilotLabWidgetsRegistered() {
|
|||
})
|
||||
registerDashboardWidget({
|
||||
id: 'activity_overview',
|
||||
Component: PilotActivitySection,
|
||||
Component: ActivityOverviewWidget,
|
||||
mapProps: (ctx) => ({
|
||||
refreshTick: ctx.refreshTick,
|
||||
chartDays: normalizeBodyChartDays(ctx.layoutEntry?.config?.chart_days),
|
||||
|
|
@ -193,6 +193,6 @@ export function ensurePilotLabWidgetsRegistered() {
|
|||
}
|
||||
|
||||
/** @internal Nur für Tests */
|
||||
export function __resetPilotLabRegistrationForTests() {
|
||||
export function __resetDashboardWidgetRegistrationForTests() {
|
||||
_registered = false
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user