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>
Mark new regular and chunked uploads as pending and claim persisted article references, including partial saves and large reference sets. Protect associated media from cleanup and add upload lifecycle regression coverage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Accept the stdClass user data passed by WordPress and derive the user ID only from the hook object. Cover callback registration, nonce and permission guards, and mismatched posted IDs; refresh the identity review dialog contract.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace inline identity review forms with focused detail and review dialogs, improve list filters and caching, and align evidence permissions and deletion feedback. Add contract and runtime regression coverage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unify status filters, search, bulk actions and pagination in one invite list card. Remove the standalone filter summary and preserve usable search widths on small screens, with structural regression coverage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace standalone add and export views with accessible dialogs, refine list interactions, and remove obsolete paths and unused counts. Fix filtered totals and reject oversized imports without truncation, with regression coverage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Display credits_added as 积分增加 with a success badge across message
filters, list rows and details. Extend rendering regression coverage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Center the operations heading and row buttons, with 20px padding on both
sides of the cell. Add a regression contract for alignment and spacing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a dedicated operations column for viewing, marking read and deleting
messages. Keep the message cell focused on its title, excerpt and ID;
adjust column widths and empty-state colspan with regression coverage.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Register Chinese labels and matching badge tones for balance and points
increase/decrease types across message filters, list badges and details.
Add regression coverage without changing notification delivery behavior.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add message details, per-row actions, selection-aware bulk controls and
confirmation dialogs. Merge filters into the list card and link overview
statistics to the matching views with site-timezone today filtering.
Improve the compose dialog with Unicode character counters, recipient mode
segments, role counts and explicit broadcast confirmation verified against
the resolved role and recipient count on submission. Split rendering into
focused sibling traits and cover rendering, handlers and UI interactions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the inline <details> compose card on users.php?page=aksrt_messages
with a primary header button that opens a native <dialog>. NativeUi::header()
gains dialog-type actions via NativeUi::button_dialog(). The dialog reopens
automatically after a failed send.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Replace per-row inline <details> forms and the separate bulk form with a
single <dialog> shared by single-user and bulk adjustments
- Add stats row, styled tabs, date range filter with reset, sortable expiry
- Show fallback tier, remaining time, and disable unavailable operations
- History tab: labelled operation/status badges, avatars, local time,
batch marker and error message
- Move inline script/onclick to assets/js/admin-vip.js; add vip.css
- Surface the service error message when every user fails
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add NativeUi helper + native-ui.css (tokens mirror the SPA Naive UI theme)
and move the pay center, orders, products, coupons, charge cards, income,
withdrawals, tickets, invite codes, VIP, verify, messages and report/ban
pages onto shared header, stat cards, cards, filters, badges and empty
states. Remove inline styles and dead helpers; split PayChargeCards into
sibling traits to stay under the file size limit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WP core widgets.js applies a negative margin-left to widgets whose
control width exceeds 250px, pushing the form over the available
widgets list. Use the default width like the reference theme.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Replace transfer modal/admin-post flow with reference-style AJAX
export/import/template dialogs (admin-transfer.js, TransferController)
- Customizer "移出模块" action, reference lazy form loading with t-notice
- Common fields use reference labels, dependencies, compact/inline rows,
top/bottom-only extra spacing; form header with description/reminder
- Remove placement hint, template preview and legacy transfer code
- Fix widget audit (current_datetime stub, honor catalog common flags)
and regenerate stale golden fixtures for posts_toc, quote, posts
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Align homepage and category discovery, modern widgets, admin controls, quotes, and comments with the reference theme. Remove superseded paths and add rendering and behavior coverage. Update stale asset and finance contracts; all 760 Node tests and 139 widget assertions pass.
- Render pending orders (max 5, >=3 min left) in a top-right float button
with hover list and countdown, gated by pay_wait_float_s
- Fix order countdown parsing expires_at as UTC
- Drop unused .paid-modal-lists-box thumb rule
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
_win.ver only tracked main.min.js, so rebuilding svg-icon.js alone kept
serving the cached sprite and new icons (ticket-color) rendered blank.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Move the 114 built-in sprite icons out of assets/js/runtime/svg-icon.js into
assets/icons/sprite/{key}.svg and generate svg-icon.js with
tools/build-svg-sprite.mjs. Add ticket-color and use it for the user center
ticket entry. Update sidebar contract tests for the creator card labels.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
widgets/frontend.css loads after main.min.css and its global
.zib-widget{padding:15px} overrode .padding-6.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Rewrite sidebar stats as a single zib-widget row (user-statistics-item, em14 / em09 opacity5 mt6), drop legacy .uc-stats styles
- Restore reference body line-height (1.42857143) on stats and service/settings boxes
- Move 我的订单 to the front of 我的服务 to match reference order
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the native input[type=color] in the page config meta box with a
wpColorPicker-enhanced text input, enqueued only on page edit screens, and
contract-lock that no native color inputs remain in theme PHP.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- pay-pages-pagination asserts the $parent_file base URL added in b74381cf
- Drop native loading="lazy" from message avatars and medal presets per
the lazyload contract
- verify-ban-settings-defaults requires SchemaUsersTicket and new deps
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add AKSRT\Core\Color::sanitize($value, $allow_alpha) as the single PHP
color sanitizer; replace sanitize_hex_color calls and per-class regexes
(FloatButtons, FieldSchema, ThemeSearch, icon cards, TicketStatusService)
- SchemaProvider / ControllerBase sanitize `color` fields as colors;
alpha kept only when the schema sets showAlpha
- Split SchemaProvider OAuth validation and option matching into sibling
traits to stay under the 1000-line limit
- Add AksColorPicker.vue + utils/color.js mirroring the PHP rules; AksField,
FloatBtnCommonFields and TicketStatusPanel use it
- Widget background color input now uses wpColorPicker
- Remove dead AksCompositeField dimensions/spacing/background branches and
the unused Gutenberg ColorPicker var
- Add color-unification contract test; update harness requires and tests
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drop the 备案信息 group (icp_filing, police_filing, footer_text) from the
global.common PHP/Vue schemas, the FacadeBulk readers and the
get_global_settings() payload. No frontend consumer existed.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Media and gallery previews now hug the image (inline box, capped height)
instead of stretching across fixed grid cells, removing empty letterbox space.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Media/gallery previews no longer stretch or crop: use object-fit contain,
natural sizing for single media, and the uncropped medium size instead of
the square thumbnail for gallery and native meta-box previews.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- OAuth provider and payment gateway groups are collapsible and collapsed by default; remove dead expandWhen logic
- Textarea fields support secret masking; mark alipay_privatekey as secret in JS and PHP schemas
- Extract AksSecretClear shared by input and textarea secret fields
- Replace inline styles in payment/post-list notice HTML with aksrt-hint / aksrt-warn classes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Badge page: rewrite as collapsible sortable category/item cards with
preset medal icon picker, dirty-tracked save bar and duplicate slug check;
drop user_badge_* dual-read and legacy name/icon_url fallbacks
- OAuth: provider config groups collapsible, expanded when provider is on;
clogin visibility moved to group-level dependsOn
- Gateway: expandWhen accepts dependsOn-style rules; select-gated groups no
longer always expand ('null' was truthy); mark five gateway secrets as
secret/password in JS and PHP; class-based notice list styling
- Remove unused UserRuntime::parse_badge_dictionary
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Add useCollapsibleItems: key-based open state, clickable/keyboard head, aria-expanded
- sortable items with sub-fields and every inline repeater/group row now collapse
- Repeater head summary falls back to the first filled field when no listPreview
- Drop per-schema collapsible/defaultCollapsed for repeater groups (JS + PHP)
- Align FloatBtnItemPanel head with the shared collapse attrs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Validate status_key (^[a-z][a-z0-9_]{1,23}$, unique) on create/update
- Renaming a key moves aksrt_ticket.status in the same transaction
- Default statuses remain immutable
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Global input[type=radio] rules in main.css out-specified the ticket
picker's hide rule, pulling the radio back into flow before the pill.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- aksrt_ticket gains urge_count / last_urge_time (schema 3.1.0)
- cooldown moves from transient to an atomic conditional UPDATE
- urge inserts a system reply rendered as a centered notice (not quotable)
- admin list badge + info card row for urge count / last time
- new ticket_urge_admin mail event via aksrt_ticket_urged
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mark front-end uploads (comment/ticket/report) as pending, return the
attachment id, discard own pending uploads on remove via
DELETE /upload/pending/{id}, claim referenced URLs on successful submit,
and purge unclaimed uploads older than 24h with an hourly cron.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The static-array callable pointed at an instance method, which PHP 8
rejects as non-callable, so WP answered every upload with rest_invalid_handler.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Detail modal: fixed-height dialog, thread is the only (thin) scroll area
- Collapsible pinned summary bar (collapsed by default, state kept across re-render)
- Composer: toolbar (image / urge / close) above input, pending image tray with remove
- Paste screenshots to upload; create form uses the same button + tray
- Urge/close now POST (routes are CREATABLE); urge cooldown countdown via urge_wait
- Notify admins on urge when ticket is unassigned
- Split composer/form styles into tickets-compose.css; drop dead uploader code
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The hidden file input was appended to the flex-centered modal shell,
becoming a second flex child that pushed the dialog left and exposed
the native file button (theme display rules override [hidden]).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Status stat cards double as quick filters; debounced search with stale-response guard
- Card list with status stripe, windowed pagination, skeleton/empty/error states
- Detail dialog as chat timeline with in-place refresh, quote preview, Ctrl+Enter send
- Create form with department chips, inline validation, multi-image upload, success notice
- Split shared helpers into ticket-center-core.js and enqueue it before the panel script
- Drop dead data-aksrt-ticket-config and legacy colorful modal header
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Move table cell styles out of scoped block so h()-rendered swatches get size
- Replace the bare color-picker trigger with preset swatch radios + custom picker
- Disable alpha so picked colors sync to the hex input (was #rrggbbaa, dropped)
- Normalize hex via toHex() matching TicketStatusService::sanitize_color()
- Add live badge preview, hex input chip, inline closed-state hint
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
/users/ticket loaded in the SPA but the form never did: the console showed
"未知的设置分组: ticket" from /settings/schema/users/ticket. The ticket section
was composed one array level too high in SchemaUsers::build() — as a sibling of
'sections' rather than inside it — so SchemaProvider::get_sections(), which reads
only $schema['sections'], never saw it and the REST handler 404'd.
Every other axis was wired correctly: the route, the menu entry, users/ticket.js
and SchemaUsersTicket itself. That is why only this page failed while sibling
/users/* pages worked. Move the entry inside sections.
The contract test could not catch this because it asserted only that the compose
call appears somewhere in the file. Replace it with a bracket-balanced scan that
locates the sections array bounds and asserts the compose call falls inside, so
nesting mistakes are visible.
Also update the two ticket contract tests that still read the trait from
src/Ticket/TicketRepository-traits/, its pre-flattening path. They assert on the
trait's contents, so they were silently skipping the very file the earlier fatal
was about.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TicketRepository fatalled on /user/tickets/ with "Trait AKSRT\Ticket\TicketQuery
not found". src/ classes load only via Composer PSR-4 (AKSRT\ => src/) — there is
no bootstrap require list for them — so the autoloader resolved the traits by
namespace to src/Ticket/TicketQuery.php and src/Ticket/TicketStatus.php. Both
files actually sat in src/Ticket/TicketRepository-traits/, one level deeper, and
were therefore invisible: PHP hit the `use` statement with no trait declared and
died the moment the repository compiled.
The nested directory name imitated the old inc/classes/**/*-traits/ layout, which
only worked because inc/bootstrap.php carried an explicit require_once for every
one of those files. No such mechanism exists under src/. Move both traits up to
sit flat as siblings of the class that uses them — the same shape as
GlobalControllerPart1, ModerationPart1 and the rest of src/. No code changes
inside the traits; their namespace was already correct.
Add tools/audit-psr4-paths.php, which checks every symbol under src/ against the
path Composer maps it to and exits 1 on mismatch. It flags this exact layout, so
the nesting cannot come back unnoticed. Document it in CLAUDE.md next to the
existing admin-contract audit.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The old 售后 flow was an order-refund appendage: tickets were hard-bound to an
order number, users could only file them from 「我的订单」against a paid order,
and the admin action bottomed out at 「标记订单已退款」. It could not carry
account questions or feature requests that have nothing to do with an order.
Replace it with a general ticket system modelled on 魔方财务云's idcsmart_ticket:
title + department + description are required, linking an order is optional
(user's own paid orders only). Add departments, custom statuses, preset replies
and internal admin notes as four configuration surfaces. Conversation history
moves off the ticket row's JSON log into dedicated ticket_reply rows, which
unlocks quoted replies. Front end gets a 工单中心 panel under 我的服务; back end
moves from 支付中心 → 售后服务 to users.php → 工单管理.
Per CLAUDE.md's rewrite-over-compatibility rule: drop the old table, build new
ones, delete the old code. No dual paths, no data migration. The upstream
plugin's upstream/downstream delivery and internal-ticket conversion are
deliberately left out — both are multi-provider platform concerns with no
meaning on a single WordPress site.
Data: order_aftersale becomes six aksrt_ticket* tables (ticket, ticket_reply,
ticket_status, ticket_type, ticket_prereply, ticket_note). SCHEMA_VERSION
2.6.0 -> 3.0.0. Status and department seeds are idempotent; the default status
「待接单」is flagged as non-editable.
Domain: new src/Ticket/ (13 files, all under 460 lines). Attachments reuse
UserController::rest_upload_comment_image with a ticket-specific permission
callback, mirroring the existing /report/upload-image route rather than
growing a second upload path.
Notifications go through AKSRT\Messages\Message::send() and deep-link to the
right surface; email/SMS stay out of scope since the theme's mail and SMS
stacks are separate implementations.
Refund gate opens up: marking an order refunded no longer requires an in-flight
ticket, only that the order is paid.
Also generalise a hardcoded special case in tools/audit-admin-contracts.php —
schema domain inference now reads the parent directory instead of matching
"PostList" by name, so new leaf schema files need no per-name exception.
Verification:
php tools/audit-admin-contracts.php exit 0, errors=0
node --test (widget-system + admin) 664 pass / 0 fail
php -l over all new/changed files clean
node --check over all JS clean
npm run build + build:public clean
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>