26 Commits
Author SHA1 Message Date
kate522andClaude Opus 5 427968a721 chore(branding): remove leftover upstream theme identifiers
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>
2026-11-02 00:06:54 +08:00
kate522andClaude Opus 5 35bb96e82f refactor: rename the theme brand and every identifier from AKSRT to Jio
Full-project rename. `git grep -i aksrt` is now empty apart from the
DBMigrator 3.3.0 changelog note that documents the rename itself.

Code identifiers
- PHP namespace AKSRT\ -> Jio\ (composer PSR-4 + 378 declarations)
- global constants AKSRT_* -> JIO_* (VERSION/DIR/URI, PAY_TYPE_*, CAPTCHA_*)
- procedural functions, hooks, options, meta keys, table names,
  cookies, query vars, ajax actions and nonces: aksrt_ -> jio_
- text domain aksrt -> jio; Theme Name -> Jio
- JS globals: aksrtData/aksrtAdminData/... -> jioData/jioAdminData/...,
  window.AKSRTCaptcha & friends -> window.JioCaptcha, Aksrt* -> Jio*
- Vue components Aks*.vue -> Jio*.vue (15 files renamed + imports)
- CSS classes/data-attrs/custom properties: aksrt- -> jio-
- REST namespace aksrt/v1 -> jio/v1 (incl. payment gateway callbacks)

Storage layer
- DB tables {prefix}aksrt_* -> {prefix}jio_*; SCHEMA_VERSION 3.2.0 -> 3.3.0
  so dbDelta creates the new names on install
- widget id_base aksrt_widget_* -> jio_widget_*
- uninstall.php drop list rebuilt from the actual migrator tables
  (dropped 3 stale entries, added the 17 missing ones)

Removed
- hardcoded https://api.aksrt.com/version.json default update endpoint;
  get_endpoint() now reads the 「更新端点」 setting and do_check() no-ops
  when it is empty

Files renamed: inc/helpers/aksrt-*.php -> jio-*.php (54),
assets/admin/src/components/form/Aks*.vue -> Jio*.vue (15).
Build artifacts regenerated: assets/css/build, assets/js/runtime,
assets/admin/build.

Verified: audit-psr4-paths errors=0, audit-admin-contracts errors=0
(warnings=56, unchanged baseline), 813/813 widget-system tests,
44/44 phpunit, php -l clean across all PHP.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-01 20:05:04 +08:00
kate522andClaude Fable 5 80aecab586 feat(frontend): upgrade report dialog experience
Replace manual report links with automatic target association, improve validation and evidence uploads, and remove retired report UI code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-30 23:56:59 +08:00
kate522 1c501ff713 fix(user): align badge modals with reference theme 2026-10-29 17:38:10 +08:00
kate522andClaude Opus 5 dbb7de41e9 feat(ticket): replace the after-sale flow with a general ticket system
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>
2026-10-25 23:29:51 +08:00
kate522 039fa6d863 feat(after-sale): 处理记录时间线改为「节点 + 状态 + 时间」同一行
原实现把状态与时间竖排在节点右侧的 88px 固定列里,状态文字一长就折行、
时间被挤到第三行,和右侧备注也对不齐。改为节点独占一列,状态与时间包在
head 里紧随其后,处理备注另起一行:

- [节点][处理状态][时间] 一行,状态色仍按工单状态走 --as-accent
- 竖线改挂到条目的 ::before(原来是 aside 的),top 从 12px 调到 14px,
  与加了 4px 上边距的节点底部对齐,绳子依旧穿过每个节且末项不收口
- head 用 flex + baseline + gap:8px,状态过长时时间自然换行而不重叠
- 售后详细弹窗与订单详情弹窗共用同一套标记,两侧同步改

