mitai-jinkendo/frontend/src/components/pilot/PilotVizAdminCard.jsx

129 lines
4.6 KiB
React
Raw Normal View History

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>
)
}