feat: admin settings page UI/UX pass with WordPress-native patterns

- Header: h1 + logo in a wrapping flex row, hr.wp-header-end before the tabs,
  aria-current on the active tab; own CSS/JS enqueued only on this screen.
- Verbindung: ordered "So verbindest du Claude" steps, copy button (core
  clipboard.js + wp.a11y.speak), Site-Health-style status list, German details.
- Zugriff: roles as form-table fieldset, connections with site date format,
  empty-state row, confirm on revoke; ConnectionUsage records last use via
  WP_Application_Passwords::record_application_password_usage (once a day).
- Tools: plain-language legend, counter as inline notice, groups as core
  postboxes (postbox.js toggles, per-user closed state, summary when closed),
  fixed column widths with new "Art" column, submit above and below.
- Protokoll: site-timezone times with UTC tooltip, result icons, filters with
  labels in the list table nav, retention link to Limits.
- Limits: short labels with units and descriptions; snapshot count hidden but
  preserved.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-26 16:50:58 +02:00
co-authored by Claude Opus 5.5
parent 5965e32acb
commit 843f45aeec
22 changed files with 1535 additions and 242 deletions
+37 -19
View File
@@ -7,7 +7,8 @@ namespace LIT\MCP\Admin;
use LIT\MCP\Limits;
/**
* Tab "Limits": upload/download size, log retention, snapshot count (spec §8.4).
* Tab "Limits": upload/download size and log retention (spec §8.4). The snapshot count is
* kept as a hidden field until the design system feature that uses it exists.
* Changes apply immediately – Limits::all() always reads the current option.
*/
final class LimitsTab implements AdminTab
@@ -41,39 +42,56 @@ final class LimitsTab implements AdminTab
{
$values = (new Limits())->all();
$bounds = Limits::bounds();
$labels = [
'upload_mb' => sprintf(
/* translators: 1: maximum value in MB, 2: cap for Base64 transfers in MB */
__(
'Maximale Upload-/Download-Größe (MB, höchstens %1$d; Base64-Upload/Download höchstens %2$d MB)',
'wp-lit-mcp'
$fields = [
'upload_mb' => [
__('Maximale Dateigröße', 'wp-lit-mcp'),
__('MB', 'wp-lit-mcp'),
sprintf(
/* translators: 1: maximum value in MB, 2: cap for Base64 transfers in MB */
__('Höchstens %1$d MB; für Base64-Übertragungen höchstens %2$d MB.', 'wp-lit-mcp'),
$bounds['upload_mb'][1],
Limits::INLINE_MAX_MB
),
$bounds['upload_mb'][1],
Limits::INLINE_MAX_MB
),
'log_retention_days' => __('Protokoll aufbewahren (Tage)', 'wp-lit-mcp'),
'snapshot_count' => __('Anzahl Designsystem-Snapshots (ab Etappe 4)', 'wp-lit-mcp'),
],
'log_retention_days' => [
__('Protokoll aufbewahren', 'wp-lit-mcp'),
__('Tage', 'wp-lit-mcp'),
__('Ältere Einträge im Protokoll werden automatisch gelöscht.', 'wp-lit-mcp'),
],
];
echo '<form method="post" action="options.php">';
settings_fields(self::GROUP);
echo '<table class="form-table"><tbody>';
foreach ($labels as $key => $label) {
echo '<table class="form-table" role="presentation"><tbody>';
foreach ($fields as $key => [$label, $unit, $description]) {
[$min, $max] = $bounds[$key];
printf(
'<tr><th scope="row"><label for="lit-mcp-%1$s">%2$s</label></th><td>'
. '<input id="lit-mcp-%1$s" type="number" min="%3$d" max="%4$d" name="%5$s[%1$s]" value="%6$d">'
. '</td></tr>',
. '<input id="lit-mcp-%1$s" class="small-text" type="number" min="%3$d" max="%4$d"'
. ' name="%5$s[%1$s]" value="%6$d" aria-describedby="lit-mcp-%1$s-description">'
. ' <span class="lit-mcp-unit">%7$s</span>'
. '<p class="description" id="lit-mcp-%1$s-description">%8$s</p>',
esc_attr($key),
esc_html($label),
esc_attr((string) $min),
esc_attr((string) $max),
esc_attr(Limits::OPTION),
esc_attr((string) $values[$key])
esc_attr((string) $values[$key]),
esc_html($unit),
esc_html($description)
);
if ($key === 'upload_mb') {
$this->maybeRenderUploadSizeNotice($values['upload_mb']);
}
echo '</td></tr>';
}
echo '</tbody></table>';
$this->maybeRenderUploadSizeNotice($values['upload_mb']);
// Design system snapshots are not available yet: no visible setting, but keep the saved value.
printf(
'<input type="hidden" name="%s[snapshot_count]" value="%d">',
esc_attr(Limits::OPTION),
(int) $values['snapshot_count']
);
submit_button();
echo '</form>';
}
@@ -96,6 +114,6 @@ final class LimitsTab implements AdminTab
$uploadMb,
$serverMaxMb
);
printf('<p class="description">%s</p>', esc_html($message));
printf('<div class="notice notice-warning inline"><p>%s</p></div>', esc_html($message));
}
}