验证:tests/widget-system/ 462 项、tests/admin/ 76 项通过;用 node 分别抽取
两个 JS 里的时间线构造函数跑样例载荷,确认输出为
[节点][状态][时间] + 另起一行的备注;重建 public.css / public.min.css,
diff 仅限时间线规则。未做浏览器视觉验收。
2026-10-09 18:01:16 +08:00
kate522 26ce9a5904 feat(after-sale): 售后处理记录改为每次提交留痕并新增时间线
工单行内新增 handling_log 列,用户申请、用户撤销与后台每次改状态都落一条
「状态 + 备注 + 来源 + 时间」,按时间正序存放、上限 50 条(对齐
MetaAssetService::MAX_RECORDS)。set_status 因此变成读-改-写,补上
GET_LOCK/RELEASE_LOCK 包裹,并在锁内重读工单快照——锁外取快照仍会被并发
追加覆盖。

三处界面由「只显示最后一条」改为回放整条处理链路:

- 后台订单明细「售后记录」按工单渲染头部(状态/金额/申请时间)+ 原因 +
  完整处理记录
- 售后面板手风琴「处理备注」单独占整行并置于「申请说明」上方,删掉已无
  意义的「审核时间」
- 前台售后详细弹窗与订单详情弹窗的售后记录改为时间线:左列绳结状态条
  (节点 + 状态 + 时间),右列处理备注;样式只在 modal-runtime.css 定义
  一份,两个弹窗共用

清理随之失效的死代码:工单的 admin_note / reviewed_at / completed_at,以及
全仓库无写入方的 gateway_request_id / gateway_refund_id / gateway_result;
连带删掉 gatewayResultText()、uninstall 里从未存在过的 aksrt_after_sale
表项,并把两处后台页面重复的状态标签数组收成 after_sale_status_labels()、
重复的 .aksrt-as-log 样式收成 after_sale_log_css()。

顺带修复 88669f69 重构退款流水线时误删 refund_amount()、却仍在 create()
里调用的问题——此前任何用户提交售后都会直接 fatal。

验证:tests/widget-system/ 462 项、tests/admin/ 76 项通过;新增
after-sale-log-harness.php 驱动真实 AfterSaleService,覆盖首条日志、空备注
也留痕、撤销记录、50 条上限与畸形行规范化;两个后台页面经 dev harness
渲染校验「处理备注」宽行位于「申请说明」之前;未做浏览器视觉验收。
2026-10-09 13:59:50 +08:00
kate522 3eb9cc739c style(modal): 售后与订单弹窗底部按钮满幅化并优化悬停反馈 2026-10-07 10:18:48 +08:00
kate522 e9a1a75259 fix(styles): 还原弹窗 footer 相邻按钮分界线并补悬停阴影
被平铺规则的 border:0 抹掉的 legacy 分隔线按家族色还原,售后四弹窗与订单详情同款;更新构建产物与契约测试。
2026-10-07 09:53:10 +08:00
kate522 8023e2e472 feat(after-sale): 支持撤销售后工单并接入人机验证 2026-10-07 09:20:59 +08:00
kate522 918ca371be chore(repo): 行尾规范化,恢复索引 LF 契约
远端 9 个文件以 CRLF 入库,违反 .gitattributes「索引一律 LF」声明,
在 autocrlf=true 的工作区产生永久假脏并阻塞合并。仅行尾变化,无内容改动。
2026-10-07 09:13:42 +08:00
kate522 732f615a09 style(payment): 炫彩标题区样式扩展到订单详情弹窗 2026-10-06 18:27:27 +00:00
kate522 1a9a2762f3 feat(payment): 新增「售后策略」配置与前台售后弹窗链路
- 后台「支付&付费 → 售后策略」:售后功能开关、申请时效、超时效提示(HTML)、
  预置原因、申请售后说明、所需材料、申请说明占位文案、可上传图片开关;
  PHP schema 与 SPA schema 1:1 对齐,group 字段支持拖拽排序
- PaymentRuntime 新增售后设置解析(缺键回退 schema 默认、HTML 走 kses、
  中文原因稳定 slug)与 after_sale_expired 时效判定(支付时间起算)
- 售后工单支持举证图片(order_aftersale.images 列,最多 3 张,
  复用评论图片上传校验)与上传路由;订单视图模型输出 after_sale 按钮状态
