129 lines
4.6 KiB
React
129 lines
4.6 KiB
React
|
|
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>
|
|||
|
|
)
|
|||
|
|
}
|