The 代码高亮 and 系统公告 schemas existed but appeared in no menu, so their
routes were never generated (routes come from the sidebar constants). The
reference keeps both inside the 常用功能 panel rather than as their own
entries, so follow that structure instead of adding sidebar items.
- global/common now carries three groups after the lazy-load ones:
代码高亮 (switch / line numbers / buttons / max height / light+dark theme)
and 弹窗通知 (policy, content, 4 buttons, size, radius, icon, expiry)
- group keys are uniquified (code-highlight, code-theme, notice-*) so they do
not collide with the existing performance group
- delete the orphan code-highlight.js / system-notice.js schemas and their
section entries on both the Vue and PHP sides
- delete post-list/pay.js and its PHP counterpart: every post_pay_* and
post_download_* key there had zero runtime consumers, and the reference has
no such page (paid-content config lives under 支付&付费)
tools/health and tools/import-export turned out to already be reachable as
custom pages, and remain untouched.
Tests: admin contract audit 0 errors (warnings 56 → 51); SPA builds clean;
suite 967/968 with the pre-existing frontend-lifecycle failure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment [code] blocks rendered as bare <pre><code> with only Bootstrap's
grey box — no syntax colours, unlike the reference.
The runtime gates EnlighterJS on `_win.highlight_kg`, which was hardcoded to
false. With that false the selector degrades to `pre code.gl, pre code.special`,
and comment code carries neither class, so it never highlighted. The admin
already exposes 「全局功能 → 代码高亮」 (default on), and jioData.codeHighlight
reads it — only the runtime payload disagreed.
- _win.highlight_kg / highlight_hh / highlight_zt (plus white/dark theme
names) now come from Settings::get_code_highlight_config(); the initial
theme follows cookie → mode → time-auto via initial_theme_mode()
- define those values before the _win localize (it sits above jioData in the
same method)
- inject the reference's two head rules the runtime path was missing:
max-height from `highlight_maxheight`, and
`.enlighter-toolbar{display:none}` when `highlight_btn` is off
Tests: comment-image-upload contract gains cases pinning the settings-backed
booleans (and their definition order) and the two inline CSS rules; suite
837/838 with the pre-existing frontend-lifecycle failure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
While uploading, the confirm button turned into a thin 3px bar below the
label instead of the reference's striped overlay that fills the button.
Two causes:
- the markup used <span class="progress">; Bootstrap sizes the bar with a
block-level .progress, so the swap produced a flattened line
- shell.css carried a bespoke override pinning .progress to
`position: static; height: 3px`, discarding the absolute full-size
overlay that main.css already provides
Changes:
- emit the reference skeleton: <div class="progress progress-striped active">
> <div class="progress-bar progress-bar-success">, so the striped bar
spans the button and the gradient shows through the translucent layer
- give the upload button `position: relative` so the overlay is contained by
the button rather than the dropdown panel; delete the static-line override
- restore the reference's "正在处理请稍等..." toast and switch the label to
"处理中 ..." at 100%, both keyed to the shared `miniupload_ajax` toast id
so the completion message replaces the loading one
- notify() now forwards the toast id instead of dropping it
Tests: comment-image-upload contract gains two cases pinning the div-based
striped overlay and the deletion of the 3px override; suite 835/836 with the
pre-existing frontend-lifecycle failure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The publish time under the author name diverged from the reference in four
ways: it rendered a leading bullet from `.icon-circle`, its tooltip opened
above instead of below, its wording was "2 天发布" (WordPress'
human_time_diff has no 前 and keeps spaces), and it ignored the
「时间显示数据源」 setting entirely.
- markup: drop `.icon-circle` from the time span and the description fallback;
add `data-placement="bottom"` so the tooltip opens below like the reference
- wording: use jio_time_ago() (which already ends in 前) and append only
发布 / 更新, yielding "2天前发布"
- branches: mirror zib_get_post_time_tooltip exactly (date source → publish
time; unpublished → modified time; published-with-update → modified time
labelled 更新 while the tooltip keeps the publish time; otherwise publish)
- the setting is read from post_time_source via get_post_list_style_settings()
Tests: new author-bar-time suite drives the real template through a stub
harness across all four branches (4/4); widget suite 833/834 with the
pre-existing frontend-lifecycle failure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
With a featured image the breadcrumb sits under the title inside the cover
overlay, but its first icon started 6px left of the title's left edge.
The overlay rule carried an extra `padding: 0` that this theme added on top of
the reference. The same node also matches `ul.breadcrumb { padding: 0 }`
(0,1,1 specificity), which outranks the base `.breadcrumb { padding: 0 8px }`
(0,1,0) — so the reference's `margin-left: -6px` nudge was applied with no
inset to compensate, pulling the whole row 6px to the left.
Writing the inset back as `padding: 0 8px` on the overlay rule restores the
reference geometry: -6px + 8px nets +2px from the panel edge. The global
`ul.breadcrumb` reset and the video footer variant are untouched, so archive
and video breadcrumbs keep their current padding.
Tests: new case in the single-cover contract suite pins the 8px inset (and the
global reset); cover suite 12/12, widget suite 829/830 with the pre-existing
frontend-lifecycle failure.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The like and favorite buttons gave no in-flight feedback: the label just sat
there until the request returned, the lovestar animation never replayed on a
repeat like, duplicate clicks reported "success", and guest favorites silently
wrote a cookie instead of asking the user to sign in.
- pending state: replace the count with the reference inline spinner
(`<i class="loading zts">`) and disable the button until the response lands
- animation: strip `action-animation` before re-adding it (forced reflow), so
every successful like replays the reference lovestar keyframes
- duplicate: when the server reports the same state and count, skip the
animation and surface the reference warning copy (已赞过此文章了!)
- failures: restore the original count and toast `danger`; guest-only actions
now open the site login modal instead of dead-ending on an error
- favorite requires login server-side, matching the reference; the guest
favourite cookie branch and its reader are removed as dead code
- unify like/favorite through one bindToggle path and keep every mirror
button (article row + footer tabbar) in sync
Tests: new article-actions-ux suite (8 cases) drives assets/js/core/article.js
against a DOM/fetch shim — pending spinner, replay, duplicate no-op,
concurrency, rollback, guest login, mirror sync. Widget suite 828/829; the one
failure (frontend-lifecycle theme-init stub) predates this change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The reference wraps these two widgets in a middle .theme-box layer
(widget-container > .theme-box > title + card), which is what supplies the
20px bottom margin. Our outer .jio-widget-box-wrap is only a transparent
layout container, so dropping .theme-box left the quote card flush against
the following module.
- search/quote: emit the reference .theme-box middle layer; the card itself
stays margin-free like the reference, so the gap is exactly 20px
- keep the generic .widget-content branch for user_card/author_card and the
other reference widgets untouched (the string is contract-tested)
- contract test now pins the .theme-box layer and asserts the quote card
carries no margin class of its own
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The 搜索框 and 一言 sidebar widgets fell into the generic renderer branch, so
they rendered a .jio-panel card wrapping a second bespoke card
(.jio-widget-search / .jio-widget-quote). In-article 一言 also used
divergent modifier classes and an extra hover rule.
- search: render through the shared jio_get_search_box() (legacy keyword,
history and category pickers) inside one reference card, mirroring the
reference widget's markup and title default 「搜索」
- quote: render the reference .yiyan-box card itself as the click root; the
shell now emits only the reference widget-container/title wrapper
- article 一言: adopt the reference class lists for header, footer and the
standalone after-article box; drop the invented yiyan--* modifiers and
the duplicate hover/opacity rules
- client: fill .cn/.en in the reference order (first line .cn, second .en)
and drop the stale window.JioYiyan bridge plus the dead qv-yiyan.php AJAX
path in runtime main.js
- schema: replace the ad-hoc search_style radio with the reference switches
(keywords, history, input category, more_cats) and default title
- assets: enqueue assets/js/yiyan.js through the quote widget's asset list
Tests: widget-system suite 820/821 (the one failure is the pre-existing
frontend-lifecycle theme-init stub); new search/quote card contracts pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fixes two issues:
1. **Balance recharge disabled on fresh install**: The `pay_balance_s`
setting has `default: true` in SchemaPayment but was treated as
disabled when the key was absent from the database. This meant users
had to toggle it off and back on to activate the feature.
Fixed by using `!isset() || !empty()` pattern to honor the schema
default when the key is missing.
2. **PHP Warning in withdrawal settings**: `get_withdrawal_settings()`
passed `$s['affiliate_withdrawal_reset_time']` directly to
`preg_match()` without extracting it first, causing "Undefined
array key" warnings when the option was unset.
Fixed by extracting to `$reset_time_raw` before the regex check.
Changed:
- `PaymentRuntime::get_payment_recharge_settings()`: enabled defaults to true
- `PaymentRuntime::get_payment_gateway_settings()`: balance_enabled defaults to true
- `PaymentRuntime::is_gateway_enabled('balance')`: defaults to true
- `PaymentRuntime::get_withdrawal_settings()`: no longer warns on missing key
Added test script `tools/test-payment-runtime-defaults.php` to verify
the fix works with empty settings (fresh install).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The top-bar theme switch was bound twice — core/theme.js attached a click
handler to #theme-toggle-desktop while runtime/main.js delegated click on
.toggle-theme, and both ship in main.min.js, so one press toggled twice and
the button looked dead.
Also realign the control with the reference:
- Drop the far modifier from fa-toggle-theme so the sun/moon glyphs resolve
against the Solid face at the reference weight instead of Regular.
- Restore .toggle-radius .fa/.icon to the reference form (left/right 0 +
auto margin only). The extra position/top/transform/size/flex block and
the > i selector were overriding the shared abs-* centering rule.
- Replace the per-ID bindings with one delegated listener so the floating
rail, which carries no id, keeps working.
- Remove dead theme.js queries for selectors no markup or stylesheet ships
(#theme-toggle, #dropdown-theme-toggle, .theme-icon-moon/.theme-icon-sun,
[data-action='toggle-theme'], .is-dark).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A fresh install leaves `permalink_structure` empty, so WordPress snapshots
$wp_rewrite with no post/page permalink rules and every single post resolves
to 404 until someone re-saves Settings -> Permalinks.
The existing mitigation fired too late to help: routing.php wrote the option
and flushed from `init`, but WP_Rewrite was already constructed with the empty
structure, and core defers pre-`wp_loaded` flushes to a `wp_loaded` callback
that regenerates from that stale snapshot. The rewrite_rules option ended up
non-empty but broken, so the 404 persisted on every request.
New \Jio\Core\RewriteBootstrap owns this: on `after_setup_theme` (still inside
wp-settings.php, before wp-blog-header.php parses the query) it calls
$wp_rewrite->set_permalink_structure() — the same API wp-admin/options-permalink.php
uses — which persists the option *and* refreshes the in-memory snapshot, so both
the current request and the rules written for the next one are correct. A
structure an administrator chose is never overridden.
Also consolidates the two duplicated flush sites that lived inline in
functions.php (`after_switch_theme` closure and the `jio_flushed_version`
block, whose `delete_option('rewrite_rules')` forced a needless regenerate),
and drops the now-redundant permalink write from routing.php. The version
stamp moves to `wp_loaded` priority 20 so it sees the complete rule set
instead of the pre-`add_rewrite_rule()` snapshot.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The theme was renamed Zibll/子比 → AKSRT → Jio, but upstream branding
survived the sweeps in CSS classes, the SVG sprite prefix, a persisted
widget field ID, JS events, admin Vue class names, test infrastructure
and docblocks.
Renames:
- zib-widget / zib-widget-wrap / zib-slider → jio-widget-box /
jio-widget-box-wrap / jio-slider-box (jio-widget is taken by the modern
widget shell, jio-box-* by the card primitives)
- zib-partners-scroll + zps-* → jio-partners-scroll + jps-*
- zib-combo-info-card + zci-* → jio-combo-info-card + jci-*
- zibsvg- → jiosvg-, including the exported picker helpers and the
fa-zibsvg- family-prefix guard
- zibpay_type → pay_type; the dual-read fallback in PostQuery collapses
to a single key
- [zibajax="submit"] dropped (no PHP emitter); zib_ajax.success →
jio_ajax.success
- aks-* admin Vue classes → jio-*
- zib_palette() → palette()
User-facing vendor traces are gone: the floating "more" button default no
longer ships a zibll.com link or 子比主题 marketing copy, the notice
widget ships no preset promo entries, and the four 官网教程 links are
removed from the post-list admin schema.
Test infrastructure renamed to neutral names (docker-compose.reference.yml,
Caddyfile, article-baseline.json, modern-widgets-contract.test.cjs) with
their DB/volume/env/slug identifiers following.
Kept intentionally, documented in tests/fixtures/README.md: the golden
fixture reference fingerprint (a SHA-256 record of an external directory,
read by validate-widget-golden / generate-widget-golden-fixture /
audit-widget-system), the doesNotMatch guards that prove the old tokens are
gone, and the reference theme's own meta key in the visual fixtures.
No backward-compat or data migration: saved widget configs holding old
icon keys or the old field ID silently lose them, per the project's
rewrite-over-compatibility rule.
Verified: 132/132 node contract tests, php tests/widget-system/run.php
(146 assertions), audit-admin-contracts, audit-psr4-paths and
validate-widget-golden all green; tracked bundles regenerated and in sync.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The mechanical rename turned AKSRT-theme into Jio-theme, but the
directory itself is Jio. Fix the three places that name the folder
(README install steps and tree, wp-cli-visual-fixtures usage comment,
DocsPage docs deep-link) and rebuild the SPA bundle.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
user_medal_args was declared as type=json without sanitize_type, so
sanitize_field_value() fell through to sanitize_text_field((string) $value)
and stored the literal string "Array". validate() has no json shape check,
so the save reported success.
Consequence: parse_medal_catalog() json_decoded "Array" into an empty
catalog, the dictionary came back empty, and user_context() dropped every
owned badge — the front end showed "获取徽章" and the grant modal listed
nothing, hiding both the newly added and the pre-existing badges.
Add the missing sanitize_type to match its sibling level/integral fields,
and guard sanitize_field_value() so structured container types can never
reach the scalar fallback again. Mirrors the declaration in the Vue schema.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
find_missing_admin_chunks() only parsed index.js, but a lazily
imported page chunk pulls in its own deps (shared-api, PageHeader,
sortable-options…). A deploy that drops one of those still leaves the
entry internally consistent, so the existing notice never fires and the
failure only shows up when an admin opens that menu — every schemaPage
keeps working, which makes the breakage look arbitrary.
The same scan never looked at CSS at all: 18 stylesheets are reachable
from the graph and none of them were checked, so a missing page
stylesheet ships an unstyled screen with no warning.
Walk the module graph transitively from index.js, queueing each
discovered dependency, and match .js and .css specifiers. Deleting
PageHeader now also reports every page that depends on it.
This is a diagnostic hardening only — it makes an incomplete deploy loud
instead of guessing at the cause of a broken page.
AksImageSelectField pinned every preview to a 60x60 box with
object-fit:cover, but these are wide layout strips: header layouts are
597x57 (10.47:1), the footer template is 800x190 (4.21:1) and the slide
align set is 200x101. A 10:1 strip inside a square crop keeps ~6% of its
width, so the layout it is supposed to illustrate is unrecognisable.
Constrain the width instead and let the height follow, which is what the
reference theme's CSF image_select does (max-width:100%; height:auto).
Each field now declares the width that suits its images, and an option
may still override it: header 200, footer 240, slide align 160, mobile
header 120.
Only presentation changed. No image files and no stored values moved,
so existing settings are unaffected.
generate-fa5-icons.php read fa-{brands,regular,solid}.svg from
webfonts/, but only woff/woff2 ship with the theme, so the generator
died on file_get_contents() and the committed fa5-icons.php could not
be regenerated from what the theme actually distributes.
Decode the style split from each shipped woff's cmap table instead.
The split has to come from the font: FA5 assigns different drawings to
the same codepoint per style, so fas/far/fab membership is unanswerable
from the CSS alone. Output is byte-identical to the committed catalog
(1001/151/456), so the picker is unaffected.
all.min.css also declared eot / eot?#iefix / ttf / svg fallbacks for all
three families while webfonts/ holds only woff2 + woff, so a browser
that stops at woff requested six files that do not exist. Add
tools/strip-fa5-legacy-fonts.mjs to keep the src: list to what ships;
it is idempotent and touches nothing but the @font-face blocks.
assets/admin/public/images/header-layout duplicated
assets/images/header-layout byte for byte (all five PNGs, matching
md5). The directory only existed because assets/admin/public is Vite's
default publicDir, so every build copied it into build/images.
Nothing referenced it: SchemaPageDisplay builds its preview URLs from
assets/images/header-layout via self::asset(), and a repo-wide search
finds no reference to public/images anywhere. Remove the source and let
build/images stop being regenerated.
The bulk row gained the shared aksrt-native-bulk class in 1273a013, so the
exact class="aksrt-inv-bulk" string match no longer held.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Overview page: realtime today vs. same time yesterday, 6 KPI cards with
period-over-period deltas, trend (paid / net / refund curves + order count
line), category curves, payment method donut, top 10 paid content,
conversion / close / refund rates, todos and shortcuts
- Date presets, custom range and category filter via
GET aksrt/v1/admin/pay-overview (manage_options + wp_rest nonce)
- Metrics: paid amount by paid date including later-refunded orders, refunds
by refund date, net = paid - refund; CNY only, points and recharge excluded,
foreign-currency orders listed separately; integer cents internally
- Daily records cached per month in transients (today/yesterday live),
invalidated on order status change and on purge
- ECharts 6.1.0 subset bundled to assets/js/runtime/libs/echarts-pay.min.js
(npm run build:pay-charts), loaded only on the overview page
- Split payment-attempt methods out of OrderRepositoryCore into
OrderRepositoryAttempts; remove dead get_stats(), cash_decimal(),
Order::get_stats() and the old .aksrt-native-links styles
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Split PayWithdrawals into shell + PayWithdrawalsList flat sibling trait
- Add process/policy callout, open/paid/fee stats, status views with counts
- Filter by source and payout method; reset keeps the status view
- Show net payout with gross/fee breakdown, copyable payee, note column
- Replace inline select form with per-row actions and a confirm dialog;
reject requires a reason; double-submit guard; retry reuses the
idempotency key; redirect back preserving filters and paging
- Withdrawal: add get_status_counts() and method filter; get_stats() now
returns open_gross / paid_net / paid_fee
- Remove unused REST routes (/payment/withdrawals, /transition),
admin_withdrawals(), update_status(), has_open_withdrawal(),
count_open(), resolve_gross_amount()
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Always render the checkbox column and bulk bar; disable with a reason
instead of hiding when settlement is off or the page has no pending rows
- Replace the batch-settle button with a bulk action select (settle / delete)
plus apply, matching the charge cards and invite codes pages
- Add per-row delete for pending records and Revenue::delete_pending(),
guarded by status = 'pending' so settled ledger-backed rows are never removed
- Report done / failed counts for bulk settle and delete
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add shared .aksrt-native-bulk row that locks select and button to the
same height, font size and line height
- Apply it to the charge cards and invite codes bulk action rows
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Move "新建优惠码" / "批量生成" into header-launched <dialog>s
- Extract forms into PayCouponsForms trait with shared rule fields,
hints, validation and currency/percent unit switching
- Generated codes card gets a copy-all button; list codes copy on click
- Drop old inline forms, render_generated_dump and the unread
aksrt_coupon_generated redirect arg
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Rebuild aksrt_pay_products as list + in-page quick settings dialog
(pay type, limit, cash/points prices, sales float, coupon, order expiry, copy)
- Query by _aksrt_pay_type with real pagination; drop order-summary fallback
and the duplicate "Meta 销量" column
- Centralize pay meta writes in DomainModel::persist_pay_meta(); stop writing
unread _aksrt_pay_modo / _aksrt_pay_limit keys
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep ticket info at its natural height and let the internal notes card fill
the remaining sidebar height, so only the notes list scrolls.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Share one --aksrt-ticket-pane-height between the conversation panel and
the sidebar cap so both columns line up, and drop the sidebar sticky
offset that no longer matches the pane height.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Put the reply composer at the bottom of the conversation card so the
timeline and composer form one viewport-height panel; the send button is
visible on load and Ctrl/Cmd+Enter submits. Internal notes now open in a
dialog instead of expanding inline, so the sidebar no longer grows.
Remove the shared ticket_composer helper and the inline note expansion CSS;
both dialogs share ticket_dialog_head.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move "代客提交工单" back into a native <dialog> on the ticket list while
the ticket workspace stays a full page. Closing asks before discarding
input, ignores backdrop clicks, resets the form and returns focus; an
uncertain create result keeps the form locked behind "重新加载".
Remove the ?view=new create page route and its styles.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Ticket handling on users.php?page=aksrt_tickets no longer uses dialogs.
The list, the workspace (?id=N) and the assisted create form (?view=new)
are server-rendered page views that carry the list filters and page
for going back. Writes still go through the ticket REST routes; on
success the page reloads with a native notice, and unsent drafts in
other forms survive the reload via sessionStorage. Uncertain or
conflicting writes lock every form and only offer a reload.
Remove dead code: UsersMenusTicketsDialogs, the aksrt_ticket_view AJAX
read and its nonce, dialog CSS/JS (backdrop, scroll lock, focus return,
read races) and the dialog runtime test. Fix the create link carrying
an unclamped page number on out-of-range list pages.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drop the legacy credits_added message label per the hard-cut rule, switch
three remaining Font Awesome 4 icons to FA5 solid, and align two widget
contracts with the badge rule grouping and renderer list from fc04bf65.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the type/method tab rows with status views backed by one GROUP BY
count, move filters into a single toolbar card, show gateway names, keep
list context across detail and actions, and split the detail page into a
summary/timeline column and an actions/buyer/note sidebar. Confirmations
move to admin-orders.js; PayOrders splits into flat List/Detail traits.
Remove dead code: unused Order AJAX endpoints and init(), get_orders_count(),
shipping flash codes, and the legacy raw-note fallback.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Show only the latest three notes and fold older ones into a details
toggle, collapse the note composer to one line until focused, and cap
the sidebar height to the dialog body so its controls stay reachable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Render the original question and replies as one chat-style timeline,
merge editor and toolbar into a shared composer for replies and notes,
and refresh sidebar cards. Remove superseded renderers and selectors.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Edit status, department and assignee directly in ticket information, remove the redundant handling card and its layout rules, and cover editable and closed-ticket rendering.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Keep the ticket number and status in the sidebar only, remove the unused header description payload and bindings, and update dialog regression coverage.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Move on-behalf ticket creation into a header dialog and load ticket handling on demand. Reuse the admin REST write path, preserve drafts and filters, and cover dialog and server boundaries with regression tests.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Store readable category snapshots separately from details and evidence, validate report categories, and preserve context in admin displays and moderation actions. Widen report categories to TEXT and bump the schema to 3.2.0 without legacy data conversion.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Use highlight-only reason choices, show the associated target on one line, and replace inline submission progress with shared toast feedback.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>