@nexttylabs/echo 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (579) hide show
  1. package/.changeset/README.md +21 -0
  2. package/.changeset/config.json +11 -0
  3. package/.changeset/cozy-ghosts-care.md +5 -0
  4. package/.changeset/sharp-lines-stand.md +5 -0
  5. package/.changeset/sour-doodles-eat.md +5 -0
  6. package/.changeset/tender-moose-shop.md +5 -0
  7. package/.github/pull_request_template.md +13 -0
  8. package/.github/workflows/ci.yml +41 -0
  9. package/.github/workflows/publish.yml +44 -0
  10. package/.github/workflows/release.yml +73 -0
  11. package/AGENTS.md +92 -0
  12. package/CHANGELOG.md +13 -0
  13. package/Dockerfile +57 -0
  14. package/LICENSE +661 -0
  15. package/Makefile +77 -0
  16. package/README.md +198 -0
  17. package/app/(auth)/login/page.tsx +53 -0
  18. package/app/(auth)/register/page.tsx +48 -0
  19. package/app/(auth)/sign-in/page.tsx +22 -0
  20. package/app/(dashboard)/admin/feedback/[id]/edit/page.tsx +103 -0
  21. package/app/(dashboard)/admin/feedback/[id]/page.tsx +154 -0
  22. package/app/(dashboard)/admin/feedback/new/page.tsx +91 -0
  23. package/app/(dashboard)/admin/feedback/page.tsx +81 -0
  24. package/app/(dashboard)/admin/layout.tsx +48 -0
  25. package/app/(dashboard)/analytics/portal/page.tsx +30 -0
  26. package/app/(dashboard)/dashboard/page.tsx +133 -0
  27. package/app/(dashboard)/layout.tsx +69 -0
  28. package/app/(dashboard)/no-access/page.tsx +45 -0
  29. package/app/(dashboard)/settings/access/page.tsx +56 -0
  30. package/app/(dashboard)/settings/api-keys/page.tsx +55 -0
  31. package/app/(dashboard)/settings/appearance/page.tsx +40 -0
  32. package/app/(dashboard)/settings/branding/page.tsx +62 -0
  33. package/app/(dashboard)/settings/changelog/page.tsx +51 -0
  34. package/app/(dashboard)/settings/danger-zone/page.tsx +92 -0
  35. package/app/(dashboard)/settings/feedback/page.tsx +63 -0
  36. package/app/(dashboard)/settings/integrations/page.tsx +94 -0
  37. package/app/(dashboard)/settings/layout.tsx +43 -0
  38. package/app/(dashboard)/settings/modules/page.tsx +54 -0
  39. package/app/(dashboard)/settings/notifications/page.tsx +48 -0
  40. package/app/(dashboard)/settings/organization/page.tsx +104 -0
  41. package/app/(dashboard)/settings/organization/portal/access/page.tsx +22 -0
  42. package/app/(dashboard)/settings/organization/portal/experience/page.tsx +22 -0
  43. package/app/(dashboard)/settings/organization/portal/growth/page.tsx +22 -0
  44. package/app/(dashboard)/settings/organization/portal/layout.tsx +24 -0
  45. package/app/(dashboard)/settings/organization/portal/page.tsx +22 -0
  46. package/app/(dashboard)/settings/organizations/[orgId]/members/page.tsx +69 -0
  47. package/app/(dashboard)/settings/organizations/new/page.tsx +36 -0
  48. package/app/(dashboard)/settings/page.tsx +22 -0
  49. package/app/(dashboard)/settings/portal-access/page.tsx +53 -0
  50. package/app/(dashboard)/settings/portal-branding/page.tsx +59 -0
  51. package/app/(dashboard)/settings/portal-growth/page.tsx +57 -0
  52. package/app/(dashboard)/settings/portal-modules/page.tsx +49 -0
  53. package/app/(dashboard)/settings/portal-resources/page.tsx +66 -0
  54. package/app/(dashboard)/settings/profile/page.tsx +48 -0
  55. package/app/(dashboard)/settings/widgets/page.tsx +63 -0
  56. package/app/(public)/[organizationSlug]/changelog/page.tsx +109 -0
  57. package/app/(public)/[organizationSlug]/feedback/[id]/page.tsx +146 -0
  58. package/app/(public)/[organizationSlug]/page.tsx +160 -0
  59. package/app/(public)/[organizationSlug]/roadmap/page.tsx +142 -0
  60. package/app/(public)/docs/page.tsx +48 -0
  61. package/app/(public)/feedback/[id]/not-found.tsx +33 -0
  62. package/app/(public)/feedback/[id]/page.tsx +102 -0
  63. package/app/(public)/invite/[token]/page.tsx +121 -0
  64. package/app/(public)/page.tsx +22 -0
  65. package/app/(public)/widget/[organizationId]/page.tsx +122 -0
  66. package/app/api/_utils.ts +29 -0
  67. package/app/api/admin/backup/route.ts +72 -0
  68. package/app/api/api-keys/[keyId]/route.ts +92 -0
  69. package/app/api/api-keys/route.ts +116 -0
  70. package/app/api/auth/[...all]/route.ts +21 -0
  71. package/app/api/auth/clear-session/route.ts +43 -0
  72. package/app/api/auth/register/handler.ts +176 -0
  73. package/app/api/auth/register/route.ts +26 -0
  74. package/app/api/docs/route.ts +28 -0
  75. package/app/api/feedback/[id]/comments/[commentId]/route.ts +105 -0
  76. package/app/api/feedback/[id]/comments/route.ts +421 -0
  77. package/app/api/feedback/[id]/duplicates/route.ts +285 -0
  78. package/app/api/feedback/[id]/handler.ts +91 -0
  79. package/app/api/feedback/[id]/processing-status/route.ts +199 -0
  80. package/app/api/feedback/[id]/reclassify/route.ts +145 -0
  81. package/app/api/feedback/[id]/route.ts +511 -0
  82. package/app/api/feedback/[id]/suggest-tags/route.ts +227 -0
  83. package/app/api/feedback/[id]/sync-github/route.ts +52 -0
  84. package/app/api/feedback/[id]/vote/route.ts +431 -0
  85. package/app/api/feedback/bulk/route.ts +212 -0
  86. package/app/api/feedback/handler.ts +138 -0
  87. package/app/api/feedback/route.ts +298 -0
  88. package/app/api/feedback/similar/route.ts +100 -0
  89. package/app/api/health/route.test.ts +64 -0
  90. package/app/api/health/route.ts +92 -0
  91. package/app/api/identify/jwt/route.ts +29 -0
  92. package/app/api/integrations/github/route.ts +196 -0
  93. package/app/api/internal/domain-lookup/route.ts +67 -0
  94. package/app/api/invitations/accept/handler.ts +101 -0
  95. package/app/api/invitations/accept/route.ts +29 -0
  96. package/app/api/notifications/preferences/route.ts +109 -0
  97. package/app/api/organizations/[orgId]/handler.ts +123 -0
  98. package/app/api/organizations/[orgId]/invitations/handler.ts +121 -0
  99. package/app/api/organizations/[orgId]/invitations/route.ts +29 -0
  100. package/app/api/organizations/[orgId]/members/[memberId]/handler.ts +208 -0
  101. package/app/api/organizations/[orgId]/members/[memberId]/route.ts +30 -0
  102. package/app/api/organizations/[orgId]/members/handler.ts +77 -0
  103. package/app/api/organizations/[orgId]/members/route.ts +29 -0
  104. package/app/api/organizations/[orgId]/route.ts +30 -0
  105. package/app/api/organizations/handler.ts +97 -0
  106. package/app/api/organizations/route.ts +29 -0
  107. package/app/api/tags/sync/route.ts +88 -0
  108. package/app/api/upload/handler.ts +79 -0
  109. package/app/api/upload/route.ts +37 -0
  110. package/app/api/v1/feedback/[id]/route.ts +276 -0
  111. package/app/api/v1/feedback/route.ts +250 -0
  112. package/app/api/v1/spec/route.ts +356 -0
  113. package/app/api/webhooks/[webhookId]/route.ts +213 -0
  114. package/app/api/webhooks/github/route.ts +158 -0
  115. package/app/api/webhooks/route.ts +143 -0
  116. package/app/favicon.ico +0 -0
  117. package/app/globals.css +139 -0
  118. package/app/health/route.ts +108 -0
  119. package/app/layout.tsx +60 -0
  120. package/bun.lock +2503 -0
  121. package/components/api/rate-limit-info.tsx +86 -0
  122. package/components/api-keys/api-key-manager.tsx +262 -0
  123. package/components/auth/login-form.tsx +207 -0
  124. package/components/auth/register-form.tsx +230 -0
  125. package/components/comment/comment-form.tsx +111 -0
  126. package/components/comment/internal-notes.tsx +219 -0
  127. package/components/comment/public-comments.tsx +387 -0
  128. package/components/component-example-client-only.tsx +29 -0
  129. package/components/component-example.tsx +519 -0
  130. package/components/dashboard/index.ts +22 -0
  131. package/components/dashboard/organization-switcher.tsx +96 -0
  132. package/components/dashboard/quick-actions.tsx +57 -0
  133. package/components/dashboard/recent-feedback-list.tsx +152 -0
  134. package/components/dashboard/stats-cards.tsx +88 -0
  135. package/components/dashboard/status-chart.tsx +106 -0
  136. package/components/example.tsx +70 -0
  137. package/components/feedback/attachment-list.tsx +103 -0
  138. package/components/feedback/auto-classification-badge.tsx +92 -0
  139. package/components/feedback/classification-override.tsx +64 -0
  140. package/components/feedback/duplicate-suggestions-inline.tsx +158 -0
  141. package/components/feedback/duplicate-suggestions.tsx +188 -0
  142. package/components/feedback/embedded-feedback-form.tsx +439 -0
  143. package/components/feedback/feedback-actions.tsx +160 -0
  144. package/components/feedback/feedback-bulk-actions.tsx +184 -0
  145. package/components/feedback/feedback-detail-view.tsx +321 -0
  146. package/components/feedback/feedback-detail.tsx +305 -0
  147. package/components/feedback/feedback-edit-form.tsx +131 -0
  148. package/components/feedback/feedback-filters.tsx +222 -0
  149. package/components/feedback/feedback-list-controls.tsx +433 -0
  150. package/components/feedback/feedback-list-item.tsx +298 -0
  151. package/components/feedback/feedback-list-skeleton.tsx +49 -0
  152. package/components/feedback/feedback-list.tsx +523 -0
  153. package/components/feedback/feedback-sorter.tsx +117 -0
  154. package/components/feedback/feedback-stats.tsx +124 -0
  155. package/components/feedback/file-upload.tsx +289 -0
  156. package/components/feedback/processing-status.tsx +161 -0
  157. package/components/feedback/status-history.tsx +134 -0
  158. package/components/feedback/status-selector.tsx +153 -0
  159. package/components/feedback/submit-on-behalf-form.tsx +403 -0
  160. package/components/feedback/tag-suggestions.tsx +212 -0
  161. package/components/feedback/vote-button.tsx +113 -0
  162. package/components/feedback/vote-list.tsx +108 -0
  163. package/components/integrations/github-config.tsx +200 -0
  164. package/components/landing/hero.tsx +150 -0
  165. package/components/layout/dashboard-layout.tsx +59 -0
  166. package/components/layout/index.ts +20 -0
  167. package/components/layout/language-switcher.tsx +129 -0
  168. package/components/layout/mobile-sidebar.tsx +66 -0
  169. package/components/layout/sidebar.tsx +279 -0
  170. package/components/portal/changelog-entry.tsx +132 -0
  171. package/components/portal/changelog-list.tsx +85 -0
  172. package/components/portal/contributor-badge.tsx +29 -0
  173. package/components/portal/contributors-sidebar.tsx +98 -0
  174. package/components/portal/create-post-dialog.tsx +247 -0
  175. package/components/portal/feedback-board.tsx +205 -0
  176. package/components/portal/feedback-post-card.tsx +198 -0
  177. package/components/portal/help-center.tsx +169 -0
  178. package/components/portal/leaderboard.tsx +29 -0
  179. package/components/portal/portal-header.tsx +153 -0
  180. package/components/portal/portal-layout.tsx +62 -0
  181. package/components/portal/portal-modules-panel.tsx +118 -0
  182. package/components/portal/portal-nav.tsx +59 -0
  183. package/components/portal/portal-overview.tsx +174 -0
  184. package/components/portal/portal-settings-nav.tsx +62 -0
  185. package/components/portal/portal-settings-shell.tsx +71 -0
  186. package/components/portal/portal-shell.tsx +62 -0
  187. package/components/portal/portal-tab-nav.tsx +77 -0
  188. package/components/portal/project-switcher.tsx +20 -0
  189. package/components/portal/roadmap-board.tsx +82 -0
  190. package/components/portal/roadmap-card.tsx +76 -0
  191. package/components/portal/roadmap-column.tsx +78 -0
  192. package/components/portal/settings-forms/access-form.tsx +194 -0
  193. package/components/portal/settings-forms/copy-form.tsx +95 -0
  194. package/components/portal/settings-forms/index.ts +23 -0
  195. package/components/portal/settings-forms/languages-form.tsx +223 -0
  196. package/components/portal/settings-forms/seo-form.tsx +156 -0
  197. package/components/portal/settings-forms/sharing-form.tsx +155 -0
  198. package/components/portal/settings-forms/theme-form.tsx +104 -0
  199. package/components/settings/api-keys-list.tsx +167 -0
  200. package/components/settings/appearance-form.tsx +71 -0
  201. package/components/settings/index.ts +25 -0
  202. package/components/settings/invite-member-form.tsx +119 -0
  203. package/components/settings/notification-preferences.tsx +174 -0
  204. package/components/settings/organization-form.tsx +165 -0
  205. package/components/settings/organization-members-list.tsx +197 -0
  206. package/components/settings/profile-form.tsx +124 -0
  207. package/components/settings/role-selector.tsx +57 -0
  208. package/components/settings/settings-sidebar.tsx +115 -0
  209. package/components/shared/pagination.tsx +215 -0
  210. package/components/ui/alert-dialog.tsx +201 -0
  211. package/components/ui/alert.tsx +75 -0
  212. package/components/ui/avatar.tsx +126 -0
  213. package/components/ui/badge.tsx +62 -0
  214. package/components/ui/button.tsx +77 -0
  215. package/components/ui/card.tsx +111 -0
  216. package/components/ui/combobox.tsx +311 -0
  217. package/components/ui/dialog.tsx +158 -0
  218. package/components/ui/dropdown-menu.tsx +272 -0
  219. package/components/ui/field.tsx +256 -0
  220. package/components/ui/input-group.tsx +164 -0
  221. package/components/ui/input.tsx +36 -0
  222. package/components/ui/label.tsx +41 -0
  223. package/components/ui/pagination.tsx +142 -0
  224. package/components/ui/select.tsx +202 -0
  225. package/components/ui/separator.tsx +45 -0
  226. package/components/ui/sheet.tsx +151 -0
  227. package/components/ui/skeleton.tsx +32 -0
  228. package/components/ui/switch.tsx +49 -0
  229. package/components/ui/table.tsx +118 -0
  230. package/components/ui/tabs.tsx +107 -0
  231. package/components/ui/textarea.tsx +35 -0
  232. package/components/ui/tooltip.tsx +78 -0
  233. package/components/widget/widget-form.tsx +439 -0
  234. package/components.json +24 -0
  235. package/db/init/01-init.sql +13 -0
  236. package/docker-compose.dev.yml +26 -0
  237. package/docker-compose.yml +98 -0
  238. package/docs/architecture.md +259 -0
  239. package/docs/component-inventory.md +261 -0
  240. package/docs/database-migrations.md +76 -0
  241. package/docs/development-guide.md +209 -0
  242. package/docs/e2e-user-flows.csv +31 -0
  243. package/docs/er-diagram-feedback.mmd +138 -0
  244. package/docs/er-diagram.mmd +281 -0
  245. package/docs/i18n-check-report.md +296 -0
  246. package/docs/index.md +214 -0
  247. package/docs/logic-chain.md +94 -0
  248. package/docs/plans/2026-01-02-database-migration-scripts.md +496 -0
  249. package/docs/plans/2026-01-02-user-login-design.md +37 -0
  250. package/docs/plans/2026-01-02-user-login.md +437 -0
  251. package/docs/plans/2026-01-02-user-registration-design.md +47 -0
  252. package/docs/plans/2026-01-02-user-registration.md +628 -0
  253. package/docs/plans/2026-01-03-roles-permissions-design.md +20 -0
  254. package/docs/plans/2026-01-03-roles-permissions.md +266 -0
  255. package/docs/plans/2026-01-05-authentication-middleware.md +207 -0
  256. package/docs/plans/2026-01-05-member-removal.md +186 -0
  257. package/docs/plans/2026-01-05-organization-creation.md +374 -0
  258. package/docs/plans/2026-01-05-rbac-middleware.md +112 -0
  259. package/docs/plans/2026-01-05-role-configuration.md +441 -0
  260. package/docs/plans/2026-01-06-file-upload-support.md +804 -0
  261. package/docs/plans/2026-01-06-permission-check-hook.md +155 -0
  262. package/docs/plans/2026-01-06-resource-ownership-check.md +231 -0
  263. package/docs/plans/2026-01-07-feedback-tracking-link.md +459 -0
  264. package/docs/plans/2026-01-09-logout-redirect-design.md +52 -0
  265. package/docs/plans/2026-01-09-phase2-3-plan.md +654 -0
  266. package/docs/plans/2026-01-09-portal-execution-plan.md +408 -0
  267. package/docs/plans/2026-01-09-project-delete-feature-design.md +163 -0
  268. package/docs/plans/2026-01-09-project-delete-implementation.md +451 -0
  269. package/docs/plans/2026-01-09-project-edit-delete-design.md +52 -0
  270. package/docs/plans/2026-01-09-settings-center-design.md +114 -0
  271. package/docs/plans/2026-01-09-settings-center.md +948 -0
  272. package/docs/plans/2026-01-10-organization-only-design.md +66 -0
  273. package/docs/plans/2026-01-10-organization-only-implementation.md +433 -0
  274. package/docs/plans/2026-01-10-portal-settings-restructure-plan.md +18 -0
  275. package/docs/plans/2026-01-10-project-settings-tabs-design-implementation.md +296 -0
  276. package/docs/plans/2026-01-14-e2e-playwright-feedback.md +173 -0
  277. package/docs/plans/2026-01-15-feedback-management-org-context-design.md +82 -0
  278. package/docs/plans/2026-01-15-feedback-management-org-context-implementation-plan.md +521 -0
  279. package/docs/plans/2026-01-16-admin-feedback-filters-design.md +75 -0
  280. package/docs/plans/2026-01-16-admin-feedback-filters-implementation.md +293 -0
  281. package/docs/plans/2026-01-16-admin-feedback-route-consolidation.md +180 -0
  282. package/docs/plans/2026-01-16-e2e-test-fixes.md +158 -0
  283. package/docs/plans/2026-01-17-admin-feedback-filters.md +214 -0
  284. package/docs/plans/2026-01-17-admin-feedback-improvements.md +453 -0
  285. package/docs/plans/2026-01-18-changesets-design.md +40 -0
  286. package/docs/product_changes.md +37 -0
  287. package/docs/project-overview.md +159 -0
  288. package/docs/project-scan-report.json +104 -0
  289. package/docs/route-role-visibility.md +51 -0
  290. package/docs/source-tree-analysis.md +150 -0
  291. package/docs/testing/delete-project-manual-tests.md +18 -0
  292. package/docs/user-story-tracking.md +191 -0
  293. package/drizzle.config.ts +32 -0
  294. package/eslint.config.mjs +19 -0
  295. package/hooks/use-permissions.ts +56 -0
  296. package/i18n/config.ts +45 -0
  297. package/i18n/request.ts +28 -0
  298. package/i18n/resolve-locale.ts +38 -0
  299. package/lib/api/errors.ts +62 -0
  300. package/lib/auth/cli-config.ts +35 -0
  301. package/lib/auth/client.ts +20 -0
  302. package/lib/auth/config.ts +55 -0
  303. package/lib/auth/jwt-identity.ts +21 -0
  304. package/lib/auth/org-context.ts +71 -0
  305. package/lib/auth/organization.ts +107 -0
  306. package/lib/auth/permissions.ts +87 -0
  307. package/lib/auth/session.ts +23 -0
  308. package/lib/config/rate-limits.ts +64 -0
  309. package/lib/dashboard/get-dashboard-stats.ts +136 -0
  310. package/lib/db/index.ts +41 -0
  311. package/lib/db/migrate.test.ts +49 -0
  312. package/lib/db/migrate.ts +62 -0
  313. package/lib/db/migrations/.gitkeep +0 -0
  314. package/lib/db/migrations/0000_cynical_gladiator.sql +53 -0
  315. package/lib/db/migrations/0001_wandering_sunfire.sql +27 -0
  316. package/lib/db/migrations/0002_shallow_speedball.sql +1 -0
  317. package/lib/db/migrations/0003_add_org_description.sql +1 -0
  318. package/lib/db/migrations/0003_boring_wild_pack.sql +13 -0
  319. package/lib/db/migrations/0004_windy_tyrannus.sql +27 -0
  320. package/lib/db/migrations/0005_perpetual_doorman.sql +5 -0
  321. package/lib/db/migrations/0006_aberrant_captain_midlands.sql +13 -0
  322. package/lib/db/migrations/0007_clever_captain_cross.sql +14 -0
  323. package/lib/db/migrations/0008_sparkling_pandemic.sql +2 -0
  324. package/lib/db/migrations/0009_happy_black_tom.sql +29 -0
  325. package/lib/db/migrations/0010_kind_junta.sql +8 -0
  326. package/lib/db/migrations/0011_mute_squadron_supreme.sql +25 -0
  327. package/lib/db/migrations/0012_giant_power_man.sql +24 -0
  328. package/lib/db/migrations/0013_damp_titanium_man.sql +17 -0
  329. package/lib/db/migrations/0014_blue_alice.sql +18 -0
  330. package/lib/db/migrations/0015_webhook_tables.sql +41 -0
  331. package/lib/db/migrations/0016_github_integration.sql +30 -0
  332. package/lib/db/migrations/0016_overjoyed_ghost_rider.sql +22 -0
  333. package/lib/db/migrations/0017_slimy_inhumans.sql +6 -0
  334. package/lib/db/migrations/0018_same_spitfire.sql +1 -0
  335. package/lib/db/migrations/0019_jittery_loners.sql +16 -0
  336. package/lib/db/migrations/0019_remove_projects_add_org_settings.sql +14 -0
  337. package/lib/db/migrations/meta/0000_snapshot.json +374 -0
  338. package/lib/db/migrations/meta/0001_snapshot.json +553 -0
  339. package/lib/db/migrations/meta/0002_snapshot.json +560 -0
  340. package/lib/db/migrations/meta/0003_snapshot.json +650 -0
  341. package/lib/db/migrations/meta/0004_snapshot.json +852 -0
  342. package/lib/db/migrations/meta/0005_snapshot.json +900 -0
  343. package/lib/db/migrations/meta/0006_snapshot.json +1011 -0
  344. package/lib/db/migrations/meta/0007_snapshot.json +1125 -0
  345. package/lib/db/migrations/meta/0008_snapshot.json +1146 -0
  346. package/lib/db/migrations/meta/0009_snapshot.json +1386 -0
  347. package/lib/db/migrations/meta/0010_snapshot.json +1419 -0
  348. package/lib/db/migrations/meta/0011_snapshot.json +1615 -0
  349. package/lib/db/migrations/meta/0012_snapshot.json +1805 -0
  350. package/lib/db/migrations/meta/0013_snapshot.json +1948 -0
  351. package/lib/db/migrations/meta/0014_snapshot.json +2082 -0
  352. package/lib/db/migrations/meta/0015_snapshot.json +2476 -0
  353. package/lib/db/migrations/meta/0016_snapshot.json +2633 -0
  354. package/lib/db/migrations/meta/0017_snapshot.json +2680 -0
  355. package/lib/db/migrations/meta/0018_snapshot.json +2686 -0
  356. package/lib/db/migrations/meta/0019_snapshot.json +2741 -0
  357. package/lib/db/migrations/meta/_journal.json +146 -0
  358. package/lib/db/schema/ai-processing.ts +90 -0
  359. package/lib/db/schema/api-keys.ts +61 -0
  360. package/lib/db/schema/attachments.ts +48 -0
  361. package/lib/db/schema/auth.ts +111 -0
  362. package/lib/db/schema/comments.ts +74 -0
  363. package/lib/db/schema/duplicates.ts +80 -0
  364. package/lib/db/schema/feedback.ts +88 -0
  365. package/lib/db/schema/github-integrations.ts +66 -0
  366. package/lib/db/schema/index.ts +35 -0
  367. package/lib/db/schema/invitations.ts +32 -0
  368. package/lib/db/schema/notifications.ts +85 -0
  369. package/lib/db/schema/organization-members.ts +37 -0
  370. package/lib/db/schema/organization-settings.ts +134 -0
  371. package/lib/db/schema/organizations.ts +30 -0
  372. package/lib/db/schema/projects.ts +145 -0
  373. package/lib/db/schema/status-history.ts +63 -0
  374. package/lib/db/schema/tags.ts +194 -0
  375. package/lib/db/schema/user-profiles.ts +31 -0
  376. package/lib/db/schema/votes.ts +60 -0
  377. package/lib/db/schema/webhooks.ts +106 -0
  378. package/lib/feedback/filters.ts +28 -0
  379. package/lib/feedback/find-similar.ts +49 -0
  380. package/lib/feedback/get-feedback-by-id.ts +159 -0
  381. package/lib/feedback/prefill.ts +51 -0
  382. package/lib/http/get-request-url.ts +28 -0
  383. package/lib/integrations/github.ts +159 -0
  384. package/lib/invitations.ts +22 -0
  385. package/lib/logger.test.ts +31 -0
  386. package/lib/logger.ts +58 -0
  387. package/lib/middleware/api-key.ts +126 -0
  388. package/lib/middleware/rate-limit-keys.ts +47 -0
  389. package/lib/middleware/rate-limit.ts +148 -0
  390. package/lib/middleware/rbac.ts +39 -0
  391. package/lib/middleware/request-id.test.ts +28 -0
  392. package/lib/middleware/request-id.ts +30 -0
  393. package/lib/middleware/request-logger.test.ts +36 -0
  394. package/lib/middleware/request-logger.ts +41 -0
  395. package/lib/middleware/with-rate-limit.ts +33 -0
  396. package/lib/portal/analytics.ts +20 -0
  397. package/lib/portal/contributors.ts +27 -0
  398. package/lib/portal/i18n.ts +20 -0
  399. package/lib/portal/leaderboard-settings.ts +20 -0
  400. package/lib/portal/modules.ts +20 -0
  401. package/lib/portal/portal-copy.ts +20 -0
  402. package/lib/portal/public-context.tsx +110 -0
  403. package/lib/portal/seo.ts +20 -0
  404. package/lib/portal/settings-context.ts +56 -0
  405. package/lib/portal/sharing.ts +20 -0
  406. package/lib/portal/sorting.ts +20 -0
  407. package/lib/portal/theme.ts +20 -0
  408. package/lib/services/ai/classifier.ts +296 -0
  409. package/lib/services/ai/duplicate-detector.ts +255 -0
  410. package/lib/services/ai/tag-suggester.ts +108 -0
  411. package/lib/services/api-keys.ts +164 -0
  412. package/lib/services/backup.ts +173 -0
  413. package/lib/services/email/templates.ts +158 -0
  414. package/lib/services/email.ts +68 -0
  415. package/lib/services/github-sync.ts +205 -0
  416. package/lib/services/notifications/index.ts +224 -0
  417. package/lib/services/portal-settings.ts +157 -0
  418. package/lib/swagger/config.ts +296 -0
  419. package/lib/swagger/generate.ts +400 -0
  420. package/lib/upload/file-validator.ts +52 -0
  421. package/lib/upload/storage.ts +59 -0
  422. package/lib/utils/format.ts +26 -0
  423. package/lib/utils/slug.ts +28 -0
  424. package/lib/utils.ts +23 -0
  425. package/lib/validations/auth.ts +56 -0
  426. package/lib/validations/comment.ts +44 -0
  427. package/lib/validations/feedback.ts +51 -0
  428. package/lib/validations/invitations.ts +23 -0
  429. package/lib/validations/organizations.ts +34 -0
  430. package/lib/validations/projects.ts +49 -0
  431. package/lib/validators/feedback.ts +57 -0
  432. package/lib/validators/index.ts +18 -0
  433. package/lib/webhooks/events.ts +73 -0
  434. package/lib/webhooks/index.ts +21 -0
  435. package/lib/webhooks/retry.ts +188 -0
  436. package/lib/webhooks/sender.ts +183 -0
  437. package/lib/webhooks/verify.ts +37 -0
  438. package/lib/workers/feedback-processor.ts +255 -0
  439. package/messages/en.json +965 -0
  440. package/messages/jp.json +862 -0
  441. package/messages/zh-CN.json +855 -0
  442. package/next-env.d.ts +6 -0
  443. package/next.config.ts +66 -0
  444. package/package.json +84 -0
  445. package/playwright.config.ts +44 -0
  446. package/postcss.config.mjs +7 -0
  447. package/proxy.test.ts +131 -0
  448. package/proxy.ts +190 -0
  449. package/public/file.svg +1 -0
  450. package/public/globe.svg +1 -0
  451. package/public/logo-64.svg +5 -0
  452. package/public/logo.svg +5 -0
  453. package/public/next.svg +1 -0
  454. package/public/openapi.json +673 -0
  455. package/public/uploads/.gitkeep +0 -0
  456. package/public/uploads/02695701-ded0-4c81-8a21-9326c1d65448.pdf +1 -0
  457. package/public/uploads/178843ea-2780-48ef-8988-f4cba442e4cb.pdf +1 -0
  458. package/public/uploads/24b0a9ef-da93-49da-934f-637f89c7871d.pdf +1 -0
  459. package/public/uploads/7a11626d-a8e4-4b91-a8eb-20b6213b0a5a.pdf +1 -0
  460. package/public/uploads/b0703f4d-6e7b-4aab-8191-1a7b15f1b8ee.pdf +1 -0
  461. package/public/uploads/c8de0aed-4d3a-44aa-83bb-6594b7a2ddb3.pdf +1 -0
  462. package/public/uploads/e4cce295-0d85-4525-a1b0-a61c45722e26.pdf +1 -0
  463. package/public/uploads/eb4df45e-563c-48b8-9c68-c18212312426.pdf +1 -0
  464. package/public/vercel.svg +1 -0
  465. package/public/widget/embed.js +249 -0
  466. package/public/window.svg +1 -0
  467. package/scripts/backup-db.sh +57 -0
  468. package/scripts/backup-db.ts +24 -0
  469. package/scripts/generate-openapi.ts +22 -0
  470. package/scripts/migration-helper.ts +39 -0
  471. package/scripts/pre-deploy.ts +75 -0
  472. package/scripts/restore-db.sh +60 -0
  473. package/scripts/rollback.ts +72 -0
  474. package/scripts/seed-tags.ts +48 -0
  475. package/tests/api/feedback-bulk.test.ts +47 -0
  476. package/tests/api/feedback-by-id.test.ts +67 -0
  477. package/tests/api/feedback-comments-route-import.test.ts +26 -0
  478. package/tests/api/feedback-create.test.ts +71 -0
  479. package/tests/api/feedback-delete.test.ts +160 -0
  480. package/tests/api/feedback-filter.test.ts +250 -0
  481. package/tests/api/feedback-list.test.ts +234 -0
  482. package/tests/api/feedback-route-assignee-condition.test.ts +32 -0
  483. package/tests/api/feedback-similar.test.ts +46 -0
  484. package/tests/api/feedback-sort.test.ts +261 -0
  485. package/tests/api/feedback-status-enum.test.ts +49 -0
  486. package/tests/api/feedback-status-filter.test.ts +117 -0
  487. package/tests/api/feedback-submit-on-behalf.test.ts +269 -0
  488. package/tests/api/feedback.test.ts +175 -0
  489. package/tests/api/identify-jwt.test.ts +25 -0
  490. package/tests/api/invitation-accept.test.ts +213 -0
  491. package/tests/api/organization-invitations.test.ts +186 -0
  492. package/tests/api/organization-members-list.test.ts +79 -0
  493. package/tests/api/organization-members.test.ts +340 -0
  494. package/tests/api/organizations.test.ts +149 -0
  495. package/tests/api/register.test.ts +112 -0
  496. package/tests/api/upload.test.ts +103 -0
  497. package/tests/api/vote.test.ts +82 -0
  498. package/tests/app/admin-feedback-detail-page.test.tsx +25 -0
  499. package/tests/app/admin-feedback-list-page.test.tsx +25 -0
  500. package/tests/app/admin-feedback-new-page.test.tsx +25 -0
  501. package/tests/app/health-route-helpers.test.ts +27 -0
  502. package/tests/app/login-page.test.ts +26 -0
  503. package/tests/app/portal-page.test.ts +29 -0
  504. package/tests/app/project-portal-overview.test.tsx +25 -0
  505. package/tests/app/widget-page-import.test.ts +25 -0
  506. package/tests/components/create-post-dialog-defaults.test.ts +43 -0
  507. package/tests/components/feedback/duplicate-suggestions-inline.test.tsx +27 -0
  508. package/tests/components/feedback/embedded-feedback-form.test.tsx +96 -0
  509. package/tests/components/feedback/feedback-detail.test.tsx +25 -0
  510. package/tests/components/feedback/feedback-stats.test.tsx +49 -0
  511. package/tests/components/feedback-bulk-actions.test.tsx +39 -0
  512. package/tests/components/feedback-i18n-keys.test.ts +70 -0
  513. package/tests/components/feedback-list-controls-compile.test.ts +25 -0
  514. package/tests/components/feedback-list-controls.test.tsx +204 -0
  515. package/tests/components/feedback-list-item.test.tsx +67 -0
  516. package/tests/components/landing/hero.test.tsx +46 -0
  517. package/tests/components/layout/language-switcher.test.tsx +25 -0
  518. package/tests/components/layout/sidebar.test.tsx +157 -0
  519. package/tests/components/login-form.test.ts +25 -0
  520. package/tests/components/organization-form.test.ts +32 -0
  521. package/tests/components/organization-switcher.test.ts +25 -0
  522. package/tests/components/pagination.test.tsx +43 -0
  523. package/tests/components/portal-overview.test.tsx +25 -0
  524. package/tests/components/profile-form.test.tsx +139 -0
  525. package/tests/components/role-selector.test.ts +31 -0
  526. package/tests/components/status-chart.test.tsx +90 -0
  527. package/tests/e2e/auth.e2e.ts +323 -0
  528. package/tests/e2e/feedback-actions.e2e.ts +471 -0
  529. package/tests/e2e/feedback-attachment.e2e.ts +168 -0
  530. package/tests/e2e/feedback-customer.e2e.ts +226 -0
  531. package/tests/e2e/feedback-management.e2e.ts +565 -0
  532. package/tests/e2e/feedback-submit.e2e.ts +133 -0
  533. package/tests/e2e/feedback-view.e2e.ts +297 -0
  534. package/tests/e2e/fixtures/test-data.ts +235 -0
  535. package/tests/e2e/health-check.e2e.ts +230 -0
  536. package/tests/e2e/helpers/test-utils-helpers.test.ts +43 -0
  537. package/tests/e2e/helpers/test-utils.ts +298 -0
  538. package/tests/e2e/integration-placeholders.e2e.ts +199 -0
  539. package/tests/e2e/organization.e2e.ts +292 -0
  540. package/tests/e2e/permissions.e2e.ts +424 -0
  541. package/tests/e2e/project-widget.e2e.ts +63 -0
  542. package/tests/feedback/filters.test.ts +29 -0
  543. package/tests/hooks/use-permissions.test.ts +52 -0
  544. package/tests/lib/ai/classifier.test.ts +104 -0
  545. package/tests/lib/ai/duplicate-detector.test.ts +234 -0
  546. package/tests/lib/attachments-schema.test.ts +30 -0
  547. package/tests/lib/auth/session.test.ts +49 -0
  548. package/tests/lib/auth-client.test.ts +37 -0
  549. package/tests/lib/auth-config.test.ts +26 -0
  550. package/tests/lib/feedback-prefill.test.ts +52 -0
  551. package/tests/lib/feedback-processor.test.ts +41 -0
  552. package/tests/lib/feedback-schema.test.ts +33 -0
  553. package/tests/lib/file-validator.test.ts +48 -0
  554. package/tests/lib/get-feedback-by-id.test.ts +37 -0
  555. package/tests/lib/invitations.test.ts +35 -0
  556. package/tests/lib/login-schema.test.ts +36 -0
  557. package/tests/lib/org-context.test.ts +95 -0
  558. package/tests/lib/organization-access.test.ts +44 -0
  559. package/tests/lib/organization-member-role-schema.test.ts +41 -0
  560. package/tests/lib/permissions.test.ts +88 -0
  561. package/tests/lib/portal-analytics.test.ts +25 -0
  562. package/tests/lib/portal-contributors.test.ts +25 -0
  563. package/tests/lib/portal-copy.test.ts +27 -0
  564. package/tests/lib/portal-i18n.test.ts +30 -0
  565. package/tests/lib/portal-leaderboard-settings.test.ts +25 -0
  566. package/tests/lib/portal-modules.test.ts +25 -0
  567. package/tests/lib/portal-seo.test.ts +25 -0
  568. package/tests/lib/portal-sharing.test.ts +25 -0
  569. package/tests/lib/portal-sorting.test.ts +25 -0
  570. package/tests/lib/portal-theme.test.ts +25 -0
  571. package/tests/lib/rate-limit.test.ts +142 -0
  572. package/tests/lib/resolve-locale.test.ts +34 -0
  573. package/tests/lib/services/backup.test.ts +145 -0
  574. package/tests/lib/user-organizations.test.ts +42 -0
  575. package/tests/lib/user-role-schema.test.ts +33 -0
  576. package/tests/lib/user-schema.test.ts +25 -0
  577. package/tests/setup.ts +74 -0
  578. package/tsconfig.json +34 -0
  579. package/types/bun-test.d.ts +31 -0
