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) <noreply@anthropic.com>
This commit is contained in:
@@ -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<HTMLElement | null>(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 = (
|
||||
<div role="toolbar" aria-label={t('toolbarLabel')} className="flex items-center gap-1">
|
||||
{isEditMode && <BackgroundPicker value={background} onChange={chooseBackground} />}
|
||||
{isEditMode && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCatalogOpen(true)}
|
||||
aria-label={t('addWidget')}
|
||||
title={t('addWidget')}
|
||||
className="btn btn-appbar"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<line x1="12" y1="5" x2="12" y2="19" />
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
</svg>
|
||||
<span className="hidden lg:inline">{t('addWidget')}</span>
|
||||
</button>
|
||||
)}
|
||||
<EditModeToggle isEditMode={isEditMode} onToggle={() => setEditMode(!isEditMode)} />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={`relative p-2 ${hasBackground ? 'dashboard-has-bg' : ''}`}>
|
||||
<DashboardBackdrop background={background} />
|
||||
@@ -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). */}
|
||||
<div
|
||||
className="flex justify-end px-3 pt-1 transition-[padding] duration-200 ease-out motion-reduce:transition-none"
|
||||
style={gridInset > 0 ? { paddingInline: 12 + gridInset } : undefined}
|
||||
>
|
||||
<div
|
||||
role="toolbar"
|
||||
aria-label={t('toolbarLabel')}
|
||||
className="dashboard-chrome flex items-center gap-1 rounded-lg"
|
||||
>
|
||||
{isEditMode && <BackgroundPicker value={background} onChange={chooseBackground} />}
|
||||
{isEditMode && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCatalogOpen(true)}
|
||||
className="btn btn-subtle"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<line x1="12" y1="5" x2="12" y2="19" />
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
</svg>
|
||||
{t('addWidget')}
|
||||
</button>
|
||||
)}
|
||||
<EditModeToggle
|
||||
isEditMode={isEditMode}
|
||||
onToggle={() => setEditMode(!isEditMode)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* 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 ? (
|
||||
<div className="flex min-h-[40vh] items-center justify-center">
|
||||
@@ -199,7 +198,6 @@ export default function DashboardPage() {
|
||||
onRemoveWidget={removeWidget}
|
||||
onOpenCatalog={() => setCatalogOpen(true)}
|
||||
onQuickAdd={(type) => addWidget(type)}
|
||||
onInsetChange={setGridInset}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user