Skip to content

Feat/ettic admin UI - changes to the admin backend to get in line with new ettic backend ui styling#7

Draft
r00bbert wants to merge 11 commits into
mainfrom
feat/ettic-admin-ui
Draft

Feat/ettic admin UI - changes to the admin backend to get in line with new ettic backend ui styling#7
r00bbert wants to merge 11 commits into
mainfrom
feat/ettic-admin-ui

Conversation

@r00bbert
Copy link
Copy Markdown
Collaborator

@r00bbert r00bbert commented May 11, 2026

This PR conflicts with changes I made on #5 please first merge #5 then rebase and merge this PR

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced a comprehensive admin design system with modernized styling and reusable components across all admin pages.
  • Refactor

    • Redesigned Settings page with improved form controls, layout, and user experience.
    • Updated AI, Questions, and Import/Export pages to use the new design system styling.
    • Added admin footer displaying plugin version and resource links.
    • Optimized admin asset loading for better performance.
  • Documentation

    • Updated README with latest stable release information and development branch clarification.
  • Chores

    • Expanded translation strings for admin UI.

Review Change Stack

nolderoos and others added 11 commits May 11, 2026 14:32
Drops the shared Ettic admin design system into the plugin:

- assets/css/opentrust-admin.css, assets/js/opentrust-admin.js — the design
  tokens, components, and dirty-tracking/toast/modal JS used by upcoming
  admin migrations. New asset paths and handles; existing assets/css/admin.css
  and assets/js/admin.js are untouched.
- includes/Admin/Footer.php — branded footer used across every OpenTrust
  admin screen. URL_DOCS points at plugins.ettic.nl/opentrust.
- includes/Admin/Settings.php — reference implementation of the design
  system's page shell (topbar + sections + every control type). Loaded by
  PHPStan path scan but NOT booted; OpenTrust's existing admin classes
  migrate to the same markup vocabulary in the commits that follow.
- opentrust.php — defines OPENTRUST_FILE / OPENTRUST_URL as aliases for
  the existing OPENTRUST_PLUGIN_FILE / OPENTRUST_PLUGIN_URL, so the
  shared-template files run unmodified. Requires Footer.php on load.

Behavior unchanged in this commit: no menu page registered by the new
files, no enqueue, no markup. Just installed and ready.

Template source: /Users/nolderoos/Claude Code/Ettic Admin UI (post v1).
Wraps OpenTrust_Admin_Settings::render_settings_page() in .opentrust-admin
and emits the shared template's dark topbar + footer:

- Topbar bar: brand mark (OpenTrust shield, 26x26 white-on-blue), version
  pill, dirty-changes counter (data-dirty), Discard + Save buttons. Save
  carries form="opentrust-settings-form" so it submits the active tab's
  Settings API form from outside it. Discard reloads.
- Topbar head: page title + one-paragraph description; "View Trust Center"
  link moves into the topbar as a ghost-dark button.
- Tabbar (NEW component, OpenTrust-only): the 4 tabs render as a light
  strip BELOW the topbar instead of inside it. The shared template's "no
  tabs in topbar" invariant assumes plugins use WP submenus for section
  nav; OpenTrust's ?tab=... URLs are user-bookmarkable so we keep them
  and style them as a separate component. Marked clearly in the CSS as
  not-for-upstream-extraction.
- Footer: \OpenTrust\Admin\Footer::render() at the bottom of every tab.
- AI tab "Live" pill: re-themed as .opentrust-tabbar__badge (replaces the
  inline-styled .ot-pill--live).

General + Contact forms now have id="opentrust-settings-form" so the
topbar Save submits them. The per-tab submit_button() is dropped. On the
AI + IO tabs the Save/Discard buttons aren't rendered (they have bespoke
admin-post forms that wire up in their respective migration commits).

class-opentrust-admin.php enqueues opentrust-admin.css/js as a separate
handle (opentrust-admin-ds) on plugin pages only, never on CPT meta-box
screens. Existing admin.css/admin.js stay loaded for the CPT screens.

Field markup inside the forms is untouched — that migrates per-tab in
commits 3-7. Mid-migration the WP-native form-table rows will look a bit
out of place inside the design-system wrap; that's expected.
Replaces do_settings_sections('opentrust-settings-general') with a
manual render that emits .opentrust-block + .opentrust-card + .opentrust-row
markup. The register_setting + sanitize cascade are untouched — only the
render side moves to design-system markup.

Sections:
- General  → endpoint slug, page title, company name, tagline
- Branding → logo + avatar via .opentrust-media (template's wp.media wiring
  via [data-opentrust-media-picker], replacing the legacy data-ot-media-*
  bindings for these two fields), accent color via .opentrust-color (native
  swatch + hex pair, the existing contrast-warning widget hangs beneath as
  a .opentrust-row__control--stack child), credit toggle
- Visible Sections → six chip toggles (.opentrust-chip with the template's
  :has(:checked) selector, no JS needed)

Color picker:
- Drops wp-color-picker for the accent field (existing $('.ot-color-picker')
  init now finds nothing — kept in admin.js for commit 9 cleanup since
  removing the wp-color-picker dep also requires unhooking the style/script
  enqueue in class-opentrust-admin.php, out of scope here).
