0.12.0-1 Plugins und diverses

This commit is contained in:
2026-03-19 18:57:05 +00:00
parent 0a9cf6969f
commit 24955a956d
33 changed files with 3978 additions and 479 deletions
+75 -12
View File
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { TabView, TabPanel } from 'primereact/tabview';
import { Accordion, AccordionTab } from 'primereact/accordion';
import { InputText } from 'primereact/inputtext';
@@ -357,6 +357,17 @@ function isNotificationEventToggleSetting(setting) {
return setting?.type === 'boolean' && NOTIFICATION_EVENT_TOGGLE_KEYS.has(normalizeSettingKey(setting?.key));
}
function isReadonlySetting(setting) {
try {
// API gibt validation als parsed object zurück, nicht validation_json als String
const v = setting?.validation;
if (v && typeof v === 'object') return v.readonly === true;
return JSON.parse(setting?.validation_json || '{}')?.readonly === true;
} catch {
return false;
}
}
function SettingField({
setting,
value,
@@ -367,7 +378,8 @@ function SettingField({
ownerError,
ownerDirty,
onChange,
variant = 'default'
variant = 'default',
disabled = false
}) {
const ownerKey = ownerSetting?.key;
const pathHasValue = Boolean(String(value ?? '').trim());
@@ -384,13 +396,15 @@ function SettingField({
<InputSwitch
id={setting.key}
checked={Boolean(value)}
onChange={(event) => onChange?.(setting.key, event.value)}
disabled={disabled}
onChange={disabled ? undefined : (event) => onChange?.(setting.key, event.value)}
/>
</div>
) : (
<label htmlFor={setting.key}>
<label htmlFor={setting.key} className={disabled ? 'setting-label--readonly' : undefined}>
{setting.label}
{setting.required && <span className="required">*</span>}
{disabled ? <span className="setting-badge--coming-soon"> (bald)</span> : null}
</label>
)}
@@ -424,7 +438,8 @@ function SettingField({
<InputSwitch
id={setting.key}
checked={Boolean(value)}
onChange={(event) => onChange?.(setting.key, event.value)}
disabled={disabled}
onChange={disabled ? undefined : (event) => onChange?.(setting.key, event.value)}
/>
) : null}
@@ -780,10 +795,22 @@ export default function DynamicSettingsForm({
const notificationToggleKeys = new Set(
notificationToggleSettings.map((setting) => normalizeSettingKey(setting?.key))
);
const regularSettings = baseSettings.filter(
(setting) => !notificationToggleKeys.has(normalizeSettingKey(setting?.key))
);
const renderSetting = (setting, variant = 'default') => {
// Abhängigkeitsbaum aufbauen: depends_on → Kinder
const dependentsByParent = new Map();
const rootSettings = [];
for (const setting of baseSettings) {
if (notificationToggleKeys.has(normalizeSettingKey(setting?.key))) continue;
const dep = setting?.depends_on;
if (dep) {
if (!dependentsByParent.has(dep)) dependentsByParent.set(dep, []);
dependentsByParent.get(dep).push(setting);
} else {
rootSettings.push(setting);
}
}
const renderSetting = (setting, variant = 'default', disabled = false, onChangeFn = onChange) => {
const value = values?.[setting.key];
const error = errors?.[setting.key] || null;
const dirty = Boolean(dirtyKeys?.has?.(setting.key));
@@ -805,17 +832,53 @@ export default function DynamicSettingsForm({
ownerValue={ownerValue}
ownerError={ownerError}
ownerDirty={ownerDirty}
onChange={onChange}
onChange={onChangeFn}
variant={variant}
disabled={disabled}
/>
);
};
// Alle Nachkommen eines Keys rekursiv auf false setzen
const cascadeOff = (key) => {
const children = dependentsByParent.get(key) || [];
for (const child of children) {
onChange?.(child.key, false);
cascadeOff(child.key);
}
};
// Rekursiv: Setting + seine Kinder rendern
// Wenn ein Parent auf false geht → alle Kinder werden auf false gesetzt
const renderWithChildren = (setting, depth = 0) => {
const children = dependentsByParent.get(setting.key) || [];
const readonly = depth > 0 && isReadonlySetting(setting);
const active = toBoolean(values?.[setting.key]);
const effectiveOnChange = children.length > 0
? (key, value) => {
onChange?.(key, value);
if (!value) cascadeOff(key);
}
: onChange;
return (
<React.Fragment key={setting.key}>
{renderSetting(setting, 'default', readonly, effectiveOnChange)}
{children.length > 0 && active && !readonly ? (
<div className={`settings-grid settings-grid--depth-${depth + 1}`}>
{children.map((child) => renderWithChildren(child, depth + 1))}
</div>
) : null}
</React.Fragment>
);
};
return (
<>
{regularSettings.length > 0 ? (
{rootSettings.length > 0 ? (
<div className="settings-grid">
{regularSettings.map((setting) => renderSetting(setting))}
{rootSettings.map((setting) => renderWithChildren(setting, 0))}
</div>
) : null}
{pushoverEnabled && notificationToggleSettings.length > 0 ? (