Merge develop into main #1

Merged
nvirellia merged 132 commits from develop into main 2026-05-03 14:00:39 +00:00

132 Commits

Author SHA1 Message Date
cbe0ec88e4 fix: daisyui theme
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Client Prod Build (NixCN CMS) TeamCity build finished
2026-05-02 21:59:53 +08:00
46a2451e98 Remove antfu config from zed settings
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-05-02 20:23:25 +08:00
267f97a1e7 Change node version to 24 and pin pnpm version
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-05-02 20:23:25 +08:00
a2d2f8c752 Init devenv shell
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-05-02 20:23:25 +08:00
bd7ff728fb Make the primary color nix-themed 2026-05-02 20:23:25 +08:00
b5784d2e46 Add devenv for project
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-05-02 20:23:25 +08:00
95539c3807 fix(profile): update UI constraints and tests to match new 5-255/24 limits
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:58:29 +08:00
1a11af5ea4 fix: update username/nickname constraints to match backend
Username: 3–32 → 5–255. Nickname: max 64 → max 24 (UTF-8 rune limit).
Applied to both onboarding and profile schemas.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:55:07 +08:00
9d0e591412 docs(onboarding): reformat plan and spec to consistent tab indentation
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:46:08 +08:00
f386ac8937 chore(onboarding): add cast comment and run lint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:45:32 +08:00
c5784f80dc test(onboarding): add E2E tests for UUID-username onboarding dialog
Also fixes the `completeProfile` form action: SvelteKit only supports form
actions in `+page.server.ts`, not `+layout.server.ts`. Moved the action to a
dedicated `/(app)/onboarding/+page.server.ts` and updated the dialog form's
`action` attribute to the absolute path `/app/onboarding?/completeProfile`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:44:06 +08:00
c4419a1193 feat(onboarding): add OnboardingDialog component and wire into app layout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:29:43 +08:00
cb325692f7 feat(onboarding): detect UUID username and add completeProfile layout action
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:21:37 +08:00
5754c163d7 feat(onboarding): add onboarding Zod schema
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:14:57 +08:00
18de60285e docs: add onboarding dialog implementation plan
6-task plan: schema → layout server → component → layout wire-up → E2E tests → lint/build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:10:56 +08:00
66ca0ed2a0 docs: add onboarding dialog design spec
Mandatory username/nickname gate for newly registered users (UUID username).
Layout-level detection + named action + Bits UI dialog, no dedicated route.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 00:02:21 +08:00
e80ad704d1 feat(sentry): add /vitals tunnel to bypass adblockers
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Routes Sentry envelopes through a first-party /app/vitals endpoint
instead of directly to *.ingest.sentry.io. DSN host and project ID
are validated server-side from the SENTRY_DSN env var before proxying.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 20:35:37 +08:00
e1009ed622 docs: add navigation progress bar design spec and implementation plan
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 20:09:39 +08:00
e5acc171c6 feat(ux): add navigation progress bar for SSR load feedback
Thin primary-color bar at top of viewport appears after 200 ms of
navigation latency and snaps away when the new page renders. Uses
nprogress wired to SvelteKit's navigating store via a Svelte 5
$effect. Fast navigations (< 200 ms) produce no visible flash.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 20:09:18 +08:00
1cdefc4f49 fix(agenda): use badge-warning for pending status to fix contrast
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
badge-neutral on the dark theme renders with insufficient contrast;
badge-warning (amber) is both readable and semantically correct for
an awaiting-review state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:52:22 +08:00
60f7badbb7 fix(admin-events): redirect to list after event creation
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
After creating an event, redirect to /admin/events instead of the new
event's edit page, which was confusing since the user hasn't configured
the event yet. Adds E2E test to assert the post-create redirect.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:50:39 +08:00
2c5426671e chore: replace hand-drawn NixOS SVG with official logo paths
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:40:48 +08:00
be8b8c0d94 fix(sentry): use includes() instead of startsWith() for cdn-cgi filter
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
window.onerror reports script URLs as absolute URLs, so f.filename is
"https://test.nix.org.cn/cdn-cgi/..." — startsWith('/cdn-cgi/') never
matched and Cloudflare email-decode noise kept reaching Sentry.