- admin.js accent-warning code now binds to the design system's hex text
  input via `input` events. opentrust-admin.js already syncs swatch→text on
  input, so swatch picks and direct hex typing both reach updateAccentWarning.

The legacy add_settings_section('opentrust_general'...) + opentrust_branding
+ opentrust_sections registrations stay in register_settings(). They no
longer render because do_settings_sections is gone, but they're harmless
and removing them is bundled into the commit 9 cleanup once Contact + AI
tabs have also migrated and the old render_*_field methods can be deleted
in one sweep.

PHPStan level 5 clean.
Replaces do_settings_sections('opentrust-settings-contact') with a manual
ds_render_section_contact() emitting .opentrust-block + .opentrust-card +
.opentrust-row markup. Same pattern as the General tab migration in the
previous commit.

Adds ds_row_text_typed() for email/url inputs so the native HTML5 type
flows through (mobile keyboards, inline validation, autofill hints) while
the row shell stays consistent with ds_row_text().

The legacy register_settings() entries for the Contact tab stay registered
but un-rendered; they get pruned together with the other un-used Settings
API registrations once AI + IO tabs have also migrated.

PHPStan level 5 clean.
Rewrites OpenTrust_Admin_AI::render_ai_tab(), the provider picker, the
provider cards, and the main AI settings form to emit design-system
markup. All admin-post handlers (key save/forget/refresh, summary sweep)
stay untouched.

- render_ai_tab() now wraps the intro + rationale in an .opentrust-block
  with a card containing an .opentrust-disclosure (new component — see
  the CSS extension block).
- Transient notices switch from WP-native .notice to .opentrust-notice
  variants (success/error/warn/info) via a shared ds_notice() helper.
- Summary backfill banner: .opentrust-notice--warn with an embedded
  admin-post form in .opentrust-notice__actions, primary ghost button.
- Non-anthropic active warning: .opentrust-notice--warn.
- Provider picker: .opentrust-block headed "Step 1 — Connect Anthropic",
  primary card uses .opentrust-card + .opentrust-ai-card--primary
  variant. Advanced disclosure wraps the existing flat grid of
  .opentrust-ai-card--advanced cards.
- Provider card: same content (title/badge/keylink/saved-state/form)
  but emits design-system input + button classes, and the saved-state
  ✓ icon becomes a structured .opentrust-ai-card__check pill matching
  the design language.
- AI settings form: id="opentrust-settings-form" so the topbar Save in
  the wrap submits it. submit_button() dropped. The table.form-table
  becomes two .opentrust-block sections: "Step 2 — Model & defaults"
  (model + budgets + rate limits + max-msg + contact URL + three
  toggles) and "Anti-abuse — Cloudflare Turnstile" (toggle + site key
  + secret key with masked-bullet placeholder for the encrypted blob).
- AI tab is now treated as has_settings_form so the topbar Save +
  Discard render. The opentrust-admin.js dirty tracker prefers the form
  the Save button is wired to (via HTML5 form="..." attribute) instead
  of just .opentrust-admin form, so the AI tab's many sub-forms (key
  save, refresh, forget) don't confuse the dirty tracker.
- Oversized-policies warning becomes an .opentrust-notice--error with a
  .opentrust-notice__list bullet list.

CSS additions appended to opentrust-admin.css under an OpenTrust-only
extensions block: .opentrust-disclosure, .opentrust-ai-card variants,
.opentrust-ai-advanced__grid, .opentrust-ai-model-row, .opentrust-row__unit,
.opentrust-field-msg--success, .opentrust-notice__list, .opentrust-notice__actions.

Local ds_row_number() and ds_row_toggle() helpers live in
OpenTrust_Admin_AI because OpenTrust_Admin_Settings::ds_row_* are
private. Lifting them into a shared helper class can happen alongside
the Settings API registrations cleanup in commit 9.

PHPStan level 5 clean.
OpenTrust_Admin_Questions::render_page() now wraps in .opentrust-admin
with the dark topbar (brand mark + version + back-to-AI-settings link +
View Trust Center link), no Save (read-only screen), the topbar__head
title block, an .opentrust-stack of section blocks, and the shared
\OpenTrust\Admin\Footer at the bottom.

Each surface on the page is rebuilt with design-system markup:
- Logging on/off banner → .opentrust-notice--success or --warn with an
  inline toggle action in .opentrust-notice__actions.
- Filter form → .opentrust-block + .opentrust-card with a new
  .opentrust-filterbar layout (label-above-input columns + grouped
  actions on the right). Inputs and select use design system classes.
- Log table → .opentrust-card--flush (new variant — drops card padding
  so the table can span edges) containing a token-styled
  .opentrust-log-table. Refused rows highlight in warn tones; meta
  columns use mono, tabular-nums, and muted text.
- Pagination → paginate_links() output restyled inside
  .opentrust-log-table__pagination (rounded chip per page, blue
  active, gray dots).