@@ -0,0 +1,293 @@
1
+ # Admin Feedback Filters & Sorting Implementation Plan
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
+
5
+ **Goal:** Add URL-driven filters/sorting/pagination to /admin/feedback, including assignee/hasVotes/hasReplies, plus members API with client caching.
6
+
7
+ **Architecture:** Server-side query parameters drive list data. A new members endpoint provides assignee candidates. Frontend controls update URL, triggering fetches. Data remains in existing feedback API.
8
+
9
+ **Tech Stack:** Next.js App Router, React 19, TypeScript, Tailwind v4, Bun, Drizzle ORM.
10
+
11
+ ---
12
+
13
+ ### Task 1: Add organization members list endpoint
14
+
15
+ **Files:**
16
+ - Create: `app/api/organizations/[orgId]/members/route.ts`
17
+ - Create: `app/api/organizations/[orgId]/members/handler.ts`
18
+ - Test: `tests/api/organization-members-list.test.ts`
19
+
20
+ **Step 1: Write the failing test**
21
+
22
+ ```ts
23
+ import { describe, expect, it, mock } from "bun:test";
24
+ import { NextRequest } from "next/server";
25
+
26
+ describe("GET /api/organizations/[orgId]/members", () => {
27
+ it("returns members with displayName", async () => {
28
+ const mockDb = {
29
+ select: mock(() => ({
30
+ from: mock(() => ({
31
+ innerJoin: mock(() => ({
32
+ where: mock(() => Promise.resolve([
33
+ { userId: "user_1", displayName: "Ada" },
34
+ ])),
35
+ })),
36
+ })),
37
+ })),
38
+ };
39
+
40
+ mock.module("@/lib/auth/config", () => ({
41
+ auth: { api: { getSession: mock(() => Promise.resolve({ user: { id: "user_1" } })) } },
42
+ }));
43
+
44
+ mock.module("@/lib/db", () => ({ db: mockDb }));
45
+
46
+ const { GET } = await import("@/app/api/organizations/[orgId]/members/route");
47
+ const req = new NextRequest("http://localhost:3000/api/organizations/org_1/members");
48
+ const res = await GET(req, { params: Promise.resolve({ orgId: "org_1" }) });
49
+
50
+ expect(res.status).toBe(200);
51
+ const json = await res.json();
52
+ expect(json.data[0]).toHaveProperty("userId");
53
+ expect(json.data[0]).toHaveProperty("displayName");
54
+ });
55
+ });
56
+ ```
57
+
58
+ **Step 2: Run test to verify it fails**
59
+
60
+ Run: `bun test tests/api/organization-members-list.test.ts`
61
+ Expected: FAIL (module not found or handler missing)
62
+
63
+ **Step 3: Write minimal implementation**
64
+
65
+ ```ts
66
+ // handler.ts
67
+ import { NextResponse } from "next/server";
68
+ import { assertOrganizationAccess } from "@/lib/auth/organization";
69
+ import { organizationMembers, user } from "@/lib/db/schema";
70
+ import { eq } from "drizzle-orm";
71
+
72
+ export function buildListMembersHandler({ auth, db }: { auth: any; db: any }) {
73
+ return async function GET(req: Request, context: { params: { orgId: string } | Promise<{ orgId: string }> }) {
74
+ const session = await auth.api.getSession({ headers: req.headers });
75
+ if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
76
+
77
+ const { orgId } = await Promise.resolve(context.params);
78
+ try {
79
+ await assertOrganizationAccess(db, session.user.id, orgId);
80
+ } catch {
81
+ return NextResponse.json({ error: "Forbidden" }, { status: 403 });
82
+ }
83
+
84
+ const rows = await db
85
+ .select({
86
+ userId: organizationMembers.userId,
87
+ displayName: user.name,
88
+ email: user.email,
89
+ })
90
+ .from(organizationMembers)
91
+ .innerJoin(user, eq(organizationMembers.userId, user.id))
92
+ .where(eq(organizationMembers.organizationId, orgId));
93
+
94
+ const data = rows.map((row: any) => ({
95
+ userId: row.userId,
96
+ displayName: row.displayName ?? row.email ?? "未知成员",
97
+ }));
98
+
99
+ return NextResponse.json({ data }, { status: 200 });
100
+ };
101
+ }
102
+ ```
103
+
104
+ **Step 4: Run test to verify it passes**
105
+
106
+ Run: `bun test tests/api/organization-members-list.test.ts`
107
+ Expected: PASS
108
+
109
+ **Step 5: Commit**
110
+
111
+ ```bash
112
+ git add app/api/organizations/[orgId]/members/route.ts app/api/organizations/[orgId]/members/handler.ts tests/api/organization-members-list.test.ts
113
+ git commit -m "feat: add organization members list endpoint"
114
+ ```
115
+
116
+ ---
117
+
118
+ ### Task 2: Add query param parsing/serialization helpers
119
+
120
+ **Files:**
121
+ - Create: `lib/feedback/filters.ts`
122
+ - Test: `tests/feedback/filters.test.ts`
123
+
124
+ **Step 1: Write the failing test**
125
+
126
+ ```ts
127
+ import { describe, expect, it } from "bun:test";
128
+ import { parseCsvParam, serializeCsvParam } from "@/lib/feedback/filters";
129
+
130
+ describe("filters helpers", () => {
131
+ it("parses csv into array", () => {
132
+ expect(parseCsvParam("a,b")).toEqual(["a", "b"]);
133
+ });
134
+
135
+ it("serializes array into csv", () => {
136
+ expect(serializeCsvParam(["a", "b"])).toBe("a,b");
137
+ });
138
+ });
139
+ ```
140
+
141
+ **Step 2: Run test to verify it fails**
142
+
143
+ Run: `bun test tests/feedback/filters.test.ts`
144
+ Expected: FAIL (module missing)
145
+
146
+ **Step 3: Write minimal implementation**
147
+
148
+ ```ts
149
+ export function parseCsvParam(value?: string | null) {
150
+ if (!value) return [];
151
+ return value
152
+ .split(",")
153
+ .map((item) => item.trim())
154
+ .filter(Boolean);
155
+ }
156
+
157
+ export function serializeCsvParam(values: string[]) {
158
+ return values.filter(Boolean).join(",");
159
+ }
160
+ ```
161
+
162
+ **Step 4: Run test to verify it passes**
163
+
164
+ Run: `bun test tests/feedback/filters.test.ts`
165
+ Expected: PASS
166
+
167
+ **Step 5: Commit**
168
+
169
+ ```bash
170
+ git add lib/feedback/filters.ts tests/feedback/filters.test.ts
171
+ git commit -m "chore: add feedback filter helpers"
172
+ ```
173
+
174
+ ---
175
+
176
+ ### Task 3: Add Feedback list controls UI (filters + sorting)
177
+
178
+ **Files:**
179
+ - Create: `components/feedback/feedback-list-controls.tsx`
180
+ - Modify: `components/feedback/feedback-list.tsx`
181
+ - Modify: `components/shared/pagination.tsx`
182
+
183
+ **Step 1: Write the failing test**
184
+
185
+ ```ts
186
+ import { describe, expect, it } from "bun:test";
187
+ import { render } from "@testing-library/react";
188
+ import { FeedbackListControls } from "@/components/feedback/feedback-list-controls";
189
+
190
+ describe("FeedbackListControls", () => {
191
+ it("renders filters and sort controls", () => {
192
+ const { getByLabelText } = render(
193
+ <FeedbackListControls
194
+ organizationId="org_1"
195
+ total={10}
196
+ pageSize={20}
197
+ onUpdate={() => {}}
198
+ />
199
+ );
200
+ expect(getByLabelText("搜索反馈")).toBeTruthy();
201
+ });
202
+ });
203
+ ```
204
+
205
+ **Step 2: Run test to verify it fails**
206
+
207
+ Run: `bun test tests/components/feedback-list-controls.test.tsx`
208
+ Expected: FAIL (component missing)
209
+
210
+ **Step 3: Write minimal implementation**
211
+
212
+ - Add controls for search, status/type/priority/assignee/hasVotes/hasReplies, and sort
213
+ - Read/write URL params via `useSearchParams` + `useRouter`
214
+ - Reset page to 1 on change
215
+ - Add cached assignee list loading
216
+ - Update `FeedbackList` to pass current params and render controls
217
+ - Add `aria-label` to pagination icon buttons and replace "..." with "…"
218
+
219
+ **Step 4: Run test to verify it passes**
220
+
221
+ Run: `bun test tests/components/feedback-list-controls.test.tsx`
222
+ Expected: PASS
223
+
224
+ **Step 5: Commit**
225
+
226
+ ```bash
227
+ git add components/feedback/feedback-list-controls.tsx components/feedback/feedback-list.tsx components/shared/pagination.tsx
228
+ git commit -m "feat: add feedback list controls"
229
+ ```
230
+
231
+ ---
232
+
233
+ ### Task 4: Extend /api/feedback filtering for new params
234
+
235
+ **Files:**
236
+ - Modify: `app/api/feedback/route.ts`
237
+ - Modify: `app/api/feedback/handler.ts`
238
+ - Test: `tests/api/feedback-filter.test.ts`
239
+
240
+ **Step 1: Write the failing test**
241
+
242
+ ```ts
243
+ it("filters by assignee and hasVotes", async () => {
244
+ const req = new NextRequest("http://localhost:3000/api/feedback?assignee=user_1&hasVotes=true&organizationId=org_1");
245
+ const res = await GET(req);
246
+ expect(res.status).toBe(200);
247
+ });
248
+ ```
249
+
250
+ **Step 2: Run test to verify it fails**
251
+
252
+ Run: `bun test tests/api/feedback-filter.test.ts`
253
+ Expected: FAIL (missing behavior)
254
+
255
+ **Step 3: Write minimal implementation**
256
+
257
+ - Parse CSV params for `status/type/priority/assignee/hasVotes/hasReplies`
258
+ - Apply `assignee` filter (includes `unassigned`)
259
+ - Apply `hasVotes` filter (`voteCount > 0` or `= 0`)
260
+ - Apply `hasReplies` filter (`replyCount > 0` or `= 0`)
261
+
262
+ **Step 4: Run test to verify it passes**
263
+
264
+ Run: `bun test tests/api/feedback-filter.test.ts`
265
+ Expected: PASS
266
+
267
+ **Step 5: Commit**
268
+
269
+ ```bash
270
+ git add app/api/feedback/route.ts app/api/feedback/handler.ts tests/api/feedback-filter.test.ts
271
+ git commit -m "feat: add feedback filters for assignee and replies"
272
+ ```
273
+
274
+ ---
275
+
276
+ ### Task 5: Full verification
277
+
278
+ **Step 1: Run lint**
279
+
280
+ Run: `bun run lint`
281
+ Expected: 0 errors
282
+
283
+ **Step 2: Run relevant tests**
284
+
285
+ Run: `bun test tests/api/organization-members-list.test.ts tests/feedback/filters.test.ts tests/components/feedback-list-controls.test.tsx tests/api/feedback-filter.test.ts`
286
+ Expected: PASS
287
+
288
+ **Step 3: Commit any remaining fixes**
289
+
290
+ ```bash
291
+ git add -A
292
+ git commit -m "chore: finalize feedback filters"
293
+ ```
@@ -0,0 +1,180 @@
1
+ # Admin Feedback Route Consolidation Implementation Plan
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
+
5
+ **Goal:** Move all feedback management routes under `/admin`, removing legacy `/feedback` and `/feedback/new` dashboard paths.
6
+
7
+ **Architecture:** Keep admin list and detail at `/admin/feedback` and `/admin/feedback/[id]`, add `/admin/feedback/new` by moving the submit-on-behalf page, and delete the old dashboard feedback list. Update all internal links and list defaults to use the `/admin` base. No redirects.
8
+
9
+ **Tech Stack:** Next.js App Router, React, TypeScript, Tailwind CSS, Bun.
10
+
11
+ ### Task 1: Remove legacy dashboard feedback list route
12
+
13
+ **Files:**
14
+ - Delete: `app/(dashboard)/feedback/page.tsx`
15
+
16
+ **Step 1: Write the failing test**
17
+
18
+ ```ts
19
+ // tests/e2e/feedback-management.spec.ts (add a case asserting /feedback is 404)
20
+ // Example (Playwright):
21
+ // await page.goto("/feedback");
22
+ // await expect(page.getByText("Not Found")).toBeVisible();
23
+ ```
24
+
25
+ **Step 2: Run test to verify it fails**
26
+
27
+ Run: `bun run test tests/e2e/feedback-management.spec.ts`
28
+ Expected: FAIL because `/feedback` still exists
29
+
30
+ **Step 3: Remove the legacy route**
31
+
32
+ ```ts
33
+ // Delete file: app/(dashboard)/feedback/page.tsx
34
+ ```
35
+
36
+ **Step 4: Run test to verify it passes**
37
+
38
+ Run: `bun run test tests/e2e/feedback-management.spec.ts`
39
+ Expected: PASS
40
+
41
+ **Step 5: Commit**
42
+
43
+ ```bash
44
+ git add app/(dashboard)/feedback/page.tsx tests/e2e/feedback-management.spec.ts
45
+ git commit -m "refactor: remove legacy feedback list route"
46
+ ```
47
+
48
+ ### Task 2: Move submit-on-behalf route under /admin
49
+
50
+ **Files:**
51
+ - Move: `app/(dashboard)/feedback/new/page.tsx` → `app/(dashboard)/admin/feedback/new/page.tsx`
52
+
53
+ **Step 1: Write the failing test**
54
+
55
+ ```ts
56
+ // tests/e2e/feedback-customer.spec.ts (update /feedback/new -> /admin/feedback/new and assert old path 404)
57
+ ```
58
+
59
+ **Step 2: Run test to verify it fails**
60
+
61
+ Run: `bun run test tests/e2e/feedback-customer.spec.ts`
62
+ Expected: FAIL with old URL
63
+
64
+ **Step 3: Move the route file**
65
+
66
+ ```bash
67
+ mkdir -p app/(dashboard)/admin/feedback/new
68
+ mv app/(dashboard)/feedback/new/page.tsx app/(dashboard)/admin/feedback/new/page.tsx
69
+ ```
70
+
71
+ **Step 4: Run test to verify it passes**
72
+
73
+ Run: `bun run test tests/e2e/feedback-customer.spec.ts`
74
+ Expected: PASS
75
+
76
+ **Step 5: Commit**
77
+
78
+ ```bash
79
+ git add app/(dashboard)/admin/feedback/new/page.tsx app/(dashboard)/feedback/new/page.tsx tests/e2e/feedback-customer.spec.ts
80
+ git commit -m "refactor: move submit-on-behalf route under admin"
81
+ ```
82
+
83
+ ### Task 3: Update navigation links to /admin
84
+
85
+ **Files:**
86
+ - Modify: `components/layout/sidebar.tsx`
87
+ - Modify: `components/dashboard/quick-actions.tsx`
88
+ - Modify: `components/dashboard/recent-feedback-list.tsx`
89
+
90
+ **Step 1: Write the failing test**
91
+
92
+ ```ts
93
+ // tests/e2e/feedback-management.spec.ts or a unit test
94
+ // Assert sidebar/quick-actions links resolve to /admin/feedback and /admin/feedback/new
95
+ ```
96
+
97
+ **Step 2: Run test to verify it fails**
98
+
99
+ Run: `bun run test tests/e2e/feedback-management.spec.ts`
100
+ Expected: FAIL (links still /feedback)
101
+
102
+ **Step 3: Update links**
103
+
104
+ ```ts
105
+ // sidebar: /feedback -> /admin/feedback
106
+ // quick-actions: /feedback -> /admin/feedback
107
+ // recent-feedback-list: /feedback -> /admin/feedback, /feedback/new -> /admin/feedback/new
108
+ ```
109
+
110
+ **Step 4: Run test to verify it passes**
111
+
112
+ Run: `bun run test tests/e2e/feedback-management.spec.ts`
113
+ Expected: PASS
114
+
115
+ **Step 5: Commit**
116
+
117
+ ```bash
118
+ git add components/layout/sidebar.tsx components/dashboard/quick-actions.tsx components/dashboard/recent-feedback-list.tsx tests/e2e/feedback-management.spec.ts
119
+ git commit -m "refactor: point feedback navigation to admin routes"
120
+ ```
121
+
122
+ ### Task 4: Default feedback list base path to /admin
123
+
124
+ **Files:**
125
+ - Modify: `components/feedback/feedback-list.tsx`
126
+ - Modify: `components/feedback/feedback-list-item.tsx`
127
+
128
+ **Step 1: Write the failing test**
129
+
130
+ ```ts
131
+ // tests/components/feedback/feedback-list.test.tsx (new)
132
+ // Render FeedbackList without basePath and assert item links are /admin/feedback/:id
133
+ ```
134
+
135
+ **Step 2: Run test to verify it fails**
136
+
137
+ Run: `bun run test tests/components/feedback/feedback-list.test.tsx`
138
+ Expected: FAIL because default basePath is /feedback
139
+
140
+ **Step 3: Update defaults**
141
+
142
+ ```ts
143
+ // basePath default: "/feedback" -> "/admin/feedback"
144
+ ```
145
+
146
+ **Step 4: Run test to verify it passes**
147
+
148
+ Run: `bun run test tests/components/feedback/feedback-list.test.tsx`
149
+ Expected: PASS
150
+
151
+ **Step 5: Commit**
152
+
153
+ ```bash
154
+ git add components/feedback/feedback-list.tsx components/feedback/feedback-list-item.tsx tests/components/feedback/feedback-list.test.tsx
155
+ git commit -m "refactor: default feedback list links to admin"
156
+ ```
157
+
158
+ ### Task 5: Verification
159
+
160
+ **Files:**
161
+ - (No new files)
162
+
163
+ **Step 1: Run lint**
164
+
165
+ Run: `bun run lint`
166
+ Expected: Warnings only; no new errors
167
+
168
+ **Step 2: Smoke-check routes**
169
+
170
+ - `/admin/feedback` loads list
171
+ - `/admin/feedback/[id]` loads detail
172
+ - `/admin/feedback/new` loads submit-on-behalf form
173
+ - `/feedback` and `/feedback/new` 404
174
+
175
+ **Step 3: Commit (if needed)**
176
+
177
+ ```bash
178
+ git add -A
179
+ git commit -m "chore: verify admin feedback routes"
180
+ ```
@@ -0,0 +1,158 @@
1
+ # E2E Test Fixes Implementation Plan
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
+
5
+ **Goal:** Run the requested Playwright E2E specs and fix any failures until they pass.
6
+
7
+ **Architecture:** Use the existing Playwright specs as the failing tests, trace failures to the responsible UI/API layer, and apply minimal, targeted fixes. Re-run each spec after each fix to validate.
8
+
9
+ **Tech Stack:** Next.js (App Router), React, TypeScript, Bun, Playwright.
10
+
11
+ ---
12
+
13
+ ### Task 1: Establish execution workspace
14
+
15
+ **Files:**
16
+ - Modify: `.gitignore` (only if `.worktrees/` is not ignored)
17
+
18
+ **Step 1: Check preferred worktree directory**
19
+ Run: `ls -d .worktrees 2>/dev/null || ls -d worktrees 2>/dev/null`
20
+ Expected: one of the directories exists; otherwise proceed to CLAUDE.md preference.
21
+
22
+ **Step 2: Check CLAUDE.md for worktree guidance**
23
+ Run: `grep -i "worktree.*director" CLAUDE.md 2>/dev/null`
24
+ Expected: guidance or no output.
25
+
26
+ **Step 3: Verify ignore if using project-local directory**
27
+ Run: `git check-ignore -q .worktrees 2>/dev/null || git check-ignore -q worktrees 2>/dev/null`
28
+ Expected: zero exit code; if not ignored, add and commit `.gitignore` entry.
29
+
30
+ **Step 4: Create worktree and install deps**
31
+ Run:
32
+ ```
33
+ project=$(basename "$(git rev-parse --show-toplevel)")
34
+ branch="fix/e2e-tests-2026-01-16"
35
+ path=".worktrees/$branch"
36
+
37
+ git worktree add "$path" -b "$branch"
38
+ cd "$path"
39
+ bun install
40
+ ```
41
+ Expected: worktree created and dependencies installed.
42
+
43
+ **Step 5: Baseline test command (single spec)**
44
+ Run: `bunx playwright test tests/e2e/health-check.spec.ts`
45
+ Expected: If failing, stop and report; if passing, continue.
46
+
47
+ ---
48
+
49
+ ### Task 2: Fix `tests/e2e/feedback-management.spec.ts`
50
+
51
+ **Files:**
52
+ - Modify: `tests/e2e/feedback-management.spec.ts`
53
+ - Modify: `app/**` (as indicated by failure)
54
+
55
+ **Step 1: Run spec to capture failure**
56
+ Run: `bunx playwright test tests/e2e/feedback-management.spec.ts`
57
+ Expected: FAIL with specific error output.
58
+
59
+ **Step 2: Root cause investigation**
60
+ - Identify failing assertion, locator, or API response in the error output.
61
+ - Trace to the source file indicated by stack/trace.
62
+
63
+ **Step 3: Minimal fix**
64
+ - Update the failing locator/expectation to match current UI OR fix the underlying UI/API issue.
65
+ - Keep scope minimal; avoid refactors.
66
+
67
+ **Step 4: Verify**
68
+ Run: `bunx playwright test tests/e2e/feedback-management.spec.ts`
69
+ Expected: PASS.
70
+
71
+ ---
72
+
73
+ ### Task 3: Fix `tests/e2e/feedback-submit.spec.ts`
74
+
75
+ **Files:**
76
+ - Modify: `tests/e2e/feedback-submit.spec.ts`
77
+ - Modify: `app/**` (as indicated by failure)
78
+
79
+ **Step 1: Run spec to capture failure**
80
+ Run: `bunx playwright test tests/e2e/feedback-submit.spec.ts`
81
+ Expected: FAIL with specific error output.
82
+
83
+ **Step 2: Root cause investigation**
84
+ - Identify failing step, response, or UI mismatch.
85
+ - Trace to responsible route/component.
86
+
87
+ **Step 3: Minimal fix**
88
+ - Adjust test expectations or fix underlying behavior.
89
+
90
+ **Step 4: Verify**
91
+ Run: `bunx playwright test tests/e2e/feedback-submit.spec.ts`
92
+ Expected: PASS.
93
+
94
+ ---
95
+
96
+ ### Task 4: Fix `tests/e2e/feedback-view.spec.ts`
97
+
98
+ **Files:**
99
+ - Modify: `tests/e2e/feedback-view.spec.ts`
100
+ - Modify: `app/**` (as indicated by failure)
101
+
102
+ **Step 1: Run spec to capture failure**
103
+ Run: `bunx playwright test tests/e2e/feedback-view.spec.ts`
104
+ Expected: FAIL with specific error output.
105
+
106
+ **Step 2: Root cause investigation**
107
+ - Identify failing expectations and any missing data/fixtures.
108
+ - Trace to page/data-loading code.
109
+
110
+ **Step 3: Minimal fix**
111
+ - Adjust test to current UI or fix underlying page/data.
112
+
113
+ **Step 4: Verify**
114
+ Run: `bunx playwright test tests/e2e/feedback-view.spec.ts`
115
+ Expected: PASS.
116
+
117
+ ---
118
+
119
+ ### Task 5: Fix `tests/e2e/health-check.spec.ts`
120
+
121
+ **Files:**
122
+ - Modify: `app/health/route.ts`
123
+ - Modify: `tests/e2e/health-check.spec.ts` (only if test is outdated)
124
+
125
+ **Step 1: Run spec to capture failure**
126
+ Run: `bunx playwright test tests/e2e/health-check.spec.ts`
127
+ Expected: FAIL with specific error output.
128
+
129
+ **Step 2: Root cause investigation**
130
+ - Verify health endpoint route exists and matches test expectation.
131
+ - Validate response shape and status.
132
+
133
+ **Step 3: Minimal fix**
134
+ - Adjust endpoint or test to align expected contract.
135
+
136
+ **Step 4: Verify**
137
+ Run: `bunx playwright test tests/e2e/health-check.spec.ts`
138
+ Expected: PASS.
139
+
140
+ ---
141
+
142
+ ### Task 6: Final verification (requested specs)
143
+
144
+ **Files:**
145
+ - None (test run)
146
+
147
+ **Step 1: Run requested specs in order**
148
+ Run:
149
+ ```
150
+ bunx playwright test tests/e2e/feedback-management.spec.ts
151
+ bunx playwright test tests/e2e/feedback-submit.spec.ts
152
+ bunx playwright test tests/e2e/feedback-view.spec.ts
153
+ bunx playwright test tests/e2e/health-check.spec.ts
154
+ ```
155
+ Expected: All PASS.
156
+
157
+ **Step 2: Report results**
158
+ Summarize changes, test outcomes, and any follow-ups.