@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.
- package/.changeset/README.md +21 -0
- package/.changeset/config.json +11 -0
- package/.changeset/cozy-ghosts-care.md +5 -0
- package/.changeset/sharp-lines-stand.md +5 -0
- package/.changeset/sour-doodles-eat.md +5 -0
- package/.changeset/tender-moose-shop.md +5 -0
- package/.github/pull_request_template.md +13 -0
- package/.github/workflows/ci.yml +41 -0
- package/.github/workflows/publish.yml +44 -0
- package/.github/workflows/release.yml +73 -0
- package/AGENTS.md +92 -0
- package/CHANGELOG.md +13 -0
- package/Dockerfile +57 -0
- package/LICENSE +661 -0
- package/Makefile +77 -0
- package/README.md +198 -0
- package/app/(auth)/login/page.tsx +53 -0
- package/app/(auth)/register/page.tsx +48 -0
- package/app/(auth)/sign-in/page.tsx +22 -0
- package/app/(dashboard)/admin/feedback/[id]/edit/page.tsx +103 -0
- package/app/(dashboard)/admin/feedback/[id]/page.tsx +154 -0
- package/app/(dashboard)/admin/feedback/new/page.tsx +91 -0
- package/app/(dashboard)/admin/feedback/page.tsx +81 -0
- package/app/(dashboard)/admin/layout.tsx +48 -0
- package/app/(dashboard)/analytics/portal/page.tsx +30 -0
- package/app/(dashboard)/dashboard/page.tsx +133 -0
- package/app/(dashboard)/layout.tsx +69 -0
- package/app/(dashboard)/no-access/page.tsx +45 -0
- package/app/(dashboard)/settings/access/page.tsx +56 -0
- package/app/(dashboard)/settings/api-keys/page.tsx +55 -0
- package/app/(dashboard)/settings/appearance/page.tsx +40 -0
- package/app/(dashboard)/settings/branding/page.tsx +62 -0
- package/app/(dashboard)/settings/changelog/page.tsx +51 -0
- package/app/(dashboard)/settings/danger-zone/page.tsx +92 -0
- package/app/(dashboard)/settings/feedback/page.tsx +63 -0
- package/app/(dashboard)/settings/integrations/page.tsx +94 -0
- package/app/(dashboard)/settings/layout.tsx +43 -0
- package/app/(dashboard)/settings/modules/page.tsx +54 -0
- package/app/(dashboard)/settings/notifications/page.tsx +48 -0
- package/app/(dashboard)/settings/organization/page.tsx +104 -0
- package/app/(dashboard)/settings/organization/portal/access/page.tsx +22 -0
- package/app/(dashboard)/settings/organization/portal/experience/page.tsx +22 -0
- package/app/(dashboard)/settings/organization/portal/growth/page.tsx +22 -0
- package/app/(dashboard)/settings/organization/portal/layout.tsx +24 -0
- package/app/(dashboard)/settings/organization/portal/page.tsx +22 -0
- package/app/(dashboard)/settings/organizations/[orgId]/members/page.tsx +69 -0
- package/app/(dashboard)/settings/organizations/new/page.tsx +36 -0
- package/app/(dashboard)/settings/page.tsx +22 -0
- package/app/(dashboard)/settings/portal-access/page.tsx +53 -0
- package/app/(dashboard)/settings/portal-branding/page.tsx +59 -0
- package/app/(dashboard)/settings/portal-growth/page.tsx +57 -0
- package/app/(dashboard)/settings/portal-modules/page.tsx +49 -0
- package/app/(dashboard)/settings/portal-resources/page.tsx +66 -0
- package/app/(dashboard)/settings/profile/page.tsx +48 -0
- package/app/(dashboard)/settings/widgets/page.tsx +63 -0
- package/app/(public)/[organizationSlug]/changelog/page.tsx +109 -0
- package/app/(public)/[organizationSlug]/feedback/[id]/page.tsx +146 -0
- package/app/(public)/[organizationSlug]/page.tsx +160 -0
- package/app/(public)/[organizationSlug]/roadmap/page.tsx +142 -0
- package/app/(public)/docs/page.tsx +48 -0
- package/app/(public)/feedback/[id]/not-found.tsx +33 -0
- package/app/(public)/feedback/[id]/page.tsx +102 -0
- package/app/(public)/invite/[token]/page.tsx +121 -0
- package/app/(public)/page.tsx +22 -0
- package/app/(public)/widget/[organizationId]/page.tsx +122 -0
- package/app/api/_utils.ts +29 -0
- package/app/api/admin/backup/route.ts +72 -0
- package/app/api/api-keys/[keyId]/route.ts +92 -0
- package/app/api/api-keys/route.ts +116 -0
- package/app/api/auth/[...all]/route.ts +21 -0
- package/app/api/auth/clear-session/route.ts +43 -0
- package/app/api/auth/register/handler.ts +176 -0
- package/app/api/auth/register/route.ts +26 -0
- package/app/api/docs/route.ts +28 -0
- package/app/api/feedback/[id]/comments/[commentId]/route.ts +105 -0
- package/app/api/feedback/[id]/comments/route.ts +421 -0
- package/app/api/feedback/[id]/duplicates/route.ts +285 -0
- package/app/api/feedback/[id]/handler.ts +91 -0
- package/app/api/feedback/[id]/processing-status/route.ts +199 -0
- package/app/api/feedback/[id]/reclassify/route.ts +145 -0
- package/app/api/feedback/[id]/route.ts +511 -0
- package/app/api/feedback/[id]/suggest-tags/route.ts +227 -0
- package/app/api/feedback/[id]/sync-github/route.ts +52 -0
- package/app/api/feedback/[id]/vote/route.ts +431 -0
- package/app/api/feedback/bulk/route.ts +212 -0
- package/app/api/feedback/handler.ts +138 -0
- package/app/api/feedback/route.ts +298 -0
- package/app/api/feedback/similar/route.ts +100 -0
- package/app/api/health/route.test.ts +64 -0
- package/app/api/health/route.ts +92 -0
- package/app/api/identify/jwt/route.ts +29 -0
- package/app/api/integrations/github/route.ts +196 -0
- package/app/api/internal/domain-lookup/route.ts +67 -0
- package/app/api/invitations/accept/handler.ts +101 -0
- package/app/api/invitations/accept/route.ts +29 -0
- package/app/api/notifications/preferences/route.ts +109 -0
- package/app/api/organizations/[orgId]/handler.ts +123 -0
- package/app/api/organizations/[orgId]/invitations/handler.ts +121 -0
- package/app/api/organizations/[orgId]/invitations/route.ts +29 -0
- package/app/api/organizations/[orgId]/members/[memberId]/handler.ts +208 -0
- package/app/api/organizations/[orgId]/members/[memberId]/route.ts +30 -0
- package/app/api/organizations/[orgId]/members/handler.ts +77 -0
- package/app/api/organizations/[orgId]/members/route.ts +29 -0
- package/app/api/organizations/[orgId]/route.ts +30 -0
- package/app/api/organizations/handler.ts +97 -0
- package/app/api/organizations/route.ts +29 -0
- package/app/api/tags/sync/route.ts +88 -0
- package/app/api/upload/handler.ts +79 -0
- package/app/api/upload/route.ts +37 -0
- package/app/api/v1/feedback/[id]/route.ts +276 -0
- package/app/api/v1/feedback/route.ts +250 -0
- package/app/api/v1/spec/route.ts +356 -0
- package/app/api/webhooks/[webhookId]/route.ts +213 -0
- package/app/api/webhooks/github/route.ts +158 -0
- package/app/api/webhooks/route.ts +143 -0
- package/app/favicon.ico +0 -0
- package/app/globals.css +139 -0
- package/app/health/route.ts +108 -0
- package/app/layout.tsx +60 -0
- package/bun.lock +2503 -0
- package/components/api/rate-limit-info.tsx +86 -0
- package/components/api-keys/api-key-manager.tsx +262 -0
- package/components/auth/login-form.tsx +207 -0
- package/components/auth/register-form.tsx +230 -0
- package/components/comment/comment-form.tsx +111 -0
- package/components/comment/internal-notes.tsx +219 -0
- package/components/comment/public-comments.tsx +387 -0
- package/components/component-example-client-only.tsx +29 -0
- package/components/component-example.tsx +519 -0
- package/components/dashboard/index.ts +22 -0
- package/components/dashboard/organization-switcher.tsx +96 -0
- package/components/dashboard/quick-actions.tsx +57 -0
- package/components/dashboard/recent-feedback-list.tsx +152 -0
- package/components/dashboard/stats-cards.tsx +88 -0
- package/components/dashboard/status-chart.tsx +106 -0
- package/components/example.tsx +70 -0
- package/components/feedback/attachment-list.tsx +103 -0
- package/components/feedback/auto-classification-badge.tsx +92 -0
- package/components/feedback/classification-override.tsx +64 -0
- package/components/feedback/duplicate-suggestions-inline.tsx +158 -0
- package/components/feedback/duplicate-suggestions.tsx +188 -0
- package/components/feedback/embedded-feedback-form.tsx +439 -0
- package/components/feedback/feedback-actions.tsx +160 -0
- package/components/feedback/feedback-bulk-actions.tsx +184 -0
- package/components/feedback/feedback-detail-view.tsx +321 -0
- package/components/feedback/feedback-detail.tsx +305 -0
- package/components/feedback/feedback-edit-form.tsx +131 -0
- package/components/feedback/feedback-filters.tsx +222 -0
- package/components/feedback/feedback-list-controls.tsx +433 -0
- package/components/feedback/feedback-list-item.tsx +298 -0
- package/components/feedback/feedback-list-skeleton.tsx +49 -0
- package/components/feedback/feedback-list.tsx +523 -0
- package/components/feedback/feedback-sorter.tsx +117 -0
- package/components/feedback/feedback-stats.tsx +124 -0
- package/components/feedback/file-upload.tsx +289 -0
- package/components/feedback/processing-status.tsx +161 -0
- package/components/feedback/status-history.tsx +134 -0
- package/components/feedback/status-selector.tsx +153 -0
- package/components/feedback/submit-on-behalf-form.tsx +403 -0
- package/components/feedback/tag-suggestions.tsx +212 -0
- package/components/feedback/vote-button.tsx +113 -0
- package/components/feedback/vote-list.tsx +108 -0
- package/components/integrations/github-config.tsx +200 -0
- package/components/landing/hero.tsx +150 -0
- package/components/layout/dashboard-layout.tsx +59 -0
- package/components/layout/index.ts +20 -0
- package/components/layout/language-switcher.tsx +129 -0
- package/components/layout/mobile-sidebar.tsx +66 -0
- package/components/layout/sidebar.tsx +279 -0
- package/components/portal/changelog-entry.tsx +132 -0
- package/components/portal/changelog-list.tsx +85 -0
- package/components/portal/contributor-badge.tsx +29 -0
- package/components/portal/contributors-sidebar.tsx +98 -0
- package/components/portal/create-post-dialog.tsx +247 -0
- package/components/portal/feedback-board.tsx +205 -0
- package/components/portal/feedback-post-card.tsx +198 -0
- package/components/portal/help-center.tsx +169 -0
- package/components/portal/leaderboard.tsx +29 -0
- package/components/portal/portal-header.tsx +153 -0
- package/components/portal/portal-layout.tsx +62 -0
- package/components/portal/portal-modules-panel.tsx +118 -0
- package/components/portal/portal-nav.tsx +59 -0
- package/components/portal/portal-overview.tsx +174 -0
- package/components/portal/portal-settings-nav.tsx +62 -0
- package/components/portal/portal-settings-shell.tsx +71 -0
- package/components/portal/portal-shell.tsx +62 -0
- package/components/portal/portal-tab-nav.tsx +77 -0
- package/components/portal/project-switcher.tsx +20 -0
- package/components/portal/roadmap-board.tsx +82 -0
- package/components/portal/roadmap-card.tsx +76 -0
- package/components/portal/roadmap-column.tsx +78 -0
- package/components/portal/settings-forms/access-form.tsx +194 -0
- package/components/portal/settings-forms/copy-form.tsx +95 -0
- package/components/portal/settings-forms/index.ts +23 -0
- package/components/portal/settings-forms/languages-form.tsx +223 -0
- package/components/portal/settings-forms/seo-form.tsx +156 -0
- package/components/portal/settings-forms/sharing-form.tsx +155 -0
- package/components/portal/settings-forms/theme-form.tsx +104 -0
- package/components/settings/api-keys-list.tsx +167 -0
- package/components/settings/appearance-form.tsx +71 -0
- package/components/settings/index.ts +25 -0
- package/components/settings/invite-member-form.tsx +119 -0
- package/components/settings/notification-preferences.tsx +174 -0
- package/components/settings/organization-form.tsx +165 -0
- package/components/settings/organization-members-list.tsx +197 -0
- package/components/settings/profile-form.tsx +124 -0
- package/components/settings/role-selector.tsx +57 -0
- package/components/settings/settings-sidebar.tsx +115 -0
- package/components/shared/pagination.tsx +215 -0
- package/components/ui/alert-dialog.tsx +201 -0
- package/components/ui/alert.tsx +75 -0
- package/components/ui/avatar.tsx +126 -0
- package/components/ui/badge.tsx +62 -0
- package/components/ui/button.tsx +77 -0
- package/components/ui/card.tsx +111 -0
- package/components/ui/combobox.tsx +311 -0
- package/components/ui/dialog.tsx +158 -0
- package/components/ui/dropdown-menu.tsx +272 -0
- package/components/ui/field.tsx +256 -0
- package/components/ui/input-group.tsx +164 -0
- package/components/ui/input.tsx +36 -0
- package/components/ui/label.tsx +41 -0
- package/components/ui/pagination.tsx +142 -0
- package/components/ui/select.tsx +202 -0
- package/components/ui/separator.tsx +45 -0
- package/components/ui/sheet.tsx +151 -0
- package/components/ui/skeleton.tsx +32 -0
- package/components/ui/switch.tsx +49 -0
- package/components/ui/table.tsx +118 -0
- package/components/ui/tabs.tsx +107 -0
- package/components/ui/textarea.tsx +35 -0
- package/components/ui/tooltip.tsx +78 -0
- package/components/widget/widget-form.tsx +439 -0
- package/components.json +24 -0
- package/db/init/01-init.sql +13 -0
- package/docker-compose.dev.yml +26 -0
- package/docker-compose.yml +98 -0
- package/docs/architecture.md +259 -0
- package/docs/component-inventory.md +261 -0
- package/docs/database-migrations.md +76 -0
- package/docs/development-guide.md +209 -0
- package/docs/e2e-user-flows.csv +31 -0
- package/docs/er-diagram-feedback.mmd +138 -0
- package/docs/er-diagram.mmd +281 -0
- package/docs/i18n-check-report.md +296 -0
- package/docs/index.md +214 -0
- package/docs/logic-chain.md +94 -0
- package/docs/plans/2026-01-02-database-migration-scripts.md +496 -0
- package/docs/plans/2026-01-02-user-login-design.md +37 -0
- package/docs/plans/2026-01-02-user-login.md +437 -0
- package/docs/plans/2026-01-02-user-registration-design.md +47 -0
- package/docs/plans/2026-01-02-user-registration.md +628 -0
- package/docs/plans/2026-01-03-roles-permissions-design.md +20 -0
- package/docs/plans/2026-01-03-roles-permissions.md +266 -0
- package/docs/plans/2026-01-05-authentication-middleware.md +207 -0
- package/docs/plans/2026-01-05-member-removal.md +186 -0
- package/docs/plans/2026-01-05-organization-creation.md +374 -0
- package/docs/plans/2026-01-05-rbac-middleware.md +112 -0
- package/docs/plans/2026-01-05-role-configuration.md +441 -0
- package/docs/plans/2026-01-06-file-upload-support.md +804 -0
- package/docs/plans/2026-01-06-permission-check-hook.md +155 -0
- package/docs/plans/2026-01-06-resource-ownership-check.md +231 -0
- package/docs/plans/2026-01-07-feedback-tracking-link.md +459 -0
- package/docs/plans/2026-01-09-logout-redirect-design.md +52 -0
- package/docs/plans/2026-01-09-phase2-3-plan.md +654 -0
- package/docs/plans/2026-01-09-portal-execution-plan.md +408 -0
- package/docs/plans/2026-01-09-project-delete-feature-design.md +163 -0
- package/docs/plans/2026-01-09-project-delete-implementation.md +451 -0
- package/docs/plans/2026-01-09-project-edit-delete-design.md +52 -0
- package/docs/plans/2026-01-09-settings-center-design.md +114 -0
- package/docs/plans/2026-01-09-settings-center.md +948 -0
- package/docs/plans/2026-01-10-organization-only-design.md +66 -0
- package/docs/plans/2026-01-10-organization-only-implementation.md +433 -0
- package/docs/plans/2026-01-10-portal-settings-restructure-plan.md +18 -0
- package/docs/plans/2026-01-10-project-settings-tabs-design-implementation.md +296 -0
- package/docs/plans/2026-01-14-e2e-playwright-feedback.md +173 -0
- package/docs/plans/2026-01-15-feedback-management-org-context-design.md +82 -0
- package/docs/plans/2026-01-15-feedback-management-org-context-implementation-plan.md +521 -0
- package/docs/plans/2026-01-16-admin-feedback-filters-design.md +75 -0
- package/docs/plans/2026-01-16-admin-feedback-filters-implementation.md +293 -0
- package/docs/plans/2026-01-16-admin-feedback-route-consolidation.md +180 -0
- package/docs/plans/2026-01-16-e2e-test-fixes.md +158 -0
- package/docs/plans/2026-01-17-admin-feedback-filters.md +214 -0
- package/docs/plans/2026-01-17-admin-feedback-improvements.md +453 -0
- package/docs/plans/2026-01-18-changesets-design.md +40 -0
- package/docs/product_changes.md +37 -0
- package/docs/project-overview.md +159 -0
- package/docs/project-scan-report.json +104 -0
- package/docs/route-role-visibility.md +51 -0
- package/docs/source-tree-analysis.md +150 -0
- package/docs/testing/delete-project-manual-tests.md +18 -0
- package/docs/user-story-tracking.md +191 -0
- package/drizzle.config.ts +32 -0
- package/eslint.config.mjs +19 -0
- package/hooks/use-permissions.ts +56 -0
- package/i18n/config.ts +45 -0
- package/i18n/request.ts +28 -0
- package/i18n/resolve-locale.ts +38 -0
- package/lib/api/errors.ts +62 -0
- package/lib/auth/cli-config.ts +35 -0
- package/lib/auth/client.ts +20 -0
- package/lib/auth/config.ts +55 -0
- package/lib/auth/jwt-identity.ts +21 -0
- package/lib/auth/org-context.ts +71 -0
- package/lib/auth/organization.ts +107 -0
- package/lib/auth/permissions.ts +87 -0
- package/lib/auth/session.ts +23 -0
- package/lib/config/rate-limits.ts +64 -0
- package/lib/dashboard/get-dashboard-stats.ts +136 -0
- package/lib/db/index.ts +41 -0
- package/lib/db/migrate.test.ts +49 -0
- package/lib/db/migrate.ts +62 -0
- package/lib/db/migrations/.gitkeep +0 -0
- package/lib/db/migrations/0000_cynical_gladiator.sql +53 -0
- package/lib/db/migrations/0001_wandering_sunfire.sql +27 -0
- package/lib/db/migrations/0002_shallow_speedball.sql +1 -0
- package/lib/db/migrations/0003_add_org_description.sql +1 -0
- package/lib/db/migrations/0003_boring_wild_pack.sql +13 -0
- package/lib/db/migrations/0004_windy_tyrannus.sql +27 -0
- package/lib/db/migrations/0005_perpetual_doorman.sql +5 -0
- package/lib/db/migrations/0006_aberrant_captain_midlands.sql +13 -0
- package/lib/db/migrations/0007_clever_captain_cross.sql +14 -0
- package/lib/db/migrations/0008_sparkling_pandemic.sql +2 -0
- package/lib/db/migrations/0009_happy_black_tom.sql +29 -0
- package/lib/db/migrations/0010_kind_junta.sql +8 -0
- package/lib/db/migrations/0011_mute_squadron_supreme.sql +25 -0
- package/lib/db/migrations/0012_giant_power_man.sql +24 -0
- package/lib/db/migrations/0013_damp_titanium_man.sql +17 -0
- package/lib/db/migrations/0014_blue_alice.sql +18 -0
- package/lib/db/migrations/0015_webhook_tables.sql +41 -0
- package/lib/db/migrations/0016_github_integration.sql +30 -0
- package/lib/db/migrations/0016_overjoyed_ghost_rider.sql +22 -0
- package/lib/db/migrations/0017_slimy_inhumans.sql +6 -0
- package/lib/db/migrations/0018_same_spitfire.sql +1 -0
- package/lib/db/migrations/0019_jittery_loners.sql +16 -0
- package/lib/db/migrations/0019_remove_projects_add_org_settings.sql +14 -0
- package/lib/db/migrations/meta/0000_snapshot.json +374 -0
- package/lib/db/migrations/meta/0001_snapshot.json +553 -0
- package/lib/db/migrations/meta/0002_snapshot.json +560 -0
- package/lib/db/migrations/meta/0003_snapshot.json +650 -0
- package/lib/db/migrations/meta/0004_snapshot.json +852 -0
- package/lib/db/migrations/meta/0005_snapshot.json +900 -0
- package/lib/db/migrations/meta/0006_snapshot.json +1011 -0
- package/lib/db/migrations/meta/0007_snapshot.json +1125 -0
- package/lib/db/migrations/meta/0008_snapshot.json +1146 -0
- package/lib/db/migrations/meta/0009_snapshot.json +1386 -0
- package/lib/db/migrations/meta/0010_snapshot.json +1419 -0
- package/lib/db/migrations/meta/0011_snapshot.json +1615 -0
- package/lib/db/migrations/meta/0012_snapshot.json +1805 -0
- package/lib/db/migrations/meta/0013_snapshot.json +1948 -0
- package/lib/db/migrations/meta/0014_snapshot.json +2082 -0
- package/lib/db/migrations/meta/0015_snapshot.json +2476 -0
- package/lib/db/migrations/meta/0016_snapshot.json +2633 -0
- package/lib/db/migrations/meta/0017_snapshot.json +2680 -0
- package/lib/db/migrations/meta/0018_snapshot.json +2686 -0
- package/lib/db/migrations/meta/0019_snapshot.json +2741 -0
- package/lib/db/migrations/meta/_journal.json +146 -0
- package/lib/db/schema/ai-processing.ts +90 -0
- package/lib/db/schema/api-keys.ts +61 -0
- package/lib/db/schema/attachments.ts +48 -0
- package/lib/db/schema/auth.ts +111 -0
- package/lib/db/schema/comments.ts +74 -0
- package/lib/db/schema/duplicates.ts +80 -0
- package/lib/db/schema/feedback.ts +88 -0
- package/lib/db/schema/github-integrations.ts +66 -0
- package/lib/db/schema/index.ts +35 -0
- package/lib/db/schema/invitations.ts +32 -0
- package/lib/db/schema/notifications.ts +85 -0
- package/lib/db/schema/organization-members.ts +37 -0
- package/lib/db/schema/organization-settings.ts +134 -0
- package/lib/db/schema/organizations.ts +30 -0
- package/lib/db/schema/projects.ts +145 -0
- package/lib/db/schema/status-history.ts +63 -0
- package/lib/db/schema/tags.ts +194 -0
- package/lib/db/schema/user-profiles.ts +31 -0
- package/lib/db/schema/votes.ts +60 -0
- package/lib/db/schema/webhooks.ts +106 -0
- package/lib/feedback/filters.ts +28 -0
- package/lib/feedback/find-similar.ts +49 -0
- package/lib/feedback/get-feedback-by-id.ts +159 -0
- package/lib/feedback/prefill.ts +51 -0
- package/lib/http/get-request-url.ts +28 -0
- package/lib/integrations/github.ts +159 -0
- package/lib/invitations.ts +22 -0
- package/lib/logger.test.ts +31 -0
- package/lib/logger.ts +58 -0
- package/lib/middleware/api-key.ts +126 -0
- package/lib/middleware/rate-limit-keys.ts +47 -0
- package/lib/middleware/rate-limit.ts +148 -0
- package/lib/middleware/rbac.ts +39 -0
- package/lib/middleware/request-id.test.ts +28 -0
- package/lib/middleware/request-id.ts +30 -0
- package/lib/middleware/request-logger.test.ts +36 -0
- package/lib/middleware/request-logger.ts +41 -0
- package/lib/middleware/with-rate-limit.ts +33 -0
- package/lib/portal/analytics.ts +20 -0
- package/lib/portal/contributors.ts +27 -0
- package/lib/portal/i18n.ts +20 -0
- package/lib/portal/leaderboard-settings.ts +20 -0
- package/lib/portal/modules.ts +20 -0
- package/lib/portal/portal-copy.ts +20 -0
- package/lib/portal/public-context.tsx +110 -0
- package/lib/portal/seo.ts +20 -0
- package/lib/portal/settings-context.ts +56 -0
- package/lib/portal/sharing.ts +20 -0
- package/lib/portal/sorting.ts +20 -0
- package/lib/portal/theme.ts +20 -0
- package/lib/services/ai/classifier.ts +296 -0
- package/lib/services/ai/duplicate-detector.ts +255 -0
- package/lib/services/ai/tag-suggester.ts +108 -0
- package/lib/services/api-keys.ts +164 -0
- package/lib/services/backup.ts +173 -0
- package/lib/services/email/templates.ts +158 -0
- package/lib/services/email.ts +68 -0
- package/lib/services/github-sync.ts +205 -0
- package/lib/services/notifications/index.ts +224 -0
- package/lib/services/portal-settings.ts +157 -0
- package/lib/swagger/config.ts +296 -0
- package/lib/swagger/generate.ts +400 -0
- package/lib/upload/file-validator.ts +52 -0
- package/lib/upload/storage.ts +59 -0
- package/lib/utils/format.ts +26 -0
- package/lib/utils/slug.ts +28 -0
- package/lib/utils.ts +23 -0
- package/lib/validations/auth.ts +56 -0
- package/lib/validations/comment.ts +44 -0
- package/lib/validations/feedback.ts +51 -0
- package/lib/validations/invitations.ts +23 -0
- package/lib/validations/organizations.ts +34 -0
- package/lib/validations/projects.ts +49 -0
- package/lib/validators/feedback.ts +57 -0
- package/lib/validators/index.ts +18 -0
- package/lib/webhooks/events.ts +73 -0
- package/lib/webhooks/index.ts +21 -0
- package/lib/webhooks/retry.ts +188 -0
- package/lib/webhooks/sender.ts +183 -0
- package/lib/webhooks/verify.ts +37 -0
- package/lib/workers/feedback-processor.ts +255 -0
- package/messages/en.json +965 -0
- package/messages/jp.json +862 -0
- package/messages/zh-CN.json +855 -0
- package/next-env.d.ts +6 -0
- package/next.config.ts +66 -0
- package/package.json +84 -0
- package/playwright.config.ts +44 -0
- package/postcss.config.mjs +7 -0
- package/proxy.test.ts +131 -0
- package/proxy.ts +190 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/logo-64.svg +5 -0
- package/public/logo.svg +5 -0
- package/public/next.svg +1 -0
- package/public/openapi.json +673 -0
- package/public/uploads/.gitkeep +0 -0
- package/public/uploads/02695701-ded0-4c81-8a21-9326c1d65448.pdf +1 -0
- package/public/uploads/178843ea-2780-48ef-8988-f4cba442e4cb.pdf +1 -0
- package/public/uploads/24b0a9ef-da93-49da-934f-637f89c7871d.pdf +1 -0
- package/public/uploads/7a11626d-a8e4-4b91-a8eb-20b6213b0a5a.pdf +1 -0
- package/public/uploads/b0703f4d-6e7b-4aab-8191-1a7b15f1b8ee.pdf +1 -0
- package/public/uploads/c8de0aed-4d3a-44aa-83bb-6594b7a2ddb3.pdf +1 -0
- package/public/uploads/e4cce295-0d85-4525-a1b0-a61c45722e26.pdf +1 -0
- package/public/uploads/eb4df45e-563c-48b8-9c68-c18212312426.pdf +1 -0
- package/public/vercel.svg +1 -0
- package/public/widget/embed.js +249 -0
- package/public/window.svg +1 -0
- package/scripts/backup-db.sh +57 -0
- package/scripts/backup-db.ts +24 -0
- package/scripts/generate-openapi.ts +22 -0
- package/scripts/migration-helper.ts +39 -0
- package/scripts/pre-deploy.ts +75 -0
- package/scripts/restore-db.sh +60 -0
- package/scripts/rollback.ts +72 -0
- package/scripts/seed-tags.ts +48 -0
- package/tests/api/feedback-bulk.test.ts +47 -0
- package/tests/api/feedback-by-id.test.ts +67 -0
- package/tests/api/feedback-comments-route-import.test.ts +26 -0
- package/tests/api/feedback-create.test.ts +71 -0
- package/tests/api/feedback-delete.test.ts +160 -0
- package/tests/api/feedback-filter.test.ts +250 -0
- package/tests/api/feedback-list.test.ts +234 -0
- package/tests/api/feedback-route-assignee-condition.test.ts +32 -0
- package/tests/api/feedback-similar.test.ts +46 -0
- package/tests/api/feedback-sort.test.ts +261 -0
- package/tests/api/feedback-status-enum.test.ts +49 -0
- package/tests/api/feedback-status-filter.test.ts +117 -0
- package/tests/api/feedback-submit-on-behalf.test.ts +269 -0
- package/tests/api/feedback.test.ts +175 -0
- package/tests/api/identify-jwt.test.ts +25 -0
- package/tests/api/invitation-accept.test.ts +213 -0
- package/tests/api/organization-invitations.test.ts +186 -0
- package/tests/api/organization-members-list.test.ts +79 -0
- package/tests/api/organization-members.test.ts +340 -0
- package/tests/api/organizations.test.ts +149 -0
- package/tests/api/register.test.ts +112 -0
- package/tests/api/upload.test.ts +103 -0
- package/tests/api/vote.test.ts +82 -0
- package/tests/app/admin-feedback-detail-page.test.tsx +25 -0
- package/tests/app/admin-feedback-list-page.test.tsx +25 -0
- package/tests/app/admin-feedback-new-page.test.tsx +25 -0
- package/tests/app/health-route-helpers.test.ts +27 -0
- package/tests/app/login-page.test.ts +26 -0
- package/tests/app/portal-page.test.ts +29 -0
- package/tests/app/project-portal-overview.test.tsx +25 -0
- package/tests/app/widget-page-import.test.ts +25 -0
- package/tests/components/create-post-dialog-defaults.test.ts +43 -0
- package/tests/components/feedback/duplicate-suggestions-inline.test.tsx +27 -0
- package/tests/components/feedback/embedded-feedback-form.test.tsx +96 -0
- package/tests/components/feedback/feedback-detail.test.tsx +25 -0
- package/tests/components/feedback/feedback-stats.test.tsx +49 -0
- package/tests/components/feedback-bulk-actions.test.tsx +39 -0
- package/tests/components/feedback-i18n-keys.test.ts +70 -0
- package/tests/components/feedback-list-controls-compile.test.ts +25 -0
- package/tests/components/feedback-list-controls.test.tsx +204 -0
- package/tests/components/feedback-list-item.test.tsx +67 -0
- package/tests/components/landing/hero.test.tsx +46 -0
- package/tests/components/layout/language-switcher.test.tsx +25 -0
- package/tests/components/layout/sidebar.test.tsx +157 -0
- package/tests/components/login-form.test.ts +25 -0
- package/tests/components/organization-form.test.ts +32 -0
- package/tests/components/organization-switcher.test.ts +25 -0
- package/tests/components/pagination.test.tsx +43 -0
- package/tests/components/portal-overview.test.tsx +25 -0
- package/tests/components/profile-form.test.tsx +139 -0
- package/tests/components/role-selector.test.ts +31 -0
- package/tests/components/status-chart.test.tsx +90 -0
- package/tests/e2e/auth.e2e.ts +323 -0
- package/tests/e2e/feedback-actions.e2e.ts +471 -0
- package/tests/e2e/feedback-attachment.e2e.ts +168 -0
- package/tests/e2e/feedback-customer.e2e.ts +226 -0
- package/tests/e2e/feedback-management.e2e.ts +565 -0
- package/tests/e2e/feedback-submit.e2e.ts +133 -0
- package/tests/e2e/feedback-view.e2e.ts +297 -0
- package/tests/e2e/fixtures/test-data.ts +235 -0
- package/tests/e2e/health-check.e2e.ts +230 -0
- package/tests/e2e/helpers/test-utils-helpers.test.ts +43 -0
- package/tests/e2e/helpers/test-utils.ts +298 -0
- package/tests/e2e/integration-placeholders.e2e.ts +199 -0
- package/tests/e2e/organization.e2e.ts +292 -0
- package/tests/e2e/permissions.e2e.ts +424 -0
- package/tests/e2e/project-widget.e2e.ts +63 -0
- package/tests/feedback/filters.test.ts +29 -0
- package/tests/hooks/use-permissions.test.ts +52 -0
- package/tests/lib/ai/classifier.test.ts +104 -0
- package/tests/lib/ai/duplicate-detector.test.ts +234 -0
- package/tests/lib/attachments-schema.test.ts +30 -0
- package/tests/lib/auth/session.test.ts +49 -0
- package/tests/lib/auth-client.test.ts +37 -0
- package/tests/lib/auth-config.test.ts +26 -0
- package/tests/lib/feedback-prefill.test.ts +52 -0
- package/tests/lib/feedback-processor.test.ts +41 -0
- package/tests/lib/feedback-schema.test.ts +33 -0
- package/tests/lib/file-validator.test.ts +48 -0
- package/tests/lib/get-feedback-by-id.test.ts +37 -0
- package/tests/lib/invitations.test.ts +35 -0
- package/tests/lib/login-schema.test.ts +36 -0
- package/tests/lib/org-context.test.ts +95 -0
- package/tests/lib/organization-access.test.ts +44 -0
- package/tests/lib/organization-member-role-schema.test.ts +41 -0
- package/tests/lib/permissions.test.ts +88 -0
- package/tests/lib/portal-analytics.test.ts +25 -0
- package/tests/lib/portal-contributors.test.ts +25 -0
- package/tests/lib/portal-copy.test.ts +27 -0
- package/tests/lib/portal-i18n.test.ts +30 -0
- package/tests/lib/portal-leaderboard-settings.test.ts +25 -0
- package/tests/lib/portal-modules.test.ts +25 -0
- package/tests/lib/portal-seo.test.ts +25 -0
- package/tests/lib/portal-sharing.test.ts +25 -0
- package/tests/lib/portal-sorting.test.ts +25 -0
- package/tests/lib/portal-theme.test.ts +25 -0
- package/tests/lib/rate-limit.test.ts +142 -0
- package/tests/lib/resolve-locale.test.ts +34 -0
- package/tests/lib/services/backup.test.ts +145 -0
- package/tests/lib/user-organizations.test.ts +42 -0
- package/tests/lib/user-role-schema.test.ts +33 -0
- package/tests/lib/user-schema.test.ts +25 -0
- package/tests/setup.ts +74 -0
- package/tsconfig.json +34 -0
- 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.
|