- Danger zone → .opentrust-action-row inside a card with a
  .opentrust-btn--danger Clear button (kept the same confirm() flow).

CSS additions (Questions-only extensions block in opentrust-admin.css):
.opentrust-card--flush, .opentrust-filterbar*, .opentrust-log-table*,
plus the pagination styling that overrides paginate_links()'s defaults.

Logic is unchanged — same filter handling, same query, same export /
clear / toggle-logging admin-post handlers.

PHPStan level 5 clean.
Rewrites OpenTrust_Admin_Tools::render_tab(), render_export_panel(),
render_import_panel(), and render_preview_screen() to use design-system
markup. All admin-post handlers (export, import_preview, import_apply)
are unchanged.

- Transient notices switch to .opentrust-notice variants.
- Tab intro becomes an .opentrust-block header.
- Export panel: own .opentrust-block + .opentrust-card. The kind picker
  (content vs settings) becomes an .opentrust-seg segmented control.
  Content selection uses an .opentrust-io-cpt-list of <details> groups
  (one per CPT) with nested checkboxes. "Bundle media" becomes a
  .opentrust-toggle row. Submit moves into an .opentrust-action-row.
- Import panel: warn banner becomes .opentrust-notice--warn. File input
  gets an .opentrust-input--file shim (dashed border, native chrome
  retained). Conflict strategy becomes an .opentrust-seg with three
  options. Submit moves into an .opentrust-action-row.
- Preview screen: errors/warnings as .opentrust-notice--error/--warn
  with .opentrust-notice__list bullet lists. Per-CPT preview tables
  reuse .opentrust-log-table styling inside .opentrust-card--flush
  with a new .opentrust-card__header bar above each. Action column
  becomes .opentrust-io-preview-table__pill with create/update/skip/
  new color variants. Confirm + Cancel buttons move into an
  .opentrust-io-confirm row at the bottom.

CSS additions appended to opentrust-admin.css under the I&E extensions
block: .opentrust-io-cpt-list / .opentrust-io-cpt*, .opentrust-input--file,
.opentrust-card__header / .opentrust-card__title, the preview-table
pills, and .opentrust-io-confirm.

PHPStan level 5 clean.
Now that every settings tab renders through ds_render_section_*, the
old Settings API field plumbing is dead weight. Removes:

- The `add_field()` helper and every `add_settings_section` /
  `add_settings_field` call from OpenTrust_Admin_Settings::register_settings()
  (~95 lines of registration).
- The eight render_*_field methods + the shared render_input_field()
  and render_media_field() (~190 lines). One stays on the page in
  spirit — ds_row_* — but no consumer needs the legacy variants.
- The wp-color-picker style enqueue and the 'wp-color-picker' dep on
  the admin.js script. Settings now uses the design system's native
  .opentrust-color widget; the only place that registered the jQuery
  picker had already migrated away in commit 3.