Fixes NIXCN-CMS-TEST-1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:35:02 +08:00
972965e0ea fix(checkin): use SvelteMap for dedup cache, track plan + format docs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:34:49 +08:00
d14fdf20d1 fix(checkin): strict 6-digit QR validation + 10s dedup cooldown
- Reject QR codes whose raw text is not exactly 6 digits (no strip-and-guess)
- Suppress repeated onScan calls for the same code within 10 s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:30:15 +08:00
32555f21ab docs: add QR scanner dedup & strict validation design spec
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:25:37 +08:00
6059fa0214 chore(profile): remove 500-char limit from bio placeholder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:20:40 +08:00
705af7f30b docs: add token refresh race condition design doc
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Documents the production 401-on-rotation race, the four-layer client-side
fix (proactive refresh, in-process single-flight, rotation cache, 401
interceptor), the backend AT lifetime extension to 5min, and every
alternative considered with the reasoning behind each rejection.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:12:21 +08:00
5de1518784 fix(session): raise AT lifetime to 5min, proactive buffer to 30s
Backend extended TTL_ACCESS to 300s. Matching the client-side constants
keeps proactive refresh in sync — fires 30s before expiry instead of
5s before a 15s token, reducing refresh frequency by ~20x.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 19:09:36 +08:00
ccb7680d38 fix(session): proactive token refresh to eliminate 401 rotation races
The backend does not implement a refresh-token grace window. Without it,
two browser requests sent within the same ~15s window but in separate
HTTP round-trips both carry the same expired access token. The first
request successfully rotates the token; the backend immediately invalidates
the old token; the second request arrives after the first response's
Set-Cookie has been sent but before the browser has applied it, so it
still presents the old token — and the backend rejects the refresh.

The recentlyRotated cache (previous commit) covers same-process sequential
races. This commit adds the primary defence:

Proactive refresh in hooks.server.ts: decode the access token's iat claim
(payload only, no signature verification) and rotate when ≤5s remain on
the 15s lifetime, before any API call is made. All parallel load functions
in the same request then see a freshly-issued token. The single-flight in
refreshSingleFlight collapses simultaneous proactive refreshes from
concurrent browser requests on the same pod. The 401 interceptor in
api.ts remains as a safety net for unexpected cases (clock skew, etc.).

Adds getJwtIat and isTokenAboutToExpire helpers with full unit tests.
Updates CLAUDE.md to document the two-path refresh design.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 18:12:12 +08:00
86ba252dde fix(session): cache rotation result to survive sequential refresh races
Root cause (confirmed from backend logs): when two browser requests are
in-flight simultaneously with the same expired access token, the second
request arrives at the backend ~1s after the first refresh completed.
By then the backend's grace window has closed and it rejects the already-
rotated refresh token with 401.

The existing single-flight mechanism only collapses truly concurrent
refreshes (requests that arrive before the first promise resolves). It
cannot help a sequential caller that arrives after the first refresh
completes and its inFlight entry is deleted.

Fix: keep a process-level recentlyRotated cache keyed by the old refresh
token for 10 seconds after a successful rotation. A later caller with the
same old token gets the cached pair immediately, without a second backend
call that would be rejected.

Adds a regression test that reproduces the exact scenario.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 18:07:35 +08:00
4f583e457d chore: use NixOS blue instead of currentColor in favicon SVG
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
`currentColor` inherits black from the browser's favicon rendering context.
Hardcode #7ebae4 (NixOS light blue) so the icon is recognisable in the tab bar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 17:55:44 +08:00
88e21a0bdc fix(session): log refresh failure reason for diagnostics
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
When refreshSingleFlight fails, we previously returned null with no
context. Now each branch emits a console.warn with the relevant detail
(HTTP status, missing token pair, or thrown error message). Sentry's
consoleLoggingIntegration picks these up automatically, so the next
refresh failure will show the backend's actual rejection status in Sentry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 17:47:22 +08:00
fe19146d3d fix(sentry): filter Cloudflare noise and redirect on expired session
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Fixes NIXCN-CMS-TEST-1: add beforeSend filter that drops events where
every stack frame originates from a Cloudflare /cdn-cgi/ script —
pure third-party noise from the email-decode injector.