- 前台申请 / 超时效提示 / 售后详细三个独立弹窗(modal-mini + 蓝色炫彩头),
  订单详情弹窗不再内嵌售后表单并清理相关死代码
- 新增 after-sale-policy 契约测试与运行时 harness,527 项契约测试通过
2026-10-06 22:21:47 +08:00
kate522andClaude Fable 5 b5f37d6d46 fix(pagination): align AJAX load-more loading state with reference
Loading indication now matches the reference theme's post_ajax() contract:

- Stop overwriting the 加载更多 button label with 正在加载...; the button
  keeps its own text and the .post_ajax_loader skeleton carries the busy
  state instead.
- Add the reference fallback loader (.loading ring + 加载中...) when no
  skeleton exists in the DOM, and reinsert the skeleton after the last
  card on every page load like beforeSend does.
- Retext the loader string to 加载中... (zib__('loading')) and make the
  ajax_trigger default a translatable string.
- Route ajax-list.js / comments.js / widgets frontend.js through the
  .is-loading class instead of inline display, which outranked the class
  rule and could freeze the skeleton shut.
- Drop the search.php inline display:none that defeated .is-loading, and
  remove the unproduced .post-ajax-loader / [data-aksrt-ajax-loader] shims.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-03 17:23:44 +08:00
kate522andClaude Fable 5 89a9cf24e9 fix(posts): repair the load-more skeleton and align it with the reference
Clicking 加载更多 showed no skeleton at all, and the placeholder shape did
not match the reference theme's post cards.

- Class-name mismatch: pagination.js queried .post-ajax-loader while every
  PHP producer emits .post_ajax_loader, so showSkeleton() never found a
  target and the loader stayed hidden. Accept both spellings.
- Inline display:none on the loader (pagination, comments, widget tab
  panes) outranked .is-loading, so even the correct selector could not
  reveal it. Drop the inline style; the reference uses a bare
  <div class="post_ajax_loader"> and toggles visibility from JS.
- loadNextPage() set indicator.style.display = "flex", which forced the
  .posts-item.card skeleton blocks (inline-block layout) into a single
  row instead of stacking vertically — the visible symptom. Remove those
  inline writes and drive .is-loading from CSS for every surface.
- Scope the hide/show rules on the class itself rather than requiring an
  .ajaxpager ancestor: homepage tab panes, archive streams and comment
  lists sit at three different depths.
- Rebuild aksrt_render_placeholder() on zib_get_post_placeholder's
  structure — list = t1/k1/k2 + two s1, card = t1 item-heading/k2 + two
  s1, thumb wrapped in post-graphic + radius8 item-thumbnail, all carrying
  ajax-item so the loader is not mistaken for a real card.
- Add mini/mini_thumb modes so ContentRenderer::placeholder() stops
  hand-rolling the same markup; normalize its 'lists' arg to 'list'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-03 15:42:18 +08:00
kate522 a4af024f32 feat(ban): align ban prompts with the reference theme modals and block page
对照参考主题(Zibll)修正四项偏差,申诉链路从页面整体迁移至弹窗:

- feat(ban): 新增三个 1:1 复刻弹窗(REST /user-ban/{info,appeal,log}-modal):
  查看禁封状态(多彩头 c-gray + 信息行 + 申诉按钮)、禁封申诉(edit-header
  gavel/c-blue + 信息框 + 资料项 data[key] 输入 + 审核中态 + 禁止申诉态)、
  禁封记录(多彩头 c-yellow + 计数 + 逐条卡片);user-ban.js 增加
  data-aksrt-ban-info/appeal/log 委托与 admin-ajax 申诉提交
- feat(ban): 禁封/解封弹窗的「当前用户已有N次禁封记录」静态文案改为可点击
  黄链(N>1),打开禁封记录弹窗;解封视图信息行改复用统一数据源
- refactor(ban): 治理展示态收敛至 Restriction::state_badges()(封号=c-red
  违规已封号 / 小黑屋=c-yellow 小黑屋禁封中 / 禁言=c-gray 禁言中,颜色与
  字段统一管理)+ get_ban_info()(状态/起止时间/原因/说明/申诉权限,
  三弹窗与禁封页共用)
