refactor(webui): use central widget selection
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { RefreshCw, RotateCcw, SlidersHorizontal } from "lucide-react";
|
import { RefreshCw, RotateCcw, SlidersHorizontal } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
|
AdminSelectionList,
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
MetricCard,
|
MetricCard,
|
||||||
@@ -50,14 +51,9 @@ export default function DashboardPage({ settings, auth }: { settings: ApiSetting
|
|||||||
writeLayout(storageKey, next);
|
writeLayout(storageKey, next);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setWidgetVisible(widgetId: string, visible: boolean) {
|
function setVisibleWidgets(selected: string[]) {
|
||||||
const allIds = widgets.map((widget) => widget.id);
|
const allIds = widgets.map((widget) => widget.id);
|
||||||
const nextVisible = new Set(visibleWidgetIds);
|
const nextVisible = new Set(selected);
|
||||||
if (visible) {
|
|
||||||
nextVisible.add(widgetId);
|
|
||||||
} else {
|
|
||||||
nextVisible.delete(widgetId);
|
|
||||||
}
|
|
||||||
saveLayout({
|
saveLayout({
|
||||||
known: allIds,
|
known: allIds,
|
||||||
visible: allIds.filter((id) => nextVisible.has(id))
|
visible: allIds.filter((id) => nextVisible.has(id))
|
||||||
@@ -93,21 +89,11 @@ export default function DashboardPage({ settings, auth }: { settings: ApiSetting
|
|||||||
title="Dashboard widgets"
|
title="Dashboard widgets"
|
||||||
actions={<Button onClick={resetLayout}><RotateCcw size={16} /> Reset</Button>}>
|
actions={<Button onClick={resetLayout}><RotateCcw size={16} /> Reset</Button>}>
|
||||||
{widgets.length === 0 ? <p className="muted">No installed module exposes dashboard widgets yet.</p> :
|
{widgets.length === 0 ? <p className="muted">No installed module exposes dashboard widgets yet.</p> :
|
||||||
<div className="dashboard-widget-picker">
|
<AdminSelectionList
|
||||||
{widgets.map((widget) =>
|
options={widgets.map((widget) => ({ id: widget.id, label: widget.title, description: widget.description ?? widget.id }))}
|
||||||
<label key={widget.id} className="dashboard-widget-toggle">
|
selected={visibleWidgetIds}
|
||||||
<input
|
onChange={setVisibleWidgets}
|
||||||
type="checkbox"
|
/>
|
||||||
checked={visibleWidgetIds.includes(widget.id)}
|
|
||||||
onChange={(event) => setWidgetVisible(widget.id, event.currentTarget.checked)}
|
|
||||||
/>
|
|
||||||
<span>
|
|
||||||
<strong>{widget.title}</strong>
|
|
||||||
<small>{widget.description ?? widget.id}</small>
|
|
||||||
</span>
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
}
|
}
|
||||||
</Card>
|
</Card>
|
||||||
}
|
}
|
||||||
@@ -183,4 +169,3 @@ function moduleLabels(modules: Array<{ id: string }>): string {
|
|||||||
if (!modules.length) return "Core shell only";
|
if (!modules.length) return "Core shell only";
|
||||||
return modules.slice(0, 4).map((module) => module.id).join(", ");
|
return modules.slice(0, 4).map((module) => module.id).join(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,39 +32,6 @@
|
|||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-widget-picker {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-widget-toggle {
|
|
||||||
min-width: 0;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: auto minmax(0, 1fr);
|
|
||||||
gap: 10px;
|
|
||||||
align-items: start;
|
|
||||||
border: var(--border-line);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
background: var(--panel-soft);
|
|
||||||
padding: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-widget-toggle input {
|
|
||||||
margin-top: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-widget-toggle strong,
|
|
||||||
.dashboard-widget-toggle small {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-widget-toggle small {
|
|
||||||
color: var(--muted);
|
|
||||||
margin-top: 4px;
|
|
||||||
line-height: 1.35;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dashboard-compact-list.detail-list div {
|
.dashboard-compact-list.detail-list div {
|
||||||
grid-template-columns: minmax(92px, auto) minmax(0, 1fr);
|
grid-template-columns: minmax(92px, auto) minmax(0, 1fr);
|
||||||
}
|
}
|
||||||
@@ -82,7 +49,6 @@
|
|||||||
|
|
||||||
@media (max-width: 900px) {
|
@media (max-width: 900px) {
|
||||||
.dashboard-widget-grid,
|
.dashboard-widget-grid,
|
||||||
.dashboard-widget-picker,
|
|
||||||
.dashboard-widget-metrics.metric-grid {
|
.dashboard-widget-metrics.metric-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
@@ -94,4 +60,3 @@
|
|||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user