refactor(settings/telemetry): single card with full sub-config disclosure

Collapses the two telemetry cards (Frontend telemetry + Advanced) into
one card so the master toggle and its sub-config (sampling + allowed
events) read as one coherent block. The conditional disclosure now
hides the entire sub-config when telemetry is off — previously only the
sampling fieldset hid, leaving Advanced visible with no effect.

The sampling select sits in a 2-column grid with an empty filler so it
keeps a sensible width instead of stretching across the page. Both
redundant info blockquotes are gone (the master switch carries a muted
hint, and the events block has its own caption). The simplified
component drops the now-redundant samplingRowSelector and consumes a
single data-telemetry-when-enabled wrapper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-25 18:22:23 +02:00
parent 5f3aff08cb
commit 9ee2589820
4 changed files with 57 additions and 73 deletions

View File

@@ -76,6 +76,7 @@
"Allow user theme": "Benutzer-Theme erlauben",
"Allowed CORS origins": "Erlaubte CORS-Origins",
"Allowed email domains": "Erlaubte E-Mail-Domains",
"Allowed events": "Erlaubte Events",
"Allowed file types: PNG": "Erlaubte Dateitypen: PNG",
"Allowed file types: SVG, PNG, JPG, WEBP": "Erlaubte Dateitypen: SVG, PNG, JPG, WEBP",
"Allowed range: 1-365 days (default: 30)": "Erlaubter Bereich: 1365 Tage (Standard: 30)",

View File

@@ -76,6 +76,7 @@
"Allow user theme": "Allow user theme",
"Allowed CORS origins": "Allowed CORS origins",
"Allowed email domains": "Allowed email domains",
"Allowed events": "Allowed events",
"Allowed file types: PNG": "Allowed file types: PNG",
"Allowed file types: SVG, PNG, JPG, WEBP": "Allowed file types: SVG, PNG, JPG, WEBP",
"Allowed range: 1-365 days (default: 30)": "Allowed range: 1-365 days (default: 30)",

View File

@@ -60,20 +60,17 @@ $disabledAttr = $isReadOnly ? 'disabled' : '';
?>
<form id="settings-telemetry-form" method="post" data-details-storage="settings-telemetry-details-v1" data-standard-detail-form="1">
<details class="app-details-card" name="settings-telemetry-core" data-details-key="settings-telemetry-core" open>
<details class="app-details-card" name="settings-telemetry" data-details-key="settings-telemetry" open>
<summary>
<span class="app-details-card-summary-title"><?php e(t('Frontend telemetry')); ?></span>
<span class="app-details-card-summary-meta">
<span class="badge" data-variant="neutral"><?php e($frontendTelemetryEnabled ? t('Enabled') : t('Disabled')); ?></span>
<?php if ($frontendTelemetryEnabled): ?>
<span class="badge" data-variant="neutral"><?php e($frontendTelemetrySampleRate); ?></span>
<?php endif; ?>
</span>
</summary>
<div class="app-details-card-container">
<blockquote data-variant="info">
<small><?php e(t('Telemetry helps detect recurring UI issues and failed requests in production.')); ?></small>
</blockquote>
<fieldset>
<legend><small><?php e(t('Frontend telemetry')); ?></small></legend>
<label class="app-field">
<input
type="checkbox"
@@ -85,12 +82,14 @@ $disabledAttr = $isReadOnly ? 'disabled' : '';
<?php e($disabledAttr); ?>>
<span><?php e(t('Enable frontend telemetry')); ?></span>
</label>
</fieldset>
<fieldset data-telemetry-sampling-fieldset <?php e($frontendTelemetryEnabled ? '' : 'hidden'); ?>>
<legend><small><?php e(t('Sampling rate')); ?></small></legend>
<label class="app-field" data-telemetry-sampling-row>
<small class="muted"><?php e(t('Telemetry helps detect recurring UI issues and failed requests in production.')); ?></small>
<div data-telemetry-when-enabled <?php e($frontendTelemetryEnabled ? '' : 'hidden'); ?>>
<hr>
<div class="grid">
<label class="app-field">
<span><?php e(t('Sampling rate')); ?></span>
<select name="frontend_telemetry_sample_rate" data-telemetry-sampling-select <?php e($disabledAttr); ?>>
<select name="frontend_telemetry_sample_rate" <?php e($disabledAttr); ?>>
<option value="0.1" <?php e($frontendTelemetrySampleRate === '0.1' ? 'selected' : ''); ?>>10%</option>
<option value="0.2" <?php e($frontendTelemetrySampleRate === '0.2' ? 'selected' : ''); ?>>20%</option>
<option value="0.5" <?php e($frontendTelemetrySampleRate === '0.5' ? 'selected' : ''); ?>>50%</option>
@@ -98,23 +97,11 @@ $disabledAttr = $isReadOnly ? 'disabled' : '';
</select>
<small><?php e(t('The percentage of users who will have telemetry enabled.')); ?></small>
</label>
</fieldset>
<div></div>
</div>
</details>
<details class="app-details-card" name="settings-telemetry-advanced" data-details-key="settings-telemetry-advanced">
<summary>
<span class="app-details-card-summary-title"><?php e(t('Advanced telemetry options')); ?></span>
<span class="app-details-card-summary-meta">
<span class="badge" data-variant="neutral"><?php e(sprintf(t('%d events enabled'), count($frontendTelemetryAllowedEvents))); ?></span>
</span>
</summary>
<div class="app-details-card-container">
<blockquote data-variant="info">
<small><?php e(t('Limit which event types are collected to match your privacy and operations requirements.')); ?></small>
</blockquote>
<fieldset>
<legend><small><?php e(t('Allowed telemetry events')); ?></small></legend>
<legend><small><?php e(t('Allowed events')); ?></small></legend>
<label class="app-field">
<input type="checkbox" name="frontend_telemetry_allowed_events[]" value="warn_once" <?php e(in_array('warn_once', $frontendTelemetryAllowedEvents, true) ? 'checked' : ''); ?> <?php e($disabledAttr); ?>>
<span><?php e(t('warnOnce warnings')); ?></span>
@@ -124,6 +111,8 @@ $disabledAttr = $isReadOnly ? 'disabled' : '';
<span><?php e(t('AJAX errors')); ?></span>
</label>
</fieldset>
<small class="muted"><?php e(t('Limit which event types are collected to match your privacy and operations requirements.')); ?></small>
</div>
</div>
</details>