Fixes NIXCN-CMS-TEST-2: when an API call inside a load function gets a
401 (session expired, refresh failed), loadSdk now throws redirect(303)
to /app/authorize instead of error(401). This gives the user a working
login redirect rather than an error page, and prevents SvelteKit from
passing the deserialized HttpError object to handleError where Sentry
was incorrectly capturing it as an unexpected exception.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 17:40:41 +08:00
a11af6e8b7 chore: use nixos.svg from static/ as favicon
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Replaces the Vite-processed $lib/assets/favicon.svg import with a direct
reference to static/nixos.svg via the base path, adding SVG MIME type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 17:30:30 +08:00
a01c75a2ca chore: rename app to NixCN CMS and add browser title
Corrects misspelling "Nix CN CMS" → "NixCN CMS" across all source files,
docs, and tests; adds <title>NixCN CMS</title> to the root layout; documents
the correct spelling in CLAUDE.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 17:29:37 +08:00
ba6edca4fd fix(auth): disable login button until Turnstile verification completes
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Track verified state via on:turnstile-callback; button shows 等待 Turnstile...
spinner on load and remains disabled until CF widget succeeds or errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 17:27:11 +08:00
a143e4b563 fix(profile): remove 500-char limit on bio field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 17:22:11 +08:00
d3c2d555f7 chore(container): remove PUBLIC_TURNSTILE_SITE_KEY build ARG
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
PUBLIC_TURNSTILE_SITE_KEY uses $env/dynamic/public (runtime), not
static/build-time injection, so it doesn't need to be a build ARG.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 17:04:36 +08:00
21b239492e chore(container): add build ARGs for Sentry and Turnstile env vars
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Declare build-time ARG instructions for PUBLIC_TURNSTILE_SITE_KEY,
VITE_SENTRY_DSN, SENTRY_DSN, SENTRY_ORG, SENTRY_PROJECT, and
SENTRY_AUTH_TOKEN so CI can pass them via --build-arg. TURNSTILE_SECRET_KEY
and SENTRY_DSN are runtime-only and injected via -e at container run time.
Also removes TURNSTILE_SECRET_KEY from .env.example (runtime secret,
not a build-time value).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 16:56:40 +08:00
17892b4546 fix(svelte5): wrap superForm and createKycState calls in untrack()
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
superForm() and createKycState() called during component initialisation
were triggering reactive tracking warnings in Svelte 5 because they
access reactive state internally. Wrapping with untrack() prevents
spurious re-runs of the initialisation logic on subsequent reactive
updates.

Also converts the data.form cast in admin/events/new to $derived so it
stays in sync with SvelteKit page invalidations.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 16:10:51 +08:00
e55dfbe4ee feat(sentry): add error monitoring, tracing, session replay, and structured logging
Integrates @sentry/sveltekit 10.49.0 via the modern SvelteKit path
(instrumentation.server.ts + experimental.instrumentation.server).

- instrumentation.server.ts: server-side init with tracing, logging,
  and console capture (log/warn/error forwarded to Sentry)
- hooks.client.ts: client-side init with browserTracingIntegration,
  replayIntegration (maskAllText/blockAllMedia), consoleLoggingIntegration,
  and handleErrorWithSentry
- hooks.server.ts: adds handleErrorWithSentry export, composes existing
  appHandle with sentryHandle() via sequence(), and sets isolation scope
  attributes (user_id, username, permission_level) per request for
  log/trace correlation
- svelte.config.js: enables experimental.instrumentation.server and
  experimental.tracing.server
