- Replaced the previous widget retrieval method with a new layout-based approach in PilotVizPage. - Introduced a PilotVizAdminCard for layout configuration and management. - Updated widget definitions in the registry to include new components and default order. - Enhanced user feedback for widget visibility with a message when no widgets are active.
129 lines
4.6 KiB
JavaScript
129 lines
4.6 KiB
JavaScript
import { Settings2, RotateCcw, ChevronUp, ChevronDown } from 'lucide-react'
|
||
import { PILOT_WIDGET_DEFS, PILOT_WIDGET_IDS_DEFAULT_ORDER } from '../../pilot/widgetRegistry'
|
||
import { resetPilotLayout, savePilotLayout } from '../../pilot/pilotLayoutStorage'
|
||
|
||
/**
|
||
* Pilot: lokale Konfiguration (Ein/Aus, Reihenfolge). Kein Admin-Recht nötig.
|
||
*/
|
||
export default function PilotVizAdminCard({ layout, onLayoutChange }) {
|
||
const persist = (next) => {
|
||
savePilotLayout(next)
|
||
onLayoutChange(next)
|
||
}
|
||
|
||
const move = (index, dir) => {
|
||
const nextOrder = [...layout.order]
|
||
const j = index + dir
|
||
if (j < 0 || j >= nextOrder.length) return
|
||
;[nextOrder[index], nextOrder[j]] = [nextOrder[j], nextOrder[index]]
|
||
persist({ ...layout, order: nextOrder })
|
||
}
|
||
|
||
const toggle = (id) => {
|
||
persist({
|
||
...layout,
|
||
enabled: { ...layout.enabled, [id]: !layout.enabled[id] },
|
||
})
|
||
}
|
||
|
||
const handleReset = () => {
|
||
onLayoutChange(resetPilotLayout())
|
||
}
|
||
|
||
return (
|
||
<section
|
||
className="card section-gap"
|
||
style={{
|
||
borderStyle: 'solid',
|
||
borderColor: 'var(--accent)',
|
||
borderWidth: 1,
|
||
background: 'var(--surface)',
|
||
}}
|
||
>
|
||
<div className="card-title" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||
<Settings2 size={18} color="var(--accent)" />
|
||
Widget-Konfiguration (Pilot)
|
||
</div>
|
||
<p style={{ fontSize: 12, color: 'var(--text2)', marginTop: 4, marginBottom: 14, lineHeight: 1.5 }}>
|
||
Sichtbarkeit und Reihenfolge steuern. Wird nur <strong>lokal in diesem Browser</strong> gespeichert
|
||
(<code style={{ fontSize: 11 }}>localStorage</code>) – gut zum Ausprobieren vor einer serverseitigen
|
||
Profil-Konfiguration.
|
||
</p>
|
||
|
||
<ul style={{ listStyle: 'none', margin: 0, padding: 0, display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||
{layout.order.map((id, index) => {
|
||
const def = PILOT_WIDGET_DEFS[id]
|
||
if (!def) return null
|
||
const on = !!layout.enabled[id]
|
||
return (
|
||
<li
|
||
key={id}
|
||
style={{
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8,
|
||
flexWrap: 'wrap',
|
||
padding: '10px 12px',
|
||
borderRadius: 10,
|
||
background: on ? 'var(--surface2)' : 'var(--surface)',
|
||
border: '1px solid var(--border)',
|
||
opacity: on ? 1 : 0.72,
|
||
}}
|
||
>
|
||
<div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||
<button
|
||
type="button"
|
||
className="btn btn-secondary"
|
||
style={{ padding: '4px 8px', minWidth: 36 }}
|
||
title="Nach oben"
|
||
disabled={index === 0}
|
||
onClick={() => move(index, -1)}
|
||
>
|
||
<ChevronUp size={16} />
|
||
</button>
|
||
<button
|
||
type="button"
|
||
className="btn btn-secondary"
|
||
style={{ padding: '4px 8px', minWidth: 36 }}
|
||
title="Nach unten"
|
||
disabled={index === layout.order.length - 1}
|
||
onClick={() => move(index, 1)}
|
||
>
|
||
<ChevronDown size={16} />
|
||
</button>
|
||
</div>
|
||
<div style={{ flex: 1, minWidth: 140 }}>
|
||
<div style={{ fontSize: 13, fontWeight: 600, color: 'var(--text1)' }}>{def.title}</div>
|
||
<code style={{ fontSize: 10, color: 'var(--text3)' }}>{id}</code>
|
||
</div>
|
||
<label
|
||
style={{
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
gap: 8,
|
||
fontSize: 13,
|
||
cursor: 'pointer',
|
||
userSelect: 'none',
|
||
}}
|
||
>
|
||
<input type="checkbox" checked={on} onChange={() => toggle(id)} />
|
||
sichtbar
|
||
</label>
|
||
</li>
|
||
)
|
||
})}
|
||
</ul>
|
||
|
||
<div style={{ marginTop: 14, display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'center' }}>
|
||
<button type="button" className="btn btn-secondary" onClick={handleReset} style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
|
||
<RotateCcw size={14} />
|
||
Standard wiederherstellen
|
||
</button>
|
||
<span style={{ fontSize: 11, color: 'var(--text3)' }}>
|
||
Standard-Reihenfolge: {PILOT_WIDGET_IDS_DEFAULT_ORDER.join(' → ')}
|
||
</span>
|
||
</div>
|
||
</section>
|
||
)
|
||
}
|