- The dead `$('[data-ot-media-field]')` block in admin.js (logo/avatar
  uploads moved to the template's [data-opentrust-media-picker]).
- The .ot-logo-upload / .ot-logo-preview CSS rules. CPT meta-box
  uploads use different classes (.ot-policy-attachment-*,
  .ot-upload-badge, .ot-upload-artifact) and remain untouched.

Top-of-file docblock for class-opentrust-admin-settings.php updated to
match the new model: no add_settings_section / add_settings_field; page
is rendered manually with ds_render_section_*.

`wp i18n make-pot` regenerated — captures every new translatable string
from the design-system migration (mostly section labels, help copy,
button labels, and notice text).

Plain-permalinks notice (OpenTrust_Admin::render_plain_permalinks_notice)
and the WP.org review prompt are intentionally left as WP-native
`.notice` markup: they also appear on CPT edit screens which are out of
design-system scope, so swapping to .opentrust-notice classes would
leave them unstyled on those screens.

PHPStan level 5 clean. Grep -rni 'pluginslug' returns 0 hits.
…t need it

Two follow-ups from the first review of the migrated admin:

1. Tab switching silently dropped in-flight edits. Each tab is its own
   page load, so navigating to ?tab=other discards anything you typed
   on the current tab — and the topbar dirty counter, which reset to 0
   on the next page, made it look like the changes had been saved.

   Adds two guards:
   - .opentrust-tabbar tab clicks are intercepted while dirty count > 0.
     The user sees a typed showConfirm() modal ("Discard and switch" /
     "Stay on this tab") that makes the loss explicit and offers a
     clean off-ramp.
   - A window beforeunload handler catches everything else — back
     button, address bar nav, window close — and surfaces the browser's
     native "Leave site?" prompt. A module-scoped navConsented flag
     bypasses the prompt on Save click, Discard click, and confirmed
     tab switch, so user-initiated nav stays frictionless.

   Cross-tab persistence (sessionStorage'ing one tab's input while
   editing another) was deliberately not chosen. The settings option
   has encrypted secrets, file inputs, nested arrays, and masked
   placeholders — snapshotting and restoring all of that in JS would
   be a maintenance hazard. WP convention is "leaving the page drops
   unsaved changes"; the guards just make that convention legible.

2. Tab switching felt slow. The dominant cost was wp_enqueue_media()
   firing on every plugin admin screen — ~150 KB of media-uploader JS
   that only the General tab (logo + AI avatar) and the CPT edit
   screens (badge / artifact / policy PDF) actually use. The Contact,
   AI, IO, and Questions screens load it for no reason.

   Gates wp_enqueue_media() to the screens that render a media
   picker. The General tab still gets it; everywhere else does not.

   Other contributors (AI tab corpus warmup, IO tab's five
   posts_per_page=-1 queries, libsodium decrypt of every stored key)
   pre-date this PR and are out of scope here.

PHPStan level 5 clean.
Simplify pass (from three-agent review):
- Fix <br> literal-text rendering in the IO import error message
- Translate IO preview action pills (was English-only ucfirst())
- Use provider->label() instead of ucfirst($slug) in AI tab warning
- Localize tab-switch modal strings via window.OpenTrustAdmin.i18n
- Drop redundant get_current_screen() reassignment in enqueue_assets
- Normalize Questions log filter param (q -> search) so pagination
  and export URLs stop carrying both names; also fixes pagination
  base URL silently overriding ?page=opentrust-questions with the
  pagination-page integer
- Lift tab allowlist to OpenTrust_Admin_Settings::TABS const
- Trim AI-style file headers on admin-settings/admin-ai
- Regenerate POT for new translatable strings

Visual polish (from screenshot review):
- 40px gap between section blocks on every tab (the form wrapper on
  General/Contact/AI now flexes too, so direct-child tabs like IO no
  longer double-count gap + margin)
- Move "View Trust Center" out of the dark sticky bar into the hero
  head, mirrored on the Questions screen
- Dark preview background for the Logo media picker so a white-on-
  dark logo previews on its real surface
- New .opentrust-row__control--stack-left modifier; applied to
  accent picker, sections chips, and IO content selection
- .opentrust-io-cpt-list loses its border/bg/padding so the CPT
  picker flows inline under its label
- Match action-row horizontal padding to .opentrust-row (8 -> 14px)
- New .opentrust-notice--bare variant for the inline import warning
- Free-float Cancel/Confirm under the import preview (no card wrap)
@r00bbert r00bbert added the enhancement New feature or request label May 11, 2026
@r00bbert r00bbert added this to the 1.1.0 milestone May 11, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 11, 2026

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive admin UI redesign by implementing a scoped design system (opentrust-admin) with a complete CSS component library and vanilla-JS utilities, then migrates all existing OpenTrust admin pages (Settings, AI, Questions, Import/Export) from WordPress Settings API and legacy table-based markup to the new design-system-driven layout. It includes new Settings page scaffolding, shared Footer component, dirty-state tracking, form validation, toast notifications, and modal dialogs.

Changes

Admin Design System Implementation & Migration

Layer / File(s) Summary
Design System Tokens & Base Styles
assets/css/opentrust-admin.css
Establishes .opentrust-admin scope with color tokens (brand, surface, text, status), typography, radii, foundational global rules (box-sizing, links, code), and responsive layout overrides.
Topbar & Header Layout
assets/css/opentrust-admin.css
Implements sticky dark topbar with brand cluster (mark/name/version), responsive admin-bar offsets, mobile handling, and per-row dirty-state indicator with pulse animation and reduced-motion support.
Form Controls & Input Styling
assets/css/opentrust-admin.css
Defines CSS-only toggles, inputs with focus/invalid/error variants, select dropdowns with custom caret, color picker UI (swatch + hex fusion), segmented controls, and chips with checked/hover states.
Overlays, Modals & Notifications
assets/css/opentrust-admin.css
Implements modal backdrop + layout/animations, toast notifications (bottom-right with in/out animations), tooltips (trigger + bubble), and notification variants (info, success, warn, error) with icons and close buttons.
OpenTrust-Specific Components & Polish
assets/css/opentrust-admin.css
Adds tabbar strip, AI tab atoms (disclosure/details, provider/advanced cards, model row), Questions log styles (filter toolbar, flush cards, log table, pagination), Import/Export styles (CPT picker, preview tables, pill labels, UUID formatting), and visual polish overrides.
JavaScript Bootstrap & Initialization
assets/js/opentrust-admin.js
Sets up DOM-ready IIFE that calls a sequence of initializer functions to wire media pickers, color syncing, dirty tracking, form validation, notice/toast handling, and modal behavior.
Media & Color Picker Wiring
assets/js/opentrust-admin.js
Implements WordPress media picker integration for data-opentrust-media-picker elements and synchronized color picking between native color input and hex text input with #RRGGBB normalization.
Dirty State Tracking & Form Management
assets/js/opentrust-admin.js
Injects per-row dirty indicators, captures initial field signatures, tracks changes via input/change events, toggles Save/Discard buttons, and updates per-row is-dirty classes and unsaved-change messaging.
Input Validation & Character Counters
assets/js/opentrust-admin.js
Adds live hex validation for data-validate-hex inputs with inline error messages, and character counters for data-counter inputs with warning/error threshold styling.
Form Navigation Guard & Tab Switching
assets/js/opentrust-admin.js
Intercepts tab-switch clicks to show confirmation modal when leaving unsaved changes, manages navigation consent state, blocks beforeunload when dirty, and performs redirect/discard via sessionStorage.
Notice Dismissal & Toast System
assets/js/opentrust-admin.js
Implements notice close-button animations with fade/slide, toast stack infrastructure with type-based icons and auto-dismiss, toast triggering from URL query params, and WP notice scooper that reroutes settings errors into toasts.
Confirm Modal & Button Utilities
assets/js/opentrust-admin.js
Provides reusable showConfirm modal with async/Promise-friendly onConfirm handling, backdrop-click/Escape closure, and setLoading helper to swap button content/spinner during operations.
Shared Admin Footer Component
includes/Admin/Footer.php
New OpenTrust\Admin\Footer class providing static render() method that outputs escaped footer markup with branding, version display, and navigational resource links (Docs, GitHub, Support, Security, Leave review).
New Settings Page Scaffold
includes/Admin/Settings.php
Introduces OpenTrust\Admin\Settings class wiring admin hooks for settings option registration (opentrust_settings array), menu/page creation, asset enqueueing, notice suppression, and renders custom settings page with design-system field components (toggle, segmented, number, text, select, color, media, sections-visibility chips) plus validation and sanitization.
Admin Asset Enqueueing & Optimization
includes/class-opentrust-admin.php
Conditionally enqueues WordPress media uploader only on screens with media pickers (CPT edit, Settings General tab), updates opentrust-admin script to depend on jquery only (removes wp-color-picker), and scopes design-system assets to OpenTrust plugin pages.
AI Tab UI Redesign & Migration
includes/class-opentrust-admin-ai.php
Refactors AI tab to use design-system components: transient notices via new ds_notice() helper with opentrust-notice markup, citation-assistant section with new layout (opentrust-block, opentrust-disclosure), summary backfill banner styled with opentrust-notice--warn, provider picker with design-system cards, and AI settings form using ds_row_number/ds_row_toggle helpers.
Questions Log Page UI Redesign
includes/class-opentrust-admin-questions.php
Refactors Questions page: changed search parameter from q to search, rebuilt filter/export wiring via $log_filter_params and add_query_arg, replaced WordPress table markup with design-system components (topbar, styled notices, filter card, log table, danger-zone card), and updated pagination/export URLs.
Settings Page Redesign with Design System
includes/class-opentrust-admin-settings.php
Refactors OpenTrust_Admin_Settings: registers single opentrust_settings array option instead of per-section Settings API fields, replaces do_settings_sections() with custom topbar/tabbar layout, manually renders General/Contact sections via ds_render_section_*() with ds_row_*() helpers (text, textarea, toggle, media, accent-color, sections-visibility chips).
Import & Export Tab UI Redesign
includes/class-opentrust-admin-tools.php
Modernizes Import/Export tab: notice rendering switched to opentrust-notice markup with error/success/warn variants, export/import panels redesigned with componentized layout (opentrust-block, opentrust-card, opentrust-row), preview screen uses computed subtitle with counts, styled preview cards/tables with pill labels for actions.
Legacy CSS Cleanup & Admin.js Updates
assets/css/admin.css, assets/js/admin.js
Removes legacy .ot-logo-upload CSS rules, updates accent-contrast handling to use native input events on hex field instead of wpColorPicker callbacks, and removes legacy media uploader wiring (replaced by design-system .opentrust-media component).
Translation Strings & Localization
languages/opentrust.pot
Expands translatable strings for footer/resource links, settings labels (General, Branding, Contact, DPO), AI setup/onboarding, Questions UI (filters, logging status), Import/Export workflow (export options, conflict strategy, preview/merge), and admin UI confirmations.
Plugin Root & Framework Compatibility
opentrust.php, README.md
Adds Ettic shared-template compatibility constants (OPENTRUST_FILE, OPENTRUST_URL as aliases), introduces require_once for shared admin Footer component, updates README with stable version link and main-branch development note.

🎯 4 (Complex) | ⏱️ ~75 minutes

🐰 A design system spring has sprung,
Admin pages hop along with joy!
Toasts, modals, dirty state so clean—
Each toggle, chip, and card's a toy.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: migrating the admin UI to align with Ettic's design system styling. It accurately reflects the overall objective of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ettic-admin-ui

Comment @coderabbitai help to get the list of available commands and usage tips.

<div class="opentrust-filterbar__actions">
<button type="submit" class="opentrust-btn opentrust-btn--primary opentrust-btn--sm"><?php esc_html_e('Apply', 'opentrust'); ?></button>
<a href="<?php echo esc_url(admin_url('admin.php?page=opentrust-questions')); ?>" class="opentrust-btn opentrust-btn--ghost opentrust-btn--sm"><?php esc_html_e('Reset', 'opentrust'); ?></a>
<a href="<?php echo esc_url($export_url); ?>" class="opentrust-btn opentrust-btn--ghost opentrust-btn--sm opentrust-filterbar__export"><?php esc_html_e('Download CSV', 'opentrust'); ?></a>
<?php echo esc_html($row->question); ?>
</td>
<td><code class="opentrust-log-table__model"><?php echo esc_html($row->model); ?></code></td>
<td class="opentrust-log-table__num"><?php echo (int) $row->citation_count; ?></td>
</td>
<td><code class="opentrust-log-table__model"><?php echo esc_html($row->model); ?></code></td>
<td class="opentrust-log-table__num"><?php echo (int) $row->citation_count; ?></td>
<td class="opentrust-log-table__meta">&darr;<?php echo (int) $row->tokens_in; ?> / &uarr;<?php echo (int) $row->tokens_out; ?></td>
</td>
<td><code class="opentrust-log-table__model"><?php echo esc_html($row->model); ?></code></td>
<td class="opentrust-log-table__num"><?php echo (int) $row->citation_count; ?></td>
<td class="opentrust-log-table__meta">&darr;<?php echo (int) $row->tokens_in; ?> / &uarr;<?php echo (int) $row->tokens_out; ?></td>
<td><code class="opentrust-log-table__model"><?php echo esc_html($row->model); ?></code></td>
<td class="opentrust-log-table__num"><?php echo (int) $row->citation_count; ?></td>
<td class="opentrust-log-table__meta">&darr;<?php echo (int) $row->tokens_in; ?> / &uarr;<?php echo (int) $row->tokens_out; ?></td>
<td class="opentrust-log-table__meta"><?php echo (int) $row->response_ms; ?>ms</td>
Comment on lines +258 to +265
echo paginate_links([
'base' => add_query_arg('paged', '%#%', $base),
'format' => '',
'current' => $filters['page'],
'total' => $pages,
'prev_text' => '&lsaquo;',
'next_text' => '&rsaquo;',
]);
@r00bbert r00bbert marked this pull request as draft May 11, 2026 15:39
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
assets/js/admin.js (1)

155-171: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Initialize the override state on first render.

ot-accent-warning--override is only toggled after a change event. If the checkbox is already checked when the page loads, the warning renders with the wrong copy/state until the user touches it.

💡 Suggested fix
         if ($accentInput.length) {
             $accentInput.on('input', function () {
                 updateAccentWarning($accentInput.val());
             });
             // Initial check on page load.
             updateAccentWarning($accentInput.val());
         }
+
+        $accentWarning.toggleClass('ot-accent-warning--override', $forceExact.is(':checked'));

         // Live-toggle the override class so the warning tone updates without
         // a page reload. The actual clamping still happens server-side — the
         // class only drives the admin copy/colour swap.
         $forceExact.on('change', function () {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@assets/js/admin.js` around lines 155 - 171, The override CSS class for the
accent warning isn't initialized on page load when the checkbox ($forceExact) is
already checked, so set the initial state after DOM setup: when $forceExact and
$accentWarning exist, apply or remove the 'ot-accent-warning--override' class
based on $forceExact.prop('checked') (in the same area where
updateAccentWarning($accentInput.val()) is called), ensuring the live-toggle
behavior remains via the existing $forceExact.on('change') handler.
🧹 Nitpick comments (3)
includes/Admin/Settings.php (1)

44-53: 💤 Low value

Notice suppression is aggressive but intentionally scoped.

Removing all admin notices could hide important warnings. However, this is strictly scoped to settings_page_opentrust and documented as intentional for the dark hero layout. Consider adding a comment noting that critical notices (like plugin vulnerabilities) won't display on this page.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@includes/Admin/Settings.php` around lines 44 - 53, suppress_foreign_notices
aggressively removes all admin notices on the settings page (function
suppress_foreign_notices, scoped to 'settings_page_' . self::PAGE_SLUG) which
can hide critical warnings; add an explanatory comment above
suppress_foreign_notices documenting that this removal is intentional for the
dark hero layout, explicitly note that important/system/plugin vulnerability
notices will not be shown on settings_page_opentrust, and optionally add a TODO
or link to an issue describing a follow-up to surface critical notices elsewhere
if needed.
assets/js/opentrust-admin.js (2)

566-657: 💤 Low value

Modal body uses innerHTML - ensure all callers pass safe content.

The body parameter is rendered via innerHTML (line 601). Currently, all callers pass hardcoded HTML or i18n strings. This is acceptable for now, but if future callers pass user-controlled content, it could introduce XSS.

Consider documenting this expectation:

/**
 * `@param` {Object} opts
 * `@param` {string} opts.body - HTML content for modal body. MUST be sanitized if containing user input.
 */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@assets/js/opentrust-admin.js` around lines 566 - 657, Add a short JSDoc above
the showConfirm function explaining the opts.body contract: state that opts.body
is expected to be HTML (string) and MUST be sanitized before being passed if it
can contain user-controlled content, list other opts (title, lede, confirmText,
cancelText, danger, onConfirm) briefly, and note that callers should pass plain
text (and not HTML) if they expect automatic escaping or change the caller to
sanitize their content; reference the showConfirm function and the use of
backdrop.querySelector('[data-modal-body]').innerHTML so reviewers can locate
the innerHTML sink.

271-273: 💤 Low value

Hardcoded English strings for dirty label are not internationalized.

The "unsaved change" / "unsaved changes" strings are hardcoded. For consistency with the rest of the admin UI which uses esc_html_e() and translation functions, these should be localizable.

Suggested approach

Expose i18n strings via wp_localize_script from PHP:

// In JS, use:
var i18n = ( window.OpenTrustAdmin && window.OpenTrustAdmin.i18n ) || {};
labelEl.textContent = count === 1 
    ? ( i18n.unsavedChange || ' unsaved change' )
    : ( i18n.unsavedChanges || ' unsaved changes' );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@assets/js/opentrust-admin.js` around lines 271 - 273, Replace the hardcoded
English dirty-label strings in assets/js/opentrust-admin.js where
labelEl.textContent is set so they come from localized data: read strings from a
JS i18n object (e.g. window.OpenTrustAdmin.i18n) and fall back to the current
English defaults; then ensure the corresponding PHP enqueuing code uses
wp_localize_script (or wp_add_inline_script) to expose unsavedChange and
unsavedChanges via OpenTrustAdmin.i18n so the JS uses those keys instead of
hardcoded literals.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@includes/class-opentrust-admin-ai.php`:
- Line 317: The button text is double-escaping the ampersand because
esc_html_e() will escape HTML entities; change the string passed to esc_html_e()
from "Validate &amp; save" to use a plain ampersand or separate words (e.g.,
"Validate & save" or "Validate and save") so the rendered label shows correctly;
update the esc_html_e(...) call in includes/class-opentrust-admin-ai.php
accordingly.

In `@includes/class-opentrust-admin-tools.php`:
- Around line 188-199: The UI text currently shows only the plugin cap
(self::UPLOAD_MAX_MB) which can mislead users if PHP/WordPress upload limits are
lower; update the render code in class-opentrust-admin-tools.php (the block that
outputs the label/paragraph and the input#ot_import_file) to compute and display
the effective upload limit by reading server settings (upload_max_filesize and
post_max_size) and converting them to MB, then use the minimum of that value and
self::UPLOAD_MAX_MB in the displayed message so the help text reflects the true
allowed size.

---

Outside diff comments:
In `@assets/js/admin.js`:
- Around line 155-171: The override CSS class for the accent warning isn't
initialized on page load when the checkbox ($forceExact) is already checked, so
set the initial state after DOM setup: when $forceExact and $accentWarning
exist, apply or remove the 'ot-accent-warning--override' class based on
$forceExact.prop('checked') (in the same area where
updateAccentWarning($accentInput.val()) is called), ensuring the live-toggle
behavior remains via the existing $forceExact.on('change') handler.

---

Nitpick comments:
In `@assets/js/opentrust-admin.js`:
- Around line 566-657: Add a short JSDoc above the showConfirm function
explaining the opts.body contract: state that opts.body is expected to be HTML
(string) and MUST be sanitized before being passed if it can contain
user-controlled content, list other opts (title, lede, confirmText, cancelText,
danger, onConfirm) briefly, and note that callers should pass plain text (and
not HTML) if they expect automatic escaping or change the caller to sanitize
their content; reference the showConfirm function and the use of
backdrop.querySelector('[data-modal-body]').innerHTML so reviewers can locate
the innerHTML sink.
- Around line 271-273: Replace the hardcoded English dirty-label strings in
assets/js/opentrust-admin.js where labelEl.textContent is set so they come from
localized data: read strings from a JS i18n object (e.g.
window.OpenTrustAdmin.i18n) and fall back to the current English defaults; then
ensure the corresponding PHP enqueuing code uses wp_localize_script (or
wp_add_inline_script) to expose unsavedChange and unsavedChanges via
OpenTrustAdmin.i18n so the JS uses those keys instead of hardcoded literals.

In `@includes/Admin/Settings.php`:
- Around line 44-53: suppress_foreign_notices aggressively removes all admin
notices on the settings page (function suppress_foreign_notices, scoped to
'settings_page_' . self::PAGE_SLUG) which can hide critical warnings; add an
explanatory comment above suppress_foreign_notices documenting that this removal
is intentional for the dark hero layout, explicitly note that
important/system/plugin vulnerability notices will not be shown on
settings_page_opentrust, and optionally add a TODO or link to an issue
describing a follow-up to surface critical notices elsewhere if needed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 901776cf-8993-47c9-a05c-3d3aa7a31a7a

📥 Commits

Reviewing files that changed from the base of the PR and between 492e2a2 and 9f78839.

📒 Files selected for processing (14)
  • README.md
  • assets/css/admin.css
  • assets/css/opentrust-admin.css
  • assets/js/admin.js
  • assets/js/opentrust-admin.js
  • includes/Admin/Footer.php
  • includes/Admin/Settings.php
  • includes/class-opentrust-admin-ai.php
  • includes/class-opentrust-admin-questions.php
  • includes/class-opentrust-admin-settings.php
  • includes/class-opentrust-admin-tools.php
  • includes/class-opentrust-admin.php
  • languages/opentrust.pot
  • opentrust.php
💤 Files with no reviewable changes (1)
  • assets/css/admin.css

<button type="submit" class="button button-primary ot-ai-card__submit">
<?php esc_html_e('Validate & save', 'opentrust'); ?>
<button type="submit" class="opentrust-btn opentrust-btn--primary">
<?php esc_html_e('Validate &amp; save', 'opentrust'); ?>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

HTML entity will be double-escaped in button text.

The &amp; will be rendered literally as "&" because esc_html_e() will escape the ampersand. Use a plain & or separate the words.

Proposed fix
-                        <?php esc_html_e('Validate &amp; save', 'opentrust'); ?>
+                        <?php esc_html_e('Validate & save', 'opentrust'); ?>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<?php esc_html_e('Validate &amp; save', 'opentrust'); ?>
<?php esc_html_e('Validate & save', 'opentrust'); ?>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@includes/class-opentrust-admin-ai.php` at line 317, The button text is
double-escaping the ampersand because esc_html_e() will escape HTML entities;
change the string passed to esc_html_e() from "Validate &amp; save" to use a
plain ampersand or separate words (e.g., "Validate & save" or "Validate and
save") so the rendered label shows correctly; update the esc_html_e(...) call in
includes/class-opentrust-admin-ai.php accordingly.

Comment on lines +188 to +199
<div class="opentrust-row opentrust-row--stacked">
<div class="opentrust-row__main">
<span class="opentrust-row__label"><?php esc_html_e('Export file', 'opentrust'); ?></span>
<p class="opentrust-row__help">
<?php
/* translators: %d: max upload size in MB */
printf(esc_html__('ZIP archive produced by another OpenTrust install. Max %d MB.', 'opentrust'), (int) self::UPLOAD_MAX_MB);
?>
</p>
</div>
<div class="opentrust-row__control opentrust-row__control--stack">
<input type="file" id="ot_import_file" name="ot_import_file" accept=".zip" required class="opentrust-input opentrust-input--file">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Show the effective upload limit here, not just the plugin cap.

This always advertises 50 MB, but PHP/WordPress may reject a smaller upload before your own size check runs. In that case the user sees a misleading limit here and then hits the generic “No file uploaded” path.

💡 Suggested fix
+                        <?php
+                        $max_upload_mb = max(
+                            1,
+                            (int) floor(min(self::UPLOAD_MAX_MB * MB_IN_BYTES, wp_max_upload_size()) / MB_IN_BYTES)
+                        );
+                        ?>
                         <div class="opentrust-row__main">
                             <span class="opentrust-row__label"><?php esc_html_e('Export file', 'opentrust'); ?></span>
                             <p class="opentrust-row__help">
                                 <?php
                                 /* translators: %d: max upload size in MB */
-                                printf(esc_html__('ZIP archive produced by another OpenTrust install. Max %d MB.', 'opentrust'), (int) self::UPLOAD_MAX_MB);
+                                printf(esc_html__('ZIP archive produced by another OpenTrust install. Max %d MB.', 'opentrust'), $max_upload_mb);
                                 ?>
                             </p>
                         </div>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div class="opentrust-row opentrust-row--stacked">
<div class="opentrust-row__main">
<span class="opentrust-row__label"><?php esc_html_e('Export file', 'opentrust'); ?></span>
<p class="opentrust-row__help">
<?php
/* translators: %d: max upload size in MB */
printf(esc_html__('ZIP archive produced by another OpenTrust install. Max %d MB.', 'opentrust'), (int) self::UPLOAD_MAX_MB);
?>
</p>
</div>
<div class="opentrust-row__control opentrust-row__control--stack">
<input type="file" id="ot_import_file" name="ot_import_file" accept=".zip" required class="opentrust-input opentrust-input--file">
<div class="opentrust-row opentrust-row--stacked">
<?php
$max_upload_mb = max(
1,
(int) floor(min(self::UPLOAD_MAX_MB * MB_IN_BYTES, wp_max_upload_size()) / MB_IN_BYTES)
);
?>
<div class="opentrust-row__main">
<span class="opentrust-row__label"><?php esc_html_e('Export file', 'opentrust'); ?></span>
<p class="opentrust-row__help">
<?php
/* translators: %d: max upload size in MB */
printf(esc_html__('ZIP archive produced by another OpenTrust install. Max %d MB.', 'opentrust'), $max_upload_mb);
?>
</p>
</div>
<div class="opentrust-row__control opentrust-row__control--stack">
<input type="file" id="ot_import_file" name="ot_import_file" accept=".zip" required class="opentrust-input opentrust-input--file">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@includes/class-opentrust-admin-tools.php` around lines 188 - 199, The UI text
currently shows only the plugin cap (self::UPLOAD_MAX_MB) which can mislead
users if PHP/WordPress upload limits are lower; update the render code in
class-opentrust-admin-tools.php (the block that outputs the label/paragraph and
the input#ot_import_file) to compute and display the effective upload limit by
reading server settings (upload_max_filesize and post_max_size) and converting
them to MB, then use the minimum of that value and self::UPLOAD_MAX_MB in the
displayed message so the help text reflects the true allowed size.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants