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:
+67
-30
@@ -89,16 +89,17 @@ final class AccessTab implements AdminTab
|
||||
public function render(): void
|
||||
{
|
||||
$allowed = $this->access->allowedRoles();
|
||||
$legend = __('Rollen mit Zugriff', 'wp-lit-mcp');
|
||||
|
||||
echo '<h2>' . esc_html__('Freigegebene Rollen', 'wp-lit-mcp') . '</h2>';
|
||||
echo '<p>' . esc_html__(
|
||||
'Nur Benutzer dieser Rollen können Tools nutzen; zusätzlich gelten ihre normalen WordPress-Rechte.',
|
||||
'wp-lit-mcp'
|
||||
) . '</p>';
|
||||
echo '<form method="post" action="options.php">';
|
||||
settings_fields(self::GROUP);
|
||||
// Makes an all-unchecked submission arrive as [''] instead of dropping the field.
|
||||
printf('<input type="hidden" name="%s[]" value="">', esc_attr(Access::OPTION));
|
||||
printf(
|
||||
'<table class="form-table" role="presentation"><tbody><tr><th scope="row">%1$s</th><td>'
|
||||
. '<fieldset><legend class="screen-reader-text"><span>%1$s</span></legend>',
|
||||
esc_html($legend)
|
||||
);
|
||||
foreach (wp_roles()->get_names() as $role => $name) {
|
||||
printf(
|
||||
'<label><input type="checkbox" name="%s[]" value="%s"%s> %s</label><br>',
|
||||
@@ -108,6 +109,10 @@ final class AccessTab implements AdminTab
|
||||
esc_html(translate_user_role((string) $name))
|
||||
);
|
||||
}
|
||||
echo '</fieldset><p class="description">'
|
||||
. esc_html__('Nur Benutzer dieser Rollen können Claude verbinden.', 'wp-lit-mcp') . ' '
|
||||
. esc_html__('Innerhalb der Tools gelten zusätzlich ihre normalen WordPress-Rechte.', 'wp-lit-mcp')
|
||||
. '</p></td></tr></tbody></table>';
|
||||
submit_button();
|
||||
echo '</form>';
|
||||
|
||||
@@ -160,31 +165,42 @@ final class AccessTab implements AdminTab
|
||||
|
||||
private function renderConnections(): void
|
||||
{
|
||||
echo '<h2>' . esc_html__('Aktive Verbindungen', 'wp-lit-mcp') . '</h2>';
|
||||
echo '<h2>' . esc_html__('Verbindungen', 'wp-lit-mcp') . '</h2>';
|
||||
$connections = $this->connections->all();
|
||||
|
||||
printf(
|
||||
'<table class="widefat striped lit-mcp-connections"><thead><tr>'
|
||||
. '<th scope="col" class="column-user">%s</th><th scope="col" class="column-client">%s</th>'
|
||||
. '<th scope="col" class="column-created">%s</th><th scope="col" class="column-last-used">%s</th>'
|
||||
. '<th scope="col" class="column-actions"><span class="screen-reader-text">%s</span></th>'
|
||||
. '</tr></thead><tbody>',
|
||||
esc_html__('Benutzer', 'wp-lit-mcp'),
|
||||
esc_html__('Client', 'wp-lit-mcp'),
|
||||
esc_html__('Verbunden am', 'wp-lit-mcp'),
|
||||
esc_html__('Zuletzt genutzt', 'wp-lit-mcp'),
|
||||
esc_html__('Aktionen', 'wp-lit-mcp')
|
||||
);
|
||||
|
||||
if ($connections === []) {
|
||||
echo '<p>' . esc_html__('Keine aktiven Verbindungen.', 'wp-lit-mcp') . '</p>';
|
||||
return;
|
||||
printf(
|
||||
'<tr class="no-items"><td class="colspanchange" colspan="5">%s %s <a href="%s">%s</a></td></tr>',
|
||||
esc_html__('Noch keine Verbindung.', 'wp-lit-mcp'),
|
||||
esc_html__('So verbindest du Claude:', 'wp-lit-mcp'),
|
||||
esc_url(SettingsPage::url('verbindung')),
|
||||
esc_html__('Tab „Verbindung“', 'wp-lit-mcp')
|
||||
);
|
||||
}
|
||||
|
||||
echo '<table class="widefat striped"><thead><tr>';
|
||||
$headings = [
|
||||
__('Benutzer', 'wp-lit-mcp'),
|
||||
__('Client', 'wp-lit-mcp'),
|
||||
__('Erstellt', 'wp-lit-mcp'),
|
||||
__('Zuletzt genutzt', 'wp-lit-mcp'),
|
||||
'',
|
||||
];
|
||||
foreach ($headings as $heading) {
|
||||
echo '<th>' . esc_html($heading) . '</th>';
|
||||
}
|
||||
echo '</tr></thead><tbody>';
|
||||
|
||||
foreach ($connections as $connection) {
|
||||
$this->renderConnectionRow($connection);
|
||||
}
|
||||
|
||||
echo '</tbody></table>';
|
||||
|
||||
if ($connections !== []) {
|
||||
echo '<p class="description">' . esc_html__(
|
||||
'„Zuletzt genutzt“ wird wie bei Anwendungspasswörtern höchstens einmal am Tag aktualisiert.',
|
||||
'wp-lit-mcp'
|
||||
) . '</p>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,21 +209,42 @@ final class AccessTab implements AdminTab
|
||||
*/
|
||||
private function renderConnectionRow(array $connection): void
|
||||
{
|
||||
$dateFormat = (string) get_option('date_format', 'd.m.Y');
|
||||
$timeFormat = (string) get_option('time_format', 'H:i');
|
||||
|
||||
echo '<tr>';
|
||||
echo '<td>' . esc_html($connection['user_login']) . '</td>';
|
||||
printf('<td>%s<br><code>%s</code></td>', esc_html($connection['name']), esc_html($connection['app_id']));
|
||||
echo '<td>' . esc_html((string) wp_date('Y-m-d H:i', $connection['created'])) . '</td>';
|
||||
echo '<td class="column-user">' . esc_html($connection['user_login']) . '</td>';
|
||||
printf(
|
||||
'<td class="column-client"><strong>%s</strong><br><span class="lit-mcp-muted">%s</span></td>',
|
||||
esc_html($connection['name']),
|
||||
esc_html($connection['app_id'])
|
||||
);
|
||||
printf(
|
||||
'<td class="column-created">%s</td>',
|
||||
esc_html((string) wp_date($dateFormat . ' ' . $timeFormat, $connection['created']))
|
||||
);
|
||||
$lastUsed = $connection['last_used'] === null
|
||||
? '–'
|
||||
: (string) wp_date('Y-m-d H:i', $connection['last_used']);
|
||||
echo '<td>' . esc_html($lastUsed) . '</td>';
|
||||
echo '<td>';
|
||||
: (string) wp_date($dateFormat, $connection['last_used']);
|
||||
echo '<td class="column-last-used">' . esc_html($lastUsed) . '</td>';
|
||||
echo '<td class="column-actions">';
|
||||
printf('<form method="post" action="%s">', esc_url(admin_url('admin-post.php')));
|
||||
printf('<input type="hidden" name="action" value="%s">', esc_attr(self::REVOKE_ACTION));
|
||||
printf('<input type="hidden" name="user_id" value="%s">', esc_attr((string) $connection['user_id']));
|
||||
printf('<input type="hidden" name="uuid" value="%s">', esc_attr($connection['uuid']));
|
||||
wp_nonce_field(self::REVOKE_ACTION . '_' . $connection['uuid']);
|
||||
submit_button(__('Widerrufen', 'wp-lit-mcp'), 'delete small', 'submit', false);
|
||||
$confirm = esc_js(sprintf(
|
||||
/* translators: %s: client name */
|
||||
__('Verbindung „%s“ widerrufen? Claude muss sich danach neu anmelden.', 'wp-lit-mcp'),
|
||||
$connection['name']
|
||||
));
|
||||
submit_button(
|
||||
__('Widerrufen', 'wp-lit-mcp'),
|
||||
'delete small',
|
||||
'submit',
|
||||
false,
|
||||
['onclick' => "return window.confirm('" . $confirm . "');"]
|
||||
);
|
||||
echo '</form></td></tr>';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user