- vite.config.ts: adds sentrySvelteKit() plugin for source map uploads
- .env.example: documents VITE_SENTRY_DSN, SENTRY_DSN, SENTRY_ORG,
  SENTRY_PROJECT, SENTRY_AUTH_TOKEN

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 16:10:36 +08:00
4de2112a11 docs: update docker→podman/container references in spec and overview
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:30:47 +08:00
3c4d9dd15a chore: rename Dockerfile/.dockerignore to Containerfile/.containerignore
Project uses Podman; Containerfile is the conventional name.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:30:30 +08:00
786e1c709e docs: mark M9 Polish as shipped in overview; fix 2 residual E2E tests
- overview.md: M9 row flipped to shipped, links to spec/plan added,
  roadmap paragraph updated with actual deliverables, conventions
  updated to reflect dual-theme support
- tests/e2e/auth.spec.ts: add GET /event/list override so the dashboard
  renders after the magic-link → token flow (was 500ing with no override)
- tests/e2e/profile.spec.ts: use { exact: true } on username assertion to
  avoid strict-mode violation (username 'alice' matched 3 elements)
- Formatting: prettier --write pass on polish spec/plan, layout.css,
  layout.svelte

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:27:37 +08:00
3cf40462b3 feat(container): add Dockerfile, Caddyfile, and .dockerignore
Multi-stage Dockerfile for production builds with adapter-node, reverse proxy config with Caddy, and optimized Docker context.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:17:20 +08:00
f5c8cc420f test(e2e): add networkidle wait to auth magic-link flow test
Cookie propagation across the use:enhance redirect chain (authorize →
/token → /) may not settle before Playwright proceeds. Waiting for
networkidle after the submit click gives the chain time to complete
before asserting on the URL and user-menu elements.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:16:26 +08:00
39c44c292e test(e2e): fix profile test remove permission label assertion
ProfileCard never renders the permission level label, so the '普通用户'
assertion always fails. Replace with assertion on loggedInUser.username which
is guaranteed to be rendered.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:15:21 +08:00
c619f4b1b0 test(e2e): fix agenda mock status field and replace create test
Fix 1: mock items in 'agenda tab lists items' now use status/description
fields instead of is_published, matching the page's filter logic.

Fix 2: 'agenda create submits form' replaced with 'approve button opens
approve dialog' since the admin agenda page has no 新增 button — it only
supports review (approve/reject) of user-submitted items.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:14:35 +08:00
034add383e test(e2e): fix attendance mock shape data→data.items
The attendance tab test was providing a mock response with shape
{ status: 200, data: [ ... ] } (flat array). But +page.server.ts for
the admin events page casts the response to read inner?.items ?? [],
expecting shape { status: 200, data: { items: [ ... ] } }.