- feat(ban): 登录行为对齐参考主题——移除登录期拦截(enforce_login_ban)
  与 complete_login 禁封闸门,封号用户可正常登录;新增 template_redirect
  前台禁封页(1:1 zib_user_ban_restrict_login/zib_die_page:红盒提示 +
  信息行 + 申诉权限 + 退出登录/申诉按钮,模板 templates/parts/user/ban-block.php)
- feat(profile): 公开主页禁封状态从资料卡下方提示框改为占据身份行元信息位
  的 badg 徽章(参考主题 author_header_identity 语义);用户中心元信息改为
  but 按钮(徽章+箭头)打开查看禁封状态弹窗;评论拒绝态链接同源
- refactor: 移除申诉页面模板(page-ban-appeal.php)及其资产/注册/入队、
  get_profile_notice/profile_notice 链、state_labels/state_notices/
  login_ban_message、UserRuntime 的 public_notice/ban_notice_text 死配置、
  report_settings 死键、无消费方的 aksrt_ban_appeal_status 端点
- feat(ban): BanAppeal::ajax_submit 兼容弹窗 data[key] 表单结构
  (逐项清洗入库,申诉说明取 data['申诉说明'],旧平铺字段保持兼容)
- chore: 重新构建 public.css/public.min.css(三个新弹窗几何与头部样式)
- test: 契约测试重写(徽章元信息位、三弹窗与记录链接、禁封页、允许登录 +
  前台拦截、死代码移除断言),全量 362 项通过
2026-09-22 19:14:02 +08:00
kate522 7597972eea feat(user-ban): frontend ban modal replaces the backend restrictions tab
1:1 replicate the reference theme user-ban flow on the frontend:

- Ban/unban modal rendered by inc/helpers/user-ban.php, opened via
  AksrtModalRuntime.openRemote (600px desktop dialog, 360px initial
  height, mobile bottom drawer, c-yellow colorful unban header).
- Three entry points wired: user homepage cover three-dot menu,
  comment section menu, article author card menu (ban item takes
  priority over report, matching the reference hook order).
- REST endpoints /user-ban/modal and /user-ban/set under aksrt/v1,
  guarded by check_permission, persisted through User\Restriction.
- Submit semantics match zib_ajax_set_user_ban: ban reloads the page,
  unban only closes the modal.

Cleanup (no dual-track): the backend ban-restrictions tab, its form
handlers and flash keys are removed; ticket processing links to the
frontend user homepage as the reference theme does; admin/report and
appeal notification URLs move to type/status query args.

Verification: 337/337 widget-system contract tests, 41/41 admin and
front-submit tests, php -l clean on all changed PHP files.
2026-09-20 18:42:53 +08:00
kate522 2882b83c21 fix(report): restore the centered plus icon in the image upload box
main.css already ships the reference plus icon (.preview .add:before/:after,
25px rounded bars centered via inset:0 + margin:auto). The duplicate
left/top:50% + translate version in modal-runtime.css over-constrained
the same pseudo elements (right/bottom:0 + margin:auto from main.css
still applied), pushing the bars to 75%/75% so only an L-shaped corner
was visible. Drop the duplicate and lock the single-source contract in
the report test.
2026-09-20 14:28:05 +08:00
kate522 7a80297a56 fix(report): align the report modal with the reference theme 1:1
- read preset reasons from the real source (ban_preset_reason via
  UserRuntime::ban_settings); the previous injection read a
  nonexistent report.categories key, so backend reasons never showed
- rebuild the modal on the reference markup contract: edit-header
  warning header, reason chips toggling the reference .active class
  (focus color), other-reason textarea, violation link, description,
  but-average modal-buts footer
- add image evidence upload gated by user_report_img_s: new
  /report/upload-image route reusing the comment upload handler with
  its own permission, preview/remove UI, up to 3 images appended to
  the report content
- render the backend report notice (user_report_desc, sanitized HTML)
  inside the modal
