- New lib/components/pagination/pagination.svelte with numbered pages,
ellipsis for large ranges, and prev/next buttons
- All 6 admin pages (users, articles, videos, recordings, comments,
queues) now show enumerated page numbers next to the "Showing X–Y of Z"
label; offset is derived from page number * limit
- Public pages (videos, models, magazine) replace their inline
totalPages/pageNumbers derived state with the shared component
- Removes ~80 lines of duplicated pagination logic across 9 files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move queue, status, and offset to URL search params (?queue=&status=&offset=)
- Load jobs server-side in +page.server.ts with auth token (matches other admin pages)
- Derive total from adminQueues counts (waiting+active+completed+failed+delayed)
so pagination knows total without an extra query
- Add fetchFn/token params to getAdminQueueJobs for server-side use
- Retry/remove/pause/resume actions now use invalidateAll() instead of local state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- revokePoints now accepts optional recordingId; when absent it deletes
one matching row (for actions like COMMENT_CREATE that have no recording)
- deleteComment queues revokePoints + checkAchievements so leaderboard
and social achievements stay in sync
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- deleteRecording now queues revokePoints for RECORDING_CREATE (and
RECORDING_FEATURED if applicable) before deleting a published recording,
so leaderboard points are correctly removed
- Fix comment stat/achievement queries using collection "recordings" instead
of "videos" — comments are stored under collection "videos", so the count
was always 0, breaking COMMENT_CREATE stats and social achievements
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Once an achievement is unlocked, preserve date_unlocked permanently
instead of clearing it to null when the user drops below the threshold
(e.g. on unpublish). This prevents the wasUnlocked check from returning
false on republish, which was causing achievement points to be re-awarded
on every publish/unpublish cycle.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PostgreSQL cannot resolve the type of a parameterized $1 = 0.005 in
-$1 * EXTRACT(EPOCH ...) and fails with an operator type error. Using
sql.raw() embeds the constant directly in the query string so userId
is the only parameter.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Passing a JS Date to a Drizzle sql template serializes it as a locale
string (e.g. "Mon Mar 09 2026 19:51:22 GMT+0100") which PostgreSQL
cannot parse as timestamptz, causing the gamification worker to fail.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add migration 0004: partial unique index on user_points (user_id, action, recording_id)
for RECORDING_CREATE and RECORDING_FEATURED to prevent earn-on-republish farming
- Add revokePoints() to gamification lib; awardPoints() now uses onConflictDoNothing
- Add gamificationQueue (BullMQ) with 3-attempt exponential backoff
- Add gamification worker handling awardPoints, revokePoints, checkAchievements jobs
- Move all inline gamification calls in recordings + comments resolvers to queue
- Revoke RECORDING_CREATE points when a recording is unpublished (published → draft)
- Register gamification worker at server startup alongside mail worker
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace custom inline span+getStatusColor with Badge component in recording card
- Align admin recordings table badge to same style (outline, green/yellow)
- Use i18n label in admin table instead of raw status string
- Remove unused cn import and getStatusColor helper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove back button from admin entity edit pages (sidebar handles navigation)
- Remove cancel button from video/article forms, make submit button full-width
- Show actual entity title + subtitle on video/article edit pages
- Remove asterisks from Title/Slug field labels in i18n
- Remove px-3 sm:px-0 from all admin list page headers/filters (fixes mobile padding)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace ← text with icon-[ri--arrow-left-line] in admin and me layouts
- Add avatar + admin shield badge to admin sidebar header
- Wrap all admin edit forms in Card (bg-card/50 border-primary/20) with styled inputs
- Fix sm:pl-6 → lg:pl-6 so extra left padding only applies when sidebar is visible
- Update security form submit button to gradient style matching profile
- Remove "View Public Profile" button from me/profile
- Use shadcn-svelte Empty component for recordings empty state
- Install empty component via shadcn-svelte
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add BullMQ to backend; mail jobs (verification, password reset) now enqueued instead of sent inline
- Mail worker processes jobs with 3-attempt exponential backoff retry
- Admin GraphQL resolvers: adminQueues, adminQueueJobs, adminRetryJob, adminRemoveJob, adminPauseQueue, adminResumeQueue
- Admin frontend page at /admin/queues: queue cards with counts, job table with status filter, retry/remove/pause actions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- #[allow(dead_code)] on FFICallbackContextWrapper, Connected variant,
Unsubscribe variant, and device_event_receiver field
- let _ = for unused Result from callback.call1() (x2) and .send().await
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>