Updated the mock to wrap the attendance array in an { items: [...] }
object to match the server-side expectation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:12:57 +08:00
13a9413446 test(e2e): fix workbench checkin selector link→button
The "立即签到" button is rendered by bits-ui Dialog.Trigger as a
<button>, not <a>. Change getByRole('link') to getByRole('button')
on lines 120, 133, and 149 for selector consistency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:11:37 +08:00
212aa47f34 feat(theme): add light/dark toggle button and /theme endpoint
POST /app/theme sets the theme cookie and redirects back. Sun/Moon
button in the app navbar submits the form. E2E tests verify SSR
cookie-driven theme switching.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 13:05:21 +08:00
4300216e69 feat(theme): wire SSR theme cookie via transformPageChunk
Reads theme cookie in hooks.server.ts and rewrites data-theme on <html>
before the page is sent. Root and (app) layout servers expose theme so
the navbar can show the correct toggle icon. Removes hardcoded
color-scheme meta (DaisyUI CSS handles it per-theme).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 12:59:43 +08:00
2ad7519406 feat(theme): add light DaisyUI theme palette
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 12:55:56 +08:00
9c7376fa73 fix: set html lang to zh-CN
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 12:55:49 +08:00
ccdbdea83b docs: add M9 Polish implementation plan
12 bite-sized tasks: light theme CSS, SSR transformPageChunk pipeline,
theme endpoint + navbar toggle with E2E coverage, 6 targeted E2E test
fixes (wrong mock shapes / wrong role selectors), and Dockerfile +
Caddyfile for production deployment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 12:54:14 +08:00
9c6fcba1df docs: add M9 Polish design spec
Covers three deliverables: light theme + SSR cookie toggle (form-action
approach, transformPageChunk in hooks.server.ts), multi-stage Dockerfile
+ Caddyfile for adapter-node production deploy, and fixes for 6 failing
E2E tests (option b: tests corrected to match existing code — wrong mock
shapes, wrong Playwright role selectors, and tests for features that were
never built).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 12:48:14 +08:00
1ce29e4276 docs: update M9 Polish — drop Storybook, add test-passing deliverable
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 12:18:06 +08:00
9bdc3ece72 feat: svelte init 2026-04-18 12:14:30 +08:00
b491aa3669 cleanup: everything 2026-04-18 12:14:04 +08:00
8f328a1601 chore: ignore .worktrees directory
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
2026-04-08 20:54:43 +08:00
4365304845 feat(kyc): improve KYC UI with structured info display and spinner
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
- Replace raw JSON pre-block with typed KycInfoDetail component for passport/cnrid info
- Upgrade KYC method selection from dropdown to card-based picker with icons
- Fix passport form loading state: replace '...' with Spinner component

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 15:03:02 +08:00
9fa83b0a6f fix(checkin): force white background on QR code for scanner compatibility
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:21:12 +08:00
615e42f05a fix: redirect to /authorize before rendering workbench if no token exists
Prevents the unauthorized error page from briefly flashing before the
logout interceptor navigates away, by checking token presence synchronously
in beforeLoad and throwing a redirect immediately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 14:19:42 +08:00
a6469f4737 feat(checkin): add camera switcher to scanner dialog
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:53:19 +08:00
7349997ab5 feat: resolve backend error codes to human-readable messages on error page
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Uses getErrorMessage from the new exception.gen.ts dictionary to display
Chinese error descriptions instead of raw 13-char error codes in GlobalError.
Adds gen:errors script and supporting files for regenerating the dictionary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:38:13 +08:00
036a4e7fa5 fix(admin/users): invalidate getUserList queries after permission update
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 11:33:24 +08:00
f9d0531713 fix: use correct user update endpoints and wrap mutations in hooks
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
- useUpdateUserById: new hook for PATCH /user/update/{user_id} (admin updating others)
- user-permission-edit: switch to useUpdateUserById, move user_id from body to path
- useAuthToken: extract bare useMutation in token route into a proper data hook
- edit-profile: rename shadowed callback param (no behavior change)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 11:27:25 +08:00
5c2e48f0b3 fix(agenda): link submitter nicknames to profiles
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Update the admin agenda list to use the regenerated user_profile payload so submitter nicknames render correctly and link to the submitter's public profile.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 21:04:59 +08:00
d597c09905 fix(agenda): refresh admin agenda list after review
Use the generated agenda list query key so approve/reject, schedule, and edit mutations invalidate the correct cache entry. Sync the generated client types with the updated agenda list response shape.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 20:46:25 +08:00
1322ba48d4 fix: update API types, pass event_id in agenda review, sync story fixtures
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
- Regenerate client types making required fields non-optional
- Add checkin_count/join_count fields to EventListItems
- Pass event_id in agenda approve/reject mutations
- Use non-null assertion for eventId in event update form
- Replace kyc_status with kyc_info in attendance story examples
- Remove kycStatusFilter prop from attendance list stories

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 20:36:32 +08:00
6e2fba8ec2 fix(agenda): show review buttons for pending agendas regardless of publish state
Pending agendas should always be reviewable by admins. The previous
!isPublished guard hid approve/reject buttons even when pending items
existed after agenda publication.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 20:32:05 +08:00
76921428b1 docs: add E2E test suite implementation plan
15 tasks covering Cypress setup, 92 test cases across 5 permission
levels, seed SQL, and Docker Compose CI configuration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 19:53:03 +08:00
6f17e25334 docs: update E2E spec for CI + Docker Compose + SQL dump
Switch from local-only to CI execution with Docker Compose isolation.
Replace manual test data setup with SQL dump (seed.sql) including
relative timestamps for ongoing event. Add docker-compose.e2e.yml note.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 19:40:48 +08:00
1c8abbff14 docs: add E2E test suite design spec
Covers all user permission levels (Lv10/20/30/40/50), 12 feature
modules, and 92 test cases (happy path + error states) using Cypress
against a real backend with fixed test accounts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 19:31:30 +08:00
efb7975184 fix: make admin event tabs sticky below header, improve list UX
- Sticky tabs now offset by --header-height to avoid hiding behind app header
- Replace type filter buttons with Tabs component for consistent styling
- Use isLoading + keepPreviousData in useAdminEvents to prevent list flash on page/filter change
- Expand trello skill label inference rules

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 19:02:14 +08:00
11268a307f fix: load attendance guide in edit form, fix tab active state, allow agenda unpublish, decode base64 guide, add dark mode prose
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 18:45:34 +08:00
ef4fa016e6 fix(attendance): remove KYC status filter/column, show KYC info col based on event.enable_kyc; fix search input focus loss
- Remove KYC status filter dropdown and status column from attendance list
- Conditionally render KYC type and operation columns based on event's enable_kyc field
- Add keepPreviousData to useAdminAttendance to prevent input focus loss on filter change

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 18:43:58 +08:00
b9d12637b7 feat: add quota/limit fields to event form and update API client types
- Regenerated API client: ServiceEventEventListResponse now wraps paginated
  items (renamed to ServiceEventEventListItems), event info includes quota/limit,
  attendance list includes checked_in_at, joined_at, kyc_status
