@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,66 @@
|
|
|
1
|
+
# Design: Organization-Only Model (No Projects/Boards)
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-01-10
|
|
4
|
+
**Status:** Approved
|
|
5
|
+
|
|
6
|
+
## Summary
|
|
7
|
+
Move Echo to an organization-only model. Remove the “project” concept and any boards. Each organization owns exactly three resource areas: Feedback, Roadmap, and Changelog. Public portal becomes a single organization entry at `/<organizationSlug>`, with tabs switching among the three.
|
|
8
|
+
|
|
9
|
+
## Goals
|
|
10
|
+
- Remove all project/board concepts from data model, routes, and UI.
|
|
11
|
+
- Consolidate configuration at organization level (portal/widget settings, domain, visibility).
|
|
12
|
+
- Keep organization isolation and permissions unchanged.
|
|
13
|
+
- Ensure MVP: auto-create a default organization for new users.
|
|
14
|
+
|
|
15
|
+
## Non-Goals
|
|
16
|
+
- Data migration from existing projects (data loss is acceptable per requirement).
|
|
17
|
+
- Multi-portal or per-resource domains.
|
|
18
|
+
- Multiple organizations per user experience redesign.
|
|
19
|
+
|
|
20
|
+
## Data Model Changes
|
|
21
|
+
- Drop `projects` table and all `projectId` references.
|
|
22
|
+
- Remove `projectId` fields, indexes, and foreign keys from other tables.
|
|
23
|
+
- Store portal/widget configuration on organization level:
|
|
24
|
+
- Option A (preferred): new `organization_settings` table.
|
|
25
|
+
- Option B: add columns on `organizations`.
|
|
26
|
+
- Feedback, roadmap, and changelog records are keyed by `organizationId`.
|
|
27
|
+
|
|
28
|
+
## Routing & UX
|
|
29
|
+
- Public portal: `/<organizationSlug>`
|
|
30
|
+
- Default view: Feedback
|
|
31
|
+
- Tabs: Feedback / Roadmap / Changelog
|
|
32
|
+
- Remove routes:
|
|
33
|
+
- `/settings/projects`
|
|
34
|
+
- `/settings/projects/new`
|
|
35
|
+
- `/settings/projects/[projectSlug]`
|
|
36
|
+
- `/widget/[organizationId]/[projectId]`
|
|
37
|
+
- Add/update routes:
|
|
38
|
+
- `/settings/organization`
|
|
39
|
+
- `/settings/organization/portal`
|
|
40
|
+
|
|
41
|
+
## API Changes
|
|
42
|
+
- Remove project CRUD APIs.
|
|
43
|
+
- Update portal/widget APIs to be organization-scoped only.
|
|
44
|
+
- Any endpoints that require `projectId` now infer by `organizationId`.
|
|
45
|
+
|
|
46
|
+
## Content Model
|
|
47
|
+
- Feedback: organization-scoped list and detail views.
|
|
48
|
+
- Roadmap: organization-scoped items and timeline.
|
|
49
|
+
- Changelog: organization-scoped entries.
|
|
50
|
+
|
|
51
|
+
## Permissions
|
|
52
|
+
- No change: permissions remain organization-scoped.
|
|
53
|
+
- Admin-only access to organization settings and portal config.
|
|
54
|
+
|
|
55
|
+
## Data Loss & Rollout
|
|
56
|
+
- Existing project data will be dropped.
|
|
57
|
+
- Migration scripts should remove project-related tables/columns and cleanup foreign keys.
|
|
58
|
+
|
|
59
|
+
## Testing
|
|
60
|
+
- Route access: no project pages remain.
|
|
61
|
+
- Portal loads at `/<organizationSlug>` with tabs switching among sections.
|
|
62
|
+
- Organization settings update portal config successfully.
|
|
63
|
+
- Feedback list and detail work without `projectId`.
|
|
64
|
+
|
|
65
|
+
## Open Questions
|
|
66
|
+
- Final choice: `organization_settings` vs adding columns on `organizations`.
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
# Organization-Only Model Implementation Plan
|
|
2
|
+
|
|
3
|
+
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
|
4
|
+
|
|
5
|
+
**Goal:** Remove project/board concepts and consolidate all data and UX at the organization level, with a public portal at `/<organizationSlug>` and three fixed sections: Feedback, Roadmap, Changelog.
|
|
6
|
+
|
|
7
|
+
**Architecture:** Drop the `projects` table and all `projectId` references. Introduce organization-level settings for portal/widget/custom domain (new `organization_settings` table). Update routes, APIs, and UI to be organization-scoped only. Public portal uses org slug paths (`/<orgSlug>`, `/<orgSlug>/roadmap`, `/<orgSlug>/changelog`).
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** Next.js App Router, Drizzle ORM, PostgreSQL, TypeScript, Tailwind, Bun.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Task 1: Add organization settings schema + drop project references in DB
|
|
14
|
+
|
|
15
|
+
**Files:**
|
|
16
|
+
- Create: `lib/db/schema/organization-settings.ts`
|
|
17
|
+
- Modify: `lib/db/schema/organizations.ts`
|
|
18
|
+
- Modify: `lib/db/schema/feedback.ts`
|
|
19
|
+
- Modify: `lib/db/schema/index.ts`
|
|
20
|
+
- Create: `lib/db/migrations/0019_remove_projects_add_org_settings.sql`
|
|
21
|
+
|
|
22
|
+
**Step 1: Write failing schema test (optional if no schema tests exist)**
|
|
23
|
+
- If schema tests exist, add a test in `tests/lib/organization-settings-schema.test.ts` to assert default config merging or type presence. Otherwise skip.
|
|
24
|
+
|
|
25
|
+
**Step 2: Write organization settings schema**
|
|
26
|
+
- Create `lib/db/schema/organization-settings.ts`:
|
|
27
|
+
```ts
|
|
28
|
+
import { relations } from "drizzle-orm";
|
|
29
|
+
import { index, jsonb, pgTable, text, timestamp, unique } from "drizzle-orm/pg-core";
|
|
30
|
+
import { organizations } from "./organizations";
|
|
31
|
+
|
|
32
|
+
export type WidgetConfig = {
|
|
33
|
+
theme?: "light" | "dark" | "auto";
|
|
34
|
+
primaryColor?: string;
|
|
35
|
+
buttonText?: string;
|
|
36
|
+
buttonPosition?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
|
|
37
|
+
fields?: {
|
|
38
|
+
showType?: boolean;
|
|
39
|
+
showPriority?: boolean;
|
|
40
|
+
showDescription?: boolean;
|
|
41
|
+
requireEmail?: boolean;
|
|
42
|
+
};
|
|
43
|
+
types?: string[];
|
|
44
|
+
customCSS?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type PortalThemeConfig = {
|
|
48
|
+
mode?: "light" | "dark" | "system";
|
|
49
|
+
primaryColor?: string;
|
|
50
|
+
accentColor?: string;
|
|
51
|
+
borderRadius?: "none" | "sm" | "md" | "lg" | "full";
|
|
52
|
+
fontFamily?: string;
|
|
53
|
+
customCSS?: string;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type PortalCopyConfig = {
|
|
57
|
+
title?: string;
|
|
58
|
+
description?: string;
|
|
59
|
+
ctaLabel?: string;
|
|
60
|
+
emptyStateMessage?: string;
|
|
61
|
+
successMessage?: string;
|
|
62
|
+
placeholders?: {
|
|
63
|
+
titleInput?: string;
|
|
64
|
+
descriptionInput?: string;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export type PortalSeoConfig = {
|
|
69
|
+
metaTitle?: string;
|
|
70
|
+
metaDescription?: string;
|
|
71
|
+
ogImage?: string;
|
|
72
|
+
favicon?: string;
|
|
73
|
+
noIndex?: boolean;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export type PortalSharingConfig = {
|
|
77
|
+
enabled?: boolean;
|
|
78
|
+
allowPublicVoting?: boolean;
|
|
79
|
+
allowPublicComments?: boolean;
|
|
80
|
+
showVoteCount?: boolean;
|
|
81
|
+
showAuthor?: boolean;
|
|
82
|
+
socialSharing?: {
|
|
83
|
+
twitter?: boolean;
|
|
84
|
+
linkedin?: boolean;
|
|
85
|
+
facebook?: boolean;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export type PortalConfig = {
|
|
90
|
+
theme?: PortalThemeConfig;
|
|
91
|
+
copy?: PortalCopyConfig;
|
|
92
|
+
seo?: PortalSeoConfig;
|
|
93
|
+
sharing?: PortalSharingConfig;
|
|
94
|
+
languages?: string[];
|
|
95
|
+
defaultLanguage?: string;
|
|
96
|
+
modules?: {
|
|
97
|
+
feedback?: boolean;
|
|
98
|
+
roadmap?: boolean;
|
|
99
|
+
changelog?: boolean;
|
|
100
|
+
help?: boolean;
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const organizationSettings = pgTable(
|
|
105
|
+
"organization_settings",
|
|
106
|
+
{
|
|
107
|
+
organizationId: text("organizationId")
|
|
108
|
+
.notNull()
|
|
109
|
+
.references(() => organizations.id, { onDelete: "cascade" })
|
|
110
|
+
.primaryKey(),
|
|
111
|
+
widgetConfig: jsonb("widgetConfig").$type<WidgetConfig>(),
|
|
112
|
+
portalConfig: jsonb("portalConfig").$type<PortalConfig>(),
|
|
113
|
+
customDomain: text("customDomain"),
|
|
114
|
+
createdAt: timestamp("createdAt").defaultNow().notNull(),
|
|
115
|
+
updatedAt: timestamp("updatedAt")
|
|
116
|
+
.defaultNow()
|
|
117
|
+
.$onUpdate(() => new Date())
|
|
118
|
+
.notNull(),
|
|
119
|
+
},
|
|
120
|
+
(table) => ({
|
|
121
|
+
orgIdx: index("idx_org_settings_orgId").on(table.organizationId),
|
|
122
|
+
customDomainUnique: unique("unique_org_custom_domain").on(table.customDomain),
|
|
123
|
+
})
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
export const organizationSettingsRelations = relations(organizationSettings, ({ one }) => ({
|
|
127
|
+
organization: one(organizations, {
|
|
128
|
+
fields: [organizationSettings.organizationId],
|
|
129
|
+
references: [organizations.id],
|
|
130
|
+
}),
|
|
131
|
+
}));
|
|
132
|
+
|
|
133
|
+
export type OrganizationSettings = typeof organizationSettings.$inferSelect;
|
|
134
|
+
export type NewOrganizationSettings = typeof organizationSettings.$inferInsert;
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Step 3: Remove `projectId` from feedback schema**
|
|
138
|
+
- Update `lib/db/schema/feedback.ts`:
|
|
139
|
+
- Remove import of `projects`.
|
|
140
|
+
- Remove `projectId` column and `idx_feedback_projectId` index.
|
|
141
|
+
|
|
142
|
+
**Step 4: Update schema index exports**
|
|
143
|
+
- Remove `projects` export, add `organization-settings` export in `lib/db/schema/index.ts`.
|
|
144
|
+
|
|
145
|
+
**Step 5: Write migration**
|
|
146
|
+
- Create `lib/db/migrations/0019_remove_projects_add_org_settings.sql`:
|
|
147
|
+
```sql
|
|
148
|
+
DROP TABLE IF EXISTS "projects" CASCADE;
|
|
149
|
+
ALTER TABLE "feedback" DROP COLUMN IF EXISTS "projectId";
|
|
150
|
+
DROP INDEX IF EXISTS "idx_feedback_projectId";
|
|
151
|
+
|
|
152
|
+
CREATE TABLE "organization_settings" (
|
|
153
|
+
"organizationId" text PRIMARY KEY REFERENCES "organizations"("id") ON DELETE cascade,
|
|
154
|
+
"widgetConfig" jsonb,
|
|
155
|
+
"portalConfig" jsonb,
|
|
156
|
+
"customDomain" text,
|
|
157
|
+
"createdAt" timestamp DEFAULT now() NOT NULL,
|
|
158
|
+
"updatedAt" timestamp DEFAULT now() NOT NULL
|
|
159
|
+
);
|
|
160
|
+
CREATE INDEX "idx_org_settings_orgId" ON "organization_settings" ("organizationId");
|
|
161
|
+
ALTER TABLE "organization_settings" ADD CONSTRAINT "unique_org_custom_domain" UNIQUE ("customDomain");
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Step 6: Commit**
|
|
165
|
+
```bash
|
|
166
|
+
git add lib/db/schema/organization-settings.ts lib/db/schema/feedback.ts lib/db/schema/index.ts lib/db/migrations/0019_remove_projects_add_org_settings.sql
|
|
167
|
+
|
|
168
|
+
git commit -m "feat: add organization settings and drop project schema"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Task 2: Replace portal settings service with organization-scoped service
|
|
174
|
+
|
|
175
|
+
**Files:**
|
|
176
|
+
- Modify: `lib/services/portal-settings.ts`
|
|
177
|
+
|
|
178
|
+
**Step 1: Write failing test for portal settings (if applicable)**
|
|
179
|
+
- Add/adjust test in `tests/lib/portal-settings.test.ts` (create if missing) to assert organization-scoped read/write.
|
|
180
|
+
|
|
181
|
+
**Step 2: Update service implementation**
|
|
182
|
+
- Replace `projectId` params with `organizationId` and query `organizationSettings`:
|
|
183
|
+
```ts
|
|
184
|
+
import { organizationSettings, PortalConfig } from "@/lib/db/schema";
|
|
185
|
+
|
|
186
|
+
export async function getPortalSettings(organizationId: string): Promise<PortalConfig | null> { /* ... */ }
|
|
187
|
+
export async function updatePortalSettings<T extends keyof PortalConfig>(
|
|
188
|
+
organizationId: string,
|
|
189
|
+
section: T,
|
|
190
|
+
data: PortalConfig[T]
|
|
191
|
+
): Promise<{ success: boolean; error?: string }> { /* ... */ }
|
|
192
|
+
export async function updateFullPortalConfig(
|
|
193
|
+
organizationId: string,
|
|
194
|
+
config: PortalConfig
|
|
195
|
+
): Promise<{ success: boolean; error?: string }> { /* ... */ }
|
|
196
|
+
```
|
|
197
|
+
- Ensure `organization_settings` row exists (insert on first write if missing).
|
|
198
|
+
|
|
199
|
+
**Step 3: Commit**
|
|
200
|
+
```bash
|
|
201
|
+
git add lib/services/portal-settings.ts
|
|
202
|
+
|
|
203
|
+
git commit -m "refactor: scope portal settings to organization"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Task 3: Remove project APIs and adjust feedback API
|
|
209
|
+
|
|
210
|
+
**Files:**
|
|
211
|
+
- Delete: `app/api/projects/route.ts`
|
|
212
|
+
- Delete: `app/api/projects/[projectId]/route.ts`
|
|
213
|
+
- Modify: `app/api/feedback/route.ts`
|
|
214
|
+
- Modify: `app/api/feedback/handler.ts`
|
|
215
|
+
- Modify: `lib/validators/feedback.ts`
|
|
216
|
+
|
|
217
|
+
**Step 1: Update feedback validation**
|
|
218
|
+
- Remove `projectId` from `feedbackSchema`.
|
|
219
|
+
|
|
220
|
+
**Step 2: Update feedback POST handler**
|
|
221
|
+
- Remove `projectId` insert in `app/api/feedback/handler.ts`.
|
|
222
|
+
|
|
223
|
+
**Step 3: Update feedback GET handler**
|
|
224
|
+
- Remove `projectId` filter logic from `app/api/feedback/route.ts`.
|
|
225
|
+
|
|
226
|
+
**Step 4: Remove project APIs**
|
|
227
|
+
- Delete both project route files and update any import references.
|
|
228
|
+
|
|
229
|
+
**Step 5: Commit**
|
|
230
|
+
```bash
|
|
231
|
+
git add app/api/feedback/route.ts app/api/feedback/handler.ts lib/validators/feedback.ts
|
|
232
|
+
|
|
233
|
+
git rm app/api/projects/route.ts app/api/projects/[projectId]/route.ts
|
|
234
|
+
|
|
235
|
+
git commit -m "refactor: remove project APIs and projectId in feedback"
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Task 4: Update portal routing to organization-only public paths
|
|
241
|
+
|
|
242
|
+
**Files:**
|
|
243
|
+
- Create: `app/[organizationSlug]/page.tsx`
|
|
244
|
+
- Create: `app/[organizationSlug]/roadmap/page.tsx`
|
|
245
|
+
- Create: `app/[organizationSlug]/changelog/page.tsx`
|
|
246
|
+
- Delete: `app/portal/page.tsx`
|
|
247
|
+
- Delete: `app/portal/[orgSlug]/[projectSlug]/page.tsx`
|
|
248
|
+
- Modify: `components/portal/portal-shell.tsx`
|
|
249
|
+
- Modify: `components/portal/portal-nav.tsx`
|
|
250
|
+
|
|
251
|
+
**Step 1: Add org portal pages**
|
|
252
|
+
- `app/[organizationSlug]/page.tsx` should load org by slug and render Feedback tab.
|
|
253
|
+
- Roadmap/Changelog pages can be placeholder components for now (e.g. empty states) but must render PortalShell with proper section links.
|
|
254
|
+
|
|
255
|
+
**Step 2: Update PortalShell to accept only `organizationId`**
|
|
256
|
+
- Remove `projectId` prop; pass organizationId only.
|
|
257
|
+
|
|
258
|
+
**Step 3: Ensure PortalNav highlights active tab by pathname**
|
|
259
|
+
- Keep `usePathname` and ensure section hrefs are `/${orgSlug}`, `/${orgSlug}/roadmap`, `/${orgSlug}/changelog`.
|
|
260
|
+
|
|
261
|
+
**Step 4: Commit**
|
|
262
|
+
```bash
|
|
263
|
+
git add app/[organizationSlug]/page.tsx app/[organizationSlug]/roadmap/page.tsx app/[organizationSlug]/changelog/page.tsx components/portal/portal-shell.tsx components/portal/portal-nav.tsx
|
|
264
|
+
|
|
265
|
+
git rm app/portal/page.tsx app/portal/[orgSlug]/[projectSlug]/page.tsx
|
|
266
|
+
|
|
267
|
+
git commit -m "feat: move public portal to organization routes"
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## Task 5: Update widget and embedded feedback to org-only
|
|
273
|
+
|
|
274
|
+
**Files:**
|
|
275
|
+
- Modify: `app/widget/[organizationId]/[projectId]/page.tsx` → move to `app/widget/[organizationId]/page.tsx`
|
|
276
|
+
- Modify: `components/feedback/embedded-feedback-form.tsx`
|
|
277
|
+
- Modify: `components/widget/widget-form.tsx`
|
|
278
|
+
- Modify: `components/project/embed-code-generator.tsx` (or delete if unused)
|
|
279
|
+
|
|
280
|
+
**Step 1: Replace widget route**
|
|
281
|
+
- New route: `app/widget/[organizationId]/page.tsx` reads organization settings and portal/widget configs.
|
|
282
|
+
- Update embed code to use `/widget/${organizationId}` only.
|
|
283
|
+
|
|
284
|
+
**Step 2: Update embedded feedback form**
|
|
285
|
+
- Remove `projectId` prop and any submit payload usage.
|
|
286
|
+
|
|
287
|
+
**Step 3: Commit**
|
|
288
|
+
```bash
|
|
289
|
+
git add app/widget/[organizationId]/page.tsx components/feedback/embedded-feedback-form.tsx components/widget/widget-form.tsx
|
|
290
|
+
|
|
291
|
+
git rm app/widget/[organizationId]/[projectId]/page.tsx
|
|
292
|
+
|
|
293
|
+
git commit -m "refactor: org-only widget and embedded feedback"
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## Task 6: Remove project UI and update settings to organization portal
|
|
299
|
+
|
|
300
|
+
**Files:**
|
|
301
|
+
- Delete: `components/project/*`
|
|
302
|
+
- Delete: `app/(dashboard)/settings/projects/**`
|
|
303
|
+
- Modify: `components/layout/sidebar.tsx`
|
|
304
|
+
- Modify: `components/layout/mobile-sidebar.tsx`
|
|
305
|
+
- Modify: `components/layout/dashboard-layout.tsx`
|
|
306
|
+
- Modify: `app/(dashboard)/layout.tsx`
|
|
307
|
+
- Modify: `components/settings/settings-sidebar.tsx`
|
|
308
|
+
- Create: `app/(dashboard)/settings/organization/portal/page.tsx`
|
|
309
|
+
- Modify: `components/portal/portal-settings-shell.tsx`
|
|
310
|
+
- Modify: `components/portal/portal-overview.tsx`
|
|
311
|
+
- Modify: portal settings forms to accept `organizationId` instead of `projectId`
|
|
312
|
+
|
|
313
|
+
**Step 1: Remove project UI**
|
|
314
|
+
- `git rm -r components/project app/(dashboard)/settings/projects`
|
|
315
|
+
|
|
316
|
+
**Step 2: Update layout/sidebar to remove project list**
|
|
317
|
+
- Remove `projects` props and related UI sections.
|
|
318
|
+
|
|
319
|
+
**Step 3: Update portal settings UI for organization**
|
|
320
|
+
- `PortalSettingsShell` becomes org-only (no selector).
|
|
321
|
+
- Update `PortalModulesPanel` + settings forms to accept `organizationId`.
|
|
322
|
+
- Add new route `/settings/organization/portal` to render portal settings with organization context.
|
|
323
|
+
|
|
324
|
+
**Step 4: Commit**
|
|
325
|
+
```bash
|
|
326
|
+
git add components/layout/sidebar.tsx components/layout/mobile-sidebar.tsx components/layout/dashboard-layout.tsx app/(dashboard)/layout.tsx components/portal/portal-settings-shell.tsx components/portal/portal-overview.tsx components/portal/portal-modules-panel.tsx components/portal/settings-forms/* app/(dashboard)/settings/organization/portal/page.tsx components/settings/settings-sidebar.tsx
|
|
327
|
+
|
|
328
|
+
git rm -r components/project app/(dashboard)/settings/projects
|
|
329
|
+
|
|
330
|
+
git commit -m "refactor: remove project UI and move portal settings to organization"
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Task 7: Update internal services and routes for organization context
|
|
336
|
+
|
|
337
|
+
**Files:**
|
|
338
|
+
- Delete: `lib/projects/get-projects.ts`
|
|
339
|
+
- Delete: `lib/projects/get-project-for-settings.ts`
|
|
340
|
+
- Delete: `lib/routes/projects.ts`
|
|
341
|
+
- Modify: `app/api/internal/domain-lookup/route.ts`
|
|
342
|
+
- Modify: `lib/services/github-sync.ts` (if it references projects)
|
|
343
|
+
|
|
344
|
+
**Step 1: Remove project helper modules**
|
|
345
|
+
- `git rm lib/projects/get-projects.ts lib/projects/get-project-for-settings.ts lib/routes/projects.ts`
|
|
346
|
+
|
|
347
|
+
**Step 2: Update domain lookup**
|
|
348
|
+
- Query `organizationSettings.customDomain` joined to `organizations`.
|
|
349
|
+
- Response should include `orgSlug` and `organizationId` only.
|
|
350
|
+
|
|
351
|
+
**Step 3: Commit**
|
|
352
|
+
```bash
|
|
353
|
+
git add app/api/internal/domain-lookup/route.ts
|
|
354
|
+
|
|
355
|
+
git rm lib/projects/get-projects.ts lib/projects/get-project-for-settings.ts lib/routes/projects.ts
|
|
356
|
+
|
|
357
|
+
git commit -m "refactor: drop project helpers and update domain lookup"
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Task 8: Clean up tests for project removal + add org-portal tests
|
|
363
|
+
|
|
364
|
+
**Files:**
|
|
365
|
+
- Delete: `tests/api/projects.test.ts`
|
|
366
|
+
- Delete: `tests/lib/routes/projects.test.ts`
|
|
367
|
+
- Delete or update: `tests/components/portal-project-switcher.test.tsx`
|
|
368
|
+
- Update: `tests/app/portal-page.test.ts`
|
|
369
|
+
- Update: `tests/components/layout/sidebar.test.tsx`
|
|
370
|
+
- Update: `tests/components/project-settings-tabs.test.tsx`
|
|
371
|
+
- Update: any project-based tests to org equivalents
|
|
372
|
+
|
|
373
|
+
**Step 1: Update portal page test**
|
|
374
|
+
- Point to new `app/[organizationSlug]/page.tsx` exports (or test for sections construction via helper).
|
|
375
|
+
|
|
376
|
+
**Step 2: Remove project-only tests**
|
|
377
|
+
- Remove tests that validate project routes/components.
|
|
378
|
+
|
|
379
|
+
**Step 3: Add org portal nav test**
|
|
380
|
+
- Add a test that ensures portal sections are `/${orgSlug}`, `/${orgSlug}/roadmap`, `/${orgSlug}/changelog`.
|
|
381
|
+
|
|
382
|
+
**Step 4: Commit**
|
|
383
|
+
```bash
|
|
384
|
+
git add tests/app/portal-page.test.ts tests/components/layout/sidebar.test.tsx
|
|
385
|
+
|
|
386
|
+
git rm tests/api/projects.test.ts tests/lib/routes/projects.test.ts tests/components/portal-project-switcher.test.tsx tests/components/project-settings-tabs.test.tsx
|
|
387
|
+
|
|
388
|
+
git commit -m "test: align portal and layout tests to org-only model"
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## Task 9: Docs cleanup (minimal)
|
|
394
|
+
|
|
395
|
+
**Files:**
|
|
396
|
+
- Modify: `docs/route-role-visibility.md`
|
|
397
|
+
- Modify: `docs/user-story-tracking.md`
|
|
398
|
+
|
|
399
|
+
**Step 1: Replace project routes with organization routes**
|
|
400
|
+
- Remove references to `/settings/projects/*` and `/widget/[organizationId]/[projectId]`.
|
|
401
|
+
- Add `/settings/organization/portal` and `/<organizationSlug>`.
|
|
402
|
+
|
|
403
|
+
**Step 2: Commit**
|
|
404
|
+
```bash
|
|
405
|
+
git add docs/route-role-visibility.md docs/user-story-tracking.md
|
|
406
|
+
|
|
407
|
+
git commit -m "docs: update routes for organization-only model"
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## Task 10: Verification
|
|
413
|
+
|
|
414
|
+
**Step 1: Run tests (expect current baseline lint warnings still present)**
|
|
415
|
+
```bash
|
|
416
|
+
bun run lint
|
|
417
|
+
bun test
|
|
418
|
+
```
|
|
419
|
+
Expected: lint may still warn as before; new tests should pass.
|
|
420
|
+
|
|
421
|
+
**Step 2: Manual smoke**
|
|
422
|
+
- Visit `/settings/organization/portal` and confirm portal settings render.
|
|
423
|
+
- Visit `/<organizationSlug>` and see tabs for Feedback/Roadmap/Changelog.
|
|
424
|
+
- Visit `/widget/<organizationId>` and submit feedback (no projectId in payload).
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
Plan complete and saved to `docs/plans/2026-01-10-organization-only-implementation.md`. Two execution options:
|
|
429
|
+
|
|
430
|
+
1. Subagent-Driven (this session) - I dispatch fresh subagent per task, review between tasks, fast iteration
|
|
431
|
+
2. Parallel Session (separate) - Open new session with executing-plans, batch execution with checkpoints
|
|
432
|
+
|
|
433
|
+
Which approach?
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Plan
|
|
2
|
+
|
|
3
|
+
We will move Portal settings under each project, create new project-scoped Portal routes with an overview page, migrate existing Portal settings pages into the new structure, and update navigation so Portal is no longer a global settings section.
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
- In: project-scoped Portal routes, Portal overview page, migration of Portal settings pages, navigation updates
|
|
7
|
+
- Out: new backend fields/logic, Portal front-end public UI changes, analytics on overview
|
|
8
|
+
|
|
9
|
+
## Action items
|
|
10
|
+
[ ] Review current Portal settings pages and project settings page to confirm fields and entry points
|
|
11
|
+
[ ] Add project-scoped Portal routes and an overview page with portal sub-nav
|
|
12
|
+
[ ] Migrate existing Portal settings pages to new routes (experience/growth/access/modules)
|
|
13
|
+
[ ] Re-map fields so access page holds visibility/permission/noindex, growth holds sharing + SEO
|
|
14
|
+
[ ] Update global settings sidebar and project settings entry to link Portal
|
|
15
|
+
[ ] Manually verify routing, permissions, and project context switching
|
|
16
|
+
|
|
17
|
+
## Open questions
|
|
18
|
+
- None (decisions: remove /settings/portal-* routes, modules as overview fold, no analytics on overview)
|