View File

@@ -1,5 +1,7 @@
/**
* Tracks settings page interactions for telemetry.
* Hides the telemetry sub-config (sampling + allowed events) when the
* master toggle is off. The sub-block is one container so all dependent
* fields stay in sync together.
*/
import { resolveHost } from '../core/app-dom.js';
import { syncControlState } from '../core/app-conditional-controls.js';
@@ -7,30 +9,21 @@ import { syncControlState } from '../core/app-conditional-controls.js';
export const initTelemetrySettings = (root = document, options = {}) => {
const host = resolveHost(root);
const toggleSelector = String(options.toggleSelector || '[data-telemetry-enabled-toggle]').trim() || '[data-telemetry-enabled-toggle]';
const samplingFieldsetSelector = String(options.samplingFieldsetSelector || '[data-telemetry-sampling-fieldset]').trim() || '[data-telemetry-sampling-fieldset]';
const samplingRowSelector = String(options.samplingRowSelector || '[data-telemetry-sampling-row]').trim() || '[data-telemetry-sampling-row]';
const dependentSelector = String(options.dependentSelector || '[data-telemetry-when-enabled]').trim() || '[data-telemetry-when-enabled]';
const toggle = host.querySelector(toggleSelector);
const samplingFieldset = host.querySelector(samplingFieldsetSelector);
const samplingRow = host.querySelector(samplingRowSelector);
const dependent = host.querySelector(dependentSelector);
if (!toggle || !samplingFieldset) {
if (!toggle || !dependent) {
return { destroy: () => {} };
}
const syncSamplingVisibility = () => {
const active = Boolean(toggle.checked);
syncControlState(samplingFieldset, active);
if (samplingRow instanceof HTMLElement) {
samplingRow.hidden = !active;
}
};
toggle.addEventListener('change', syncSamplingVisibility);
syncSamplingVisibility();
const sync = () => syncControlState(dependent, Boolean(toggle.checked));
toggle.addEventListener('change', sync);
sync();
return {
destroy: () => {
toggle.removeEventListener('change', syncSamplingVisibility);
toggle.removeEventListener('change', sync);
},
};
};