- Fixed toEventInfo to use renamed ServiceEventEventListItems type
- Added required quota (外显上限) and limit (实际上限) fields to event
  create/edit form with positive integer validation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 18:10:40 +08:00
11929e2644 fix: convert event start/end times to RFC3339 before API submission
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 17:38:51 +08:00
f9e41cad87 fix: fixes by autoresearch
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
2026-03-27 09:04:18 +08:00
ba66fd96db docs: trello card skill 2026-03-27 09:04:00 +08:00
464b392375 docs: debug skill
Some checks failed
Client Check Build (NixCN CMS) TeamCity build failed
2026-03-26 21:20:50 +08:00
2463a51f33 feat: implement hooks 2026-03-26 21:20:49 +08:00
973a242604 fix: resolve build and lint issues from admin interface implementation 2026-03-26 19:54:32 +08:00
007cbb916b feat(agenda): integrate agenda cards into event detail page 2026-03-26 19:48:09 +08:00
cd86a037b5 fix(agenda): use explicit string comparison in formatTime 2026-03-26 19:46:49 +08:00
afb922cd7e fix(agenda): add Loading story variant to agenda submit dialog 2026-03-26 19:39:56 +08:00
32a455bc74 feat(agenda): add user-facing agenda submit/edit dialog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:25:16 +08:00
eaae4e2766 feat(admin): add global and per-event statistics views
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:21:09 +08:00
d9929e2116 fix(admin): fix user management code quality - propagation, stale state, unused props
- Stop click propagation from Edit button to TableRow to prevent double-fire
- Store full editingUser state on open so dialog persists across page changes
- Remove unused targetUserId from dialog view props and currentUserId from container props
- Add useEffect to reset selectedLevel when targetCurrentLevel changes
- Replace double as-unknown-as cast with single typed assertion using ServiceUserUserInfoUpdateData

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:15:55 +08:00
21639f671a fix(admin): add row click handler and permission guard in user management views
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:11:23 +08:00
f0e395a6ef feat(admin): add user management with permission editing
Implements user admin list with sortable columns, permission level filter, and per-row edit button gated by canEditPermission. Adds permission edit dialog with assignable levels and optimistic query invalidation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:08:06 +08:00
76d1f6fdb3 fix(admin): fix agenda list code quality - if-newline, timezone safety, base64 decode
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 15:03:22 +08:00
fe1bd4fb96 fix(admin): add PendingAgendas story variant for agenda list 2026-03-26 15:01:00 +08:00
2861cd91d6 fix(admin): fix agenda list spec compliance - columns, invalidation, story variant
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:59:27 +08:00
607b53fb03 feat(admin): add agenda review and schedule management
Implements AgendaListSkeleton, AgendaListView, AgendaListContainer and story
for reviewing, approving/rejecting, scheduling, and editing agenda submissions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:56:26 +08:00
8429db42dd feat(admin): add attendance list with filters and pagination
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:46:12 +08:00
d29593185d fix(admin): fix double base64 decode and silent create failure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:42:09 +08:00
db67287fa7 feat(admin): add event create/edit form with validation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:38:41 +08:00
b26d9ddada fix(admin): fix import path and improve container code organisation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:32:38 +08:00
4091fbced3 feat(admin): add event admin list with table, sort, filter, pagination
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:29:46 +08:00
55a43788fe feat(admin): add shared example data for admin stories 2026-03-26 14:25:21 +08:00
a897a7e0dc feat(admin): scaffold admin routes, stubs, and event tab navigation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:24:22 +08:00
a09e849409 feat(admin): add placeholder data hooks for admin APIs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:20:58 +08:00
dc0eea0637 feat(admin): add permission-driven admin sidebar navigation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:18:06 +08:00
d9d3ddd539 fix: allow Lv20 checkin managers to see scan button 2026-03-26 14:15:11 +08:00
f008282ae6 feat(admin): add permission utilities and admin nav config
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 14:13:25 +08:00
2e0377b5bc docs: add admin interface implementation plan
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 10:59:16 +08:00
f8031dc557 docs: add admin interface design spec
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 10:30:08 +08:00
cebf706708 docs: add CLAUDE.md
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
2026-03-25 21:48:53 +08:00
7e1f249cc0 feat: workbench background
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
2026-03-19 13:32:20 +08:00
2278fe4b4a refactor: adapt upstream changes
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
2026-03-18 19:03:25 +08:00
cd6cab69bd feat: remove X-Api-Version
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
2026-03-18 18:02:39 +08:00
6130c0ebb8 Modify zed settings
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-03-18 16:01:56 +08:00
d4a690854a Update zed project settings
Some checks failed
Client Check Build (NixCN CMS) TeamCity build failed
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-03-18 15:43:19 +08:00
932396866f Add devenv
Some checks failed
Client Check Build (NixCN CMS) TeamCity build failed
Signed-off-by: Asai Neko <sugar@sne.moe>
2026-03-18 15:40:43 +08:00
4355092863 feat: workbench
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
2026-03-13 17:05:28 +08:00
53d1284ba6 fix: close mobile sidebar on navigation 2026-03-13 16:49:31 +08:00
1d7216da23 fix: keep workbench header sticky while scrolling 2026-03-13 16:47:23 +08:00
7500032250 fix: keep kyc success dialog open until dismissed 2026-03-13 16:45:25 +08:00
757b5d0cb2 feat: upgrade to vite 8
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
2026-03-13 16:39:44 +08:00
bbc2ba6380 Fix concurrent token refresh requests 2026-03-13 16:37:57 +08:00
e3bc904dd5 Add check-in status polling for QR dialog 2026-03-13 16:30:53 +08:00
3e33cf4f5d Fix KYC prompt text alignment on mobile
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
2026-03-13 15:56:59 +08:00
50112a8d2c Add attendance guide to event detail 2026-03-13 15:54:22 +08:00
9297747d9a Implement event details and unify event list flows
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
2026-03-13 15:00:53 +08:00
2af53da3f6 feat: favicon and title
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
2026-03-12 21:29:52 +08:00
59215f4a5e fix: skeleton suspense
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
2026-03-12 21:00:31 +08:00
bada3271df refactor: rename dockerignore to containerignore
All checks were successful
Client Check Build (NixCN CMS) TeamCity build finished
Signed-off-by: Noa Virellia <noa@requiem.garden>
2026-02-18 12:04:25 +08:00
0f8b07a05e feat: improve containerfile
Some checks failed
Client Check Build (NixCN CMS) TeamCity build failed
Signed-off-by: Noa Virellia <noa@requiem.garden>
2026-02-18 12:02:22 +08:00
0a7d69e86b feat: initial commit
Signed-off-by: Noa Virellia <noa@requiem.garden>
2026-02-18 11:54:42 +08:00