- widen the dialog to the reference 600px desktop geometry and share
  the form-upload preview styles with the order review modal
- extend the report contract test accordingly
2026-09-20 14:20:09 +08:00
kate522 2f571c2a98 fix(checkin): keep modal runtime shell fullscreen with modal-mini class
Trigger classes are applied to the fixed shell; .modal-mini's
width:400px + auto margins collapsed the shell (and its backdrop)
into a centered column. Neutralize dialog-only width classes on the
shell so the fullscreen backdrop covers the viewport again.
2026-09-19 15:47:23 +08:00
kate522 cd2b5f6c48 fix(checkin): restore fullscreen reference backdrop on details modal
The reference modal backdrop is a fullscreen, non-transparent black
50% overlay; drop the mistaken transparent-overlay override for the
checkin shell and keep the shared runtime backdrop.
2026-09-19 15:29:33 +08:00
kate522 f7e0dbc1ce fix(checkin): drop visible backdrop for checkin details modal
Reference site keeps the page fully visible while the checkin modal
is open; make the shell overlay transparent (still closes on click
outside) and rebuild public bundles.
2026-09-19 15:17:45 +08:00
kate522 359a607010 fix(checkin): align modal overlay with reference backdrop style
Reference .modal-backdrop is plain black at 50% opacity with no blur;
replace the runtime overlay's blue-dark tint and backdrop blur to
match it 1:1.
2026-09-19 15:05:55 +08:00
kate522 4653128a18 feat(checkin): open reference-parity details modal from signed buttons
- Rebuild checkin details modal HTML 1:1 with reference theme:
  colorful header c-blue, user card (points/level), daily reward
  card and 7-day continuous mini cards
- Signed entry buttons (header dropdown, user-card badge, user
  center) become clickable checkin-details-link modal triggers
  instead of disabled buttons
- Swap data-signin-btn to details link in place after successful
  signin (reference .initiate-checkin replacement behavior)
- Add aksrt-checkin-details-modal runtime shell (modal-mini 400px)
- Remove dead code: legacy lite modal, admin-ajax details handler,
  /signin/history route, signin calendar; rebuild public bundles
- Update contract tests to the new checkin modal contract
2026-09-19 14:49:58 +08:00
kate522 c06aadb9a9 fix(css): 弹窗在 portal 内重建共享令牌基座,修复样式静默失效
弹窗外壳由 runtime portal 到 <body>,已经位于任何页面级 token 作用域之外。此前
路由样式把语义别名挂在页面容器上:

  .aksrt-orders-page { --orders-ink: var(--color-ink, #333); }

弹窗内部再消费 var(--orders-ink) 时该变量不存在,整条声明被判为无效值丢弃。
CSS 自定义属性解析失败不会抛错,只表现为边框消失、次级文字发灰发透明,排查
成本很高——订单详情弹窗此前「样式没做完」的观感就来自这里。

修法是在弹窗外壳上重建基座,而不是逐个给弹窗补别名:外壳永远位于 portal 内,
任何弹窗都能直接消费。

- modal-runtime.css 新增 portal token bridge,在 .aksrt-modal-runtime 上声明
  --modal-ink / -ink-2 / -muted / -line / -line-soft / -surface / -surface-2 /
  -surface-3 / -primary / -on-primary / -warning / -success / -danger,
  全部以 var(--color-*, 回退值) 兜底
- orders.css 的 --detail-* 由直接推导调色板值改为映射 var(--modal-*),路由文件
  不再自行持有颜色定义,避免同类问题在下一个弹窗上重演
- 重建 assets/css/build/public.css 与 public.min.css

验证:node --test(已追踪用例)333 项 / 333 通过;确认重建后的 public.min.css
已包含 --modal-ink。视觉核对未在本次提交内完成(需要可运行的站点),如需可在
真实环境按订单详情弹窗逐项比对边框、分隔线与次级文字色。
2026-09-18 23:17:18 +08:00
kate522andClaude Fable 5 34150f3358 refactor(modal): remove legacy compatibility paths
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 00:06:37 +08:00