Summary
| Check | Result |
|---|---|
| Table renders one row per course with columns Course, Status, Enrollment, Students, Lessons, Rating, Updated, Actions | Pass |
| Cell text lines up with its column header (measured left edges) | Pass 0px delta on all 8 columns; the page layout centers text, so the table sets text-start on its container |
| Status filter tabs (All 6 · Published 4 · Draft 1 · Archived 1) and result counter | Pass |
| Client-side sort on Course, Students, Lessons, Rating, Updated; direction arrow always to the right of the label | Pass |
| Row actions kebab: Manage, Settings, Archive, Open Enrollment, Duplicate (state-changing items not clicked) | Pass |
| Mobile (390px) falls back to a stacked list, no table overflow | Pass |
| Vietnamese locale: all headers translated, no raw i18n keys | Pass |
| Browser console errors / warnings during the walkthrough | None |
| Course title and meta line were centered in the cell (inherited text-align) | Found → fixed in commits dd6a8f9 → 4615232, screenshots below are after the fix |
| Column widths: metric columns were ~130-143px each for 1-3 character values, spreading the row across the full 1440px | Found → fixed Students/Lessons/Rating pinned to 96px, Updated 128px, Course takes the slack (638px), table fills the container width. Every column is left-aligned, header and cells, and sort icons always trail the label. Screenshots are after the fix. |
| Search by course name or slug, case-insensitive, debounced, combines with the status tabs | Pass |
| Backend change | Scoped new name_or_slug filter + test; the broad search filter is untouched |
Desktop (1440×900)




Sorting, row menu, hover



Search
Matches course name or slug only, case-insensitive, 300ms debounce, resets to page 1, and combines with the status tabs.
| Query | Result |
|---|---|
java | Advanced JavaScript |
JAVA / JaVaScRiPt | Advanced JavaScript (case-insensitive) |
for-beginners | Python for Beginners — slug-only match, the title has spaces |
FOR-BEGINNERS | Python for Beginners |
Draft tab + data | Data Science Essentials |
zzzz | No-results state with a Clear search action |
Why a backend filter: the existing search param also matches summary and description, so java returned Web Development Fundamentals because its description mentions Java. On a management list that reads as a bug, so the instructor endpoint got a strict name-or-slug filter. The broad filter still serves the public catalog.




Mobile (390×844)


Vietnamese locale

Test setup
- Stack:
docker compose up --build --waitin the SP-684 worktree, thenalembic upgrade headandyarn db:seed(fresh DB). - Account: skillpixeldeveloper@gmail.com (ADMIN / INSTRUCTOR), 6 seeded courses. Session injected with a minted Firebase custom token into headless Chromium (Playwright), since the local Chrome profile was not signed in on localhost.
- Screenshots: full-page at device scale 2, saved as WebP q82.
Notes and follow-ups
- Backend course tests pass (128) including a new case for the name-or-slug filter. The regenerated API client also picked up unrelated drift from the merged managed-domain delete change.
- Sorting is per page (client side). Server-side sort, ungraded / pending-review counts, owner column, admin "all instructors" toggle and bulk actions are out of scope for SP-684 and need backend work.
CourseActionCardis no longer rendered anywhere. Left in place for now; delete in a cleanup PR.- Seed data has few courses, so pagination (page size 12) was not exercised here.