From bdaa3c8db50b4c3ed44018f6bcfef65dea0c091e Mon Sep 17 00:00:00 2001 From: Schalli Date: Mon, 28 Sep 2026 23:12:38 +0200 Subject: [PATCH] feat(web): Dashboard-Aktionen in die App-Leiste, Bearbeiten als Stift Der Knopf Bearbeiten belegte eine eigene Zeile ueber den Widgets. Die Aktionen rendern jetzt per Portal in einen Einhaengepunkt rechts in der Kopfzeile (HEADER_ACTIONS_SLOT_ID); in der Ansicht nur der Stift, im Bearbeitungsmodus Hintergrund, Widget hinzufuegen und Fertig. Version 1.5.1. Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 6 ++ apps/web/src/app/(portal)/page.tsx | 90 +++++++++---------- apps/web/src/app/globals.css | 13 +++ .../dashboard/dashboard-background.tsx | 7 +- .../components/dashboard/edit-mode-toggle.tsx | 11 +-- apps/web/src/components/layout/header-slot.ts | 8 ++ apps/web/src/components/layout/header.tsx | 6 +- 7 files changed, 87 insertions(+), 54 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31190cc..4d048aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ Diese Liste beschreibt in einfachen Worten, was sich von Version zu Version an T ## Unveröffentlicht +## 1.5.1 – 2026-09-28 + +### Geändert + +- Dashboard: Der Knopf „Bearbeiten“ sitzt jetzt als Stift-Symbol rechts in der dunklen Leiste oben und nimmt über den Widgets keinen Platz mehr weg. Im Bearbeitungsmodus erscheinen dort auch „Hintergrund“, „Widget hinzufügen“ und „Fertig“. + ## 1.5.0 – 2026-09-28 ### Neu diff --git a/apps/web/src/app/(portal)/page.tsx b/apps/web/src/app/(portal)/page.tsx index 3193912..aaa3b89 100644 --- a/apps/web/src/app/(portal)/page.tsx +++ b/apps/web/src/app/(portal)/page.tsx @@ -1,7 +1,9 @@ 'use client'; import { useCallback, useEffect, useState } from 'react'; +import { createPortal } from 'react-dom'; import { useTranslations } from 'next-intl'; +import { HEADER_ACTIONS_SLOT_ID } from '@/components/layout/header-slot'; import { DashboardGrid } from '@/components/dashboard/dashboard-grid'; import { DashboardTabs } from '@/components/dashboard/dashboard-tabs'; import { EditModeToggle } from '@/components/dashboard/edit-mode-toggle'; @@ -48,7 +50,11 @@ export default function DashboardPage() { const [catalogOpen, setCatalogOpen] = useState(false); // Seitlicher Versatz der zentrierten Kacheln — die Zeile mit Begruessung // und Befehlsleiste rueckt mit, damit sie buendig ueber den Kacheln steht. - const [gridInset, setGridInset] = useState(0); + // Einhaengepunkt der Kopfzeile fuer die Dashboard-Aktionen (quick-260928-vxe) + const [actionsSlot, setActionsSlot] = useState(null); + useEffect(() => { + setActionsSlot(document.getElementById(HEADER_ACTIONS_SLOT_ID)); + }, []); // Hintergrund je Benutzer (Design „Mosaik“), seit quick-260928-ujj in der // Datenbank gespeichert und mit der Sitzung geladen. const { background, choose: chooseBackground } = useDashboardBackground(); @@ -122,6 +128,39 @@ export default function DashboardPage() { ); } + const actions = ( +
+ {isEditMode && } + {isEditMode && ( + + )} + setEditMode(!isEditMode)} /> +
+ ); + return (
@@ -140,50 +179,10 @@ export default function DashboardPage() { onReorder={reorderDashboards} /> - {/* Befehlsleiste rechts ueber dem Raster (Design „Mosaik“, Fluent - „command bar“). Die Begruessung sitzt seit Runde 3 unten in der - Seitenleiste; hier bleibt nur die Leiste. Mit Hintergrund liegt sie - auf der durchscheinenden Kachelflaeche (.dashboard-chrome). */} -
0 ? { paddingInline: 12 + gridInset } : undefined} - > -
- {isEditMode && } - {isEditMode && ( - - )} - setEditMode(!isEditMode)} - /> -
-
+ {/* Dashboard-Aktionen (quick-260928-vxe): per Portal rechts in der + App-Leiste statt als eigene Zeile ueber dem Raster. Fehlt der + Einhaengepunkt (etwa in Tests ohne Kopfzeile), stehen sie hier. */} + {actionsSlot ? createPortal(actions, actionsSlot) : actions} {isSwitchingDashboard ? (
@@ -199,7 +198,6 @@ export default function DashboardPage() { onRemoveWidget={removeWidget} onOpenCatalog={() => setCatalogOpen(true)} onQuickAdd={(type) => addWidget(type)} - onInsetChange={setGridInset} /> )} diff --git a/apps/web/src/app/globals.css b/apps/web/src/app/globals.css index 464da98..29f1eef 100644 --- a/apps/web/src/app/globals.css +++ b/apps/web/src/app/globals.css @@ -357,6 +357,19 @@ body { width: 2rem; padding-inline: 0; } + /* Knopf auf der dunklen App-Leiste (Dashboard-Aktionen, quick-260928-vxe). */ + .btn-appbar { + background-color: transparent; + color: var(--appbar-muted); + } + .btn-appbar:hover:not(:disabled) { + background-color: var(--appbar-hover); + color: var(--appbar-foreground); + } + .btn-appbar:focus-visible { + outline-color: var(--appbar-foreground); + outline-offset: -2px; + } /* Verweis: dunkle bzw. blaue Schrift, nie gelb auf Weiss. */ .link { diff --git a/apps/web/src/components/dashboard/dashboard-background.tsx b/apps/web/src/components/dashboard/dashboard-background.tsx index 072d3c6..10065ca 100644 --- a/apps/web/src/components/dashboard/dashboard-background.tsx +++ b/apps/web/src/components/dashboard/dashboard-background.tsx @@ -227,7 +227,9 @@ export function BackgroundPicker({ value, onChange }: BackgroundPickerProps) { onClick={() => setOpen((v) => !v)} aria-expanded={open} aria-haspopup="dialog" - className="btn btn-subtle" + aria-label={t('button')} + title={t('button')} + className="btn btn-appbar" > - {t('button')} + {/* In der App-Leiste ist der Platz knapp: Beschriftung erst ab lg. */} + {t('button')} {open && ( diff --git a/apps/web/src/components/dashboard/edit-mode-toggle.tsx b/apps/web/src/components/dashboard/edit-mode-toggle.tsx index 4021140..abeda8f 100644 --- a/apps/web/src/components/dashboard/edit-mode-toggle.tsx +++ b/apps/web/src/components/dashboard/edit-mode-toggle.tsx @@ -10,9 +10,9 @@ interface EditModeToggleProps { /** * Pencil / Checkmark icon button to toggle dashboard edit mode (D-01). * Active state (edit mode) uses primary background. - * Schwebt in der festen Aktionsleiste unten rechts (quick-260916-dyv): - * inaktiv mit Karten-Hintergrund, Rahmen und Schatten, damit der Stift - * ueber Widgets sichtbar bleibt. + * Sitzt seit quick-260928-vxe rechts in der dunklen App-Leiste: in der + * Ansicht nur der Stift (Name per aria-label/Tooltip), im Bearbeitungsmodus + * der gelbe Knopf „Fertig“. */ export function EditModeToggle({ isEditMode, onToggle }: EditModeToggleProps) { const t = useTranslations('widgets'); @@ -21,8 +21,9 @@ export function EditModeToggle({ isEditMode, onToggle }: EditModeToggleProps) { ); } diff --git a/apps/web/src/components/layout/header-slot.ts b/apps/web/src/components/layout/header-slot.ts index fa33574..a5876b6 100644 --- a/apps/web/src/components/layout/header-slot.ts +++ b/apps/web/src/components/layout/header-slot.ts @@ -4,3 +4,11 @@ * zeichnen per `createPortal` hinein (heute: die Dashboard-Reiter). */ export const HEADER_CENTER_SLOT_ID = 'header-center-slot'; + +/** + * Einhaengepunkt rechts in der Kopfzeile, vor „Fehler melden“ (quick-260928-vxe). + * Das Dashboard zeichnet dort seine Aktionen hinein (Bearbeiten, im + * Bearbeitungsmodus Hintergrund, Widget hinzufuegen, Fertig) — so belegen sie + * keine eigene Zeile ueber den Widgets. + */ +export const HEADER_ACTIONS_SLOT_ID = 'header-actions-slot'; diff --git a/apps/web/src/components/layout/header.tsx b/apps/web/src/components/layout/header.tsx index 0c5bf55..2cb7657 100644 --- a/apps/web/src/components/layout/header.tsx +++ b/apps/web/src/components/layout/header.tsx @@ -10,7 +10,7 @@ import { fetchSessionState, logout } from '@/lib/auth-actions'; import { buildNextParam } from '@/lib/safe-next'; import { ThemeToggle } from '@/components/theme-toggle'; import { BugReportButton } from '@/components/bug-report/bug-report-button'; -import { HEADER_CENTER_SLOT_ID } from '@/components/layout/header-slot'; +import { HEADER_ACTIONS_SLOT_ID, HEADER_CENTER_SLOT_ID } from '@/components/layout/header-slot'; import { usePathname } from 'next/navigation'; import { resolvePageTitle, useNavStore } from '@/lib/stores/nav-store'; @@ -169,6 +169,10 @@ export function Header() { {/* Right: Actions */}
+ {/* Einhaengepunkt fuer die Dashboard-Aktionen (quick-260928-vxe); + bleibt im React-Baum der Kopfzeile leer, `empty:hidden` nimmt ihm + auf anderen Seiten den Abstand. */} +