feat(csv-import): Add custom row aggregation options in AdminCsvTemplateEditorPage
- Introduced a new section for row aggregation settings, allowing users to customize aggregation functions for imported CSV data. - Implemented functionality for users to save custom aggregation configurations and select key fields for aggregation. - Enhanced user interface with detailed instructions and options for managing row aggregation, improving overall usability in template management.
This commit is contained in:
parent
a51ee1d304
commit
ad7aa2d255
|
|
@ -965,6 +965,191 @@ export default function AdminCsvTemplateEditorPage() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{!aggregateSleepImport && (
|
||||||
|
<div className="card" style={{ padding: 16, marginBottom: 16 }}>
|
||||||
|
<div className="form-label">3a. Zeilenaggregation</div>
|
||||||
|
{!modMeta?.fields || Object.keys(modMeta.fields).length === 0 ? (
|
||||||
|
<p style={{ fontSize: 14, color: 'var(--text2)', marginTop: 8 }}>
|
||||||
|
Modul-Metadaten laden … bitte Seite kurz offen lassen oder neu laden.
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<p style={{ fontSize: 13, color: 'var(--text2)', marginTop: 8, lineHeight: 1.55 }}>
|
||||||
|
Mehrere CSV-Zeilen mit denselben Werten in den gewählten <strong>Schlüsselfeldern</strong> werden zu einer
|
||||||
|
importierten Zeile zusammengefasst. Für alle übrigen zugewiesenen Zielfelder gilt{' '}
|
||||||
|
<strong>eine gemeinsame</strong> Funktion. Textfelder werden bei Summe/Mittelwert usw. automatisch
|
||||||
|
ausgelassen; mit „Erster/Letzter Wert“ sind sie enthalten.
|
||||||
|
</p>
|
||||||
|
<label
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
gap: 10,
|
||||||
|
marginTop: 14,
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: 14,
|
||||||
|
color: 'var(--text1)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={rowAggUseCustom}
|
||||||
|
onChange={(e) => {
|
||||||
|
const on = e.target.checked
|
||||||
|
setRowAggUseCustom(on)
|
||||||
|
if (!on) {
|
||||||
|
setRowAggIrregular(false)
|
||||||
|
setRowAggGroupBy([])
|
||||||
|
setRowAggMode('')
|
||||||
|
setRowAggJsonText('{}')
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
style={{ marginTop: 3 }}
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
<strong>Eigene Aggregation in dieser Vorlage speichern.</strong> Wenn deaktiviert, gilt der{' '}
|
||||||
|
<strong>Modul-Standard</strong> (siehe unten) bzw. kein Aggregat, wenn das Modul keinen definiert.
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
{modMeta.import_row_processing_default && (
|
||||||
|
<details style={{ marginTop: 12, fontSize: 13, color: 'var(--text2)' }}>
|
||||||
|
<summary style={{ cursor: 'pointer' }}>Modul-Standard (Referenz, wenn Haken oben aus ist)</summary>
|
||||||
|
<pre
|
||||||
|
style={{
|
||||||
|
marginTop: 8,
|
||||||
|
padding: 12,
|
||||||
|
background: 'var(--surface2)',
|
||||||
|
borderRadius: 8,
|
||||||
|
overflow: 'auto',
|
||||||
|
fontSize: 12,
|
||||||
|
textAlign: 'left',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{JSON.stringify(modMeta.import_row_processing_default, null, 2)}
|
||||||
|
</pre>
|
||||||
|
</details>
|
||||||
|
)}
|
||||||
|
{rowAggUseCustom && (
|
||||||
|
<>
|
||||||
|
{modMeta.import_row_processing_default && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-secondary"
|
||||||
|
style={{ marginTop: 12 }}
|
||||||
|
onClick={() => {
|
||||||
|
const d = modMeta.import_row_processing_default
|
||||||
|
const p = parseStoredImportRowProcessing(d)
|
||||||
|
setRowAggIrregular(p.irregular)
|
||||||
|
setRowAggGroupBy(p.groupBy)
|
||||||
|
setRowAggMode(p.mode)
|
||||||
|
setRowAggJsonText(JSON.stringify(d, null, 2))
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Modul-Vorgabe übernehmen
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{rowAggIrregular ? (
|
||||||
|
<>
|
||||||
|
<p style={{ fontSize: 13, color: 'var(--text2)', marginTop: 14, lineHeight: 1.55 }}>
|
||||||
|
Diese Vorlage nutzt <strong>unterschiedliche</strong> Aggregations-Funktionen pro Feld. JSON
|
||||||
|
anpassen oder vereinheitlichen (pro-Feld-Auswahl folgt in einer späteren Ausbaustufe).
|
||||||
|
</p>
|
||||||
|
<textarea
|
||||||
|
className="form-input"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
minHeight: 160,
|
||||||
|
marginTop: 8,
|
||||||
|
fontFamily: 'monospace',
|
||||||
|
fontSize: 12,
|
||||||
|
textAlign: 'left',
|
||||||
|
}}
|
||||||
|
value={rowAggJsonText}
|
||||||
|
onChange={(e) => setRowAggJsonText(e.target.value)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className="form-label" style={{ marginTop: 16 }}>
|
||||||
|
Schlüsselfelder (Mehrfachauswahl)
|
||||||
|
</div>
|
||||||
|
<p style={{ fontSize: 12, color: 'var(--text3)', marginTop: 6 }}>
|
||||||
|
Nur bereits zugewiesene Zielfelder (Abschnitt 3).
|
||||||
|
</p>
|
||||||
|
{rowAggGroupCandidates.length === 0 ? (
|
||||||
|
<p style={{ fontSize: 13, color: 'var(--text2)', marginTop: 8 }}>
|
||||||
|
Noch keine Zielfelder zugewiesen — nach Zuweisung erscheinen die Schlüssel hier.
|
||||||
|
</p>
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
marginTop: 10,
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 8,
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{rowAggGroupCandidates.map((key) => (
|
||||||
|
<label
|
||||||
|
key={key}
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 8,
|
||||||
|
cursor: 'pointer',
|
||||||
|
fontSize: 14,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={rowAggGroupBy.includes(key)}
|
||||||
|
onChange={() => {
|
||||||
|
setRowAggIrregular(false)
|
||||||
|
setRowAggGroupBy((prev) =>
|
||||||
|
prev.includes(key) ? prev.filter((x) => x !== key) : [...prev, key],
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<code>{key}</code>
|
||||||
|
</label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<label className="form-label" style={{ marginTop: 16, display: 'block' }}>
|
||||||
|
Funktion für alle übrigen Zielfelder
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
className="form-input"
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: 420,
|
||||||
|
marginTop: 8,
|
||||||
|
textAlign: 'left',
|
||||||
|
minHeight: 46,
|
||||||
|
}}
|
||||||
|
value={rowAggMode}
|
||||||
|
onChange={(e) => {
|
||||||
|
setRowAggIrregular(false)
|
||||||
|
setRowAggMode(e.target.value)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<option value="">— wählen —</option>
|
||||||
|
{ROW_AGG_OPS.map((o) => (
|
||||||
|
<option key={o.value} value={o.value}>
|
||||||
|
{o.label}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{unitTargets.length > 0 && (
|
{unitTargets.length > 0 && (
|
||||||
<div className="card" style={{ padding: 16, marginBottom: 16 }}>
|
<div className="card" style={{ padding: 16, marginBottom: 16 }}>
|
||||||
<div className="form-label">3b. Quelleinheit (optional)</div>
|
<div className="form-label">3b. Quelleinheit (optional)</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user