@nexttylabs/echo 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/app/(public)/[organizationSlug]/roadmap/page.tsx +19 -1
  3. package/app/api/admin/backup/route.ts +22 -4
  4. package/app/api/auth/register/handler.ts +1 -2
  5. package/lib/auth/config.ts +0 -7
  6. package/lib/db/migrations/0000_needy_leech.sql +335 -0
  7. package/lib/db/migrations/meta/0000_snapshot.json +2186 -1
  8. package/lib/db/migrations/meta/_journal.json +2 -135
  9. package/lib/db/schema/auth.ts +0 -1
  10. package/lib/db/schema/index.ts +0 -1
  11. package/lib/portal/public-context.tsx +5 -0
  12. package/package.json +20 -1
  13. package/.changeset/README.md +0 -21
  14. package/.changeset/config.json +0 -11
  15. package/.changeset/cozy-ghosts-care.md +0 -5
  16. package/.changeset/sharp-lines-stand.md +0 -5
  17. package/.changeset/sour-doodles-eat.md +0 -5
  18. package/.changeset/tender-moose-shop.md +0 -5
  19. package/.github/pull_request_template.md +0 -13
  20. package/.github/workflows/ci.yml +0 -41
  21. package/.github/workflows/publish.yml +0 -44
  22. package/.github/workflows/release.yml +0 -73
  23. package/AGENTS.md +0 -92
  24. package/Dockerfile +0 -57
  25. package/Makefile +0 -77
  26. package/bun.lock +0 -2503
  27. package/components/portal/project-switcher.tsx +0 -20
  28. package/docker-compose.dev.yml +0 -26
  29. package/docker-compose.yml +0 -98
  30. package/docs/architecture.md +0 -259
  31. package/docs/component-inventory.md +0 -261
  32. package/docs/database-migrations.md +0 -76
  33. package/docs/development-guide.md +0 -209
  34. package/docs/e2e-user-flows.csv +0 -31
  35. package/docs/er-diagram-feedback.mmd +0 -138
  36. package/docs/er-diagram.mmd +0 -281
  37. package/docs/i18n-check-report.md +0 -296
  38. package/docs/index.md +0 -214
  39. package/docs/logic-chain.md +0 -94
  40. package/docs/plans/2026-01-02-database-migration-scripts.md +0 -496
  41. package/docs/plans/2026-01-02-user-login-design.md +0 -37
  42. package/docs/plans/2026-01-02-user-login.md +0 -437
  43. package/docs/plans/2026-01-02-user-registration-design.md +0 -47
  44. package/docs/plans/2026-01-02-user-registration.md +0 -628
  45. package/docs/plans/2026-01-03-roles-permissions-design.md +0 -20
  46. package/docs/plans/2026-01-03-roles-permissions.md +0 -266
  47. package/docs/plans/2026-01-05-authentication-middleware.md +0 -207
  48. package/docs/plans/2026-01-05-member-removal.md +0 -186
  49. package/docs/plans/2026-01-05-organization-creation.md +0 -374
  50. package/docs/plans/2026-01-05-rbac-middleware.md +0 -112
  51. package/docs/plans/2026-01-05-role-configuration.md +0 -441
  52. package/docs/plans/2026-01-06-file-upload-support.md +0 -804
  53. package/docs/plans/2026-01-06-permission-check-hook.md +0 -155
  54. package/docs/plans/2026-01-06-resource-ownership-check.md +0 -231
  55. package/docs/plans/2026-01-07-feedback-tracking-link.md +0 -459
  56. package/docs/plans/2026-01-09-logout-redirect-design.md +0 -52
  57. package/docs/plans/2026-01-09-phase2-3-plan.md +0 -654
  58. package/docs/plans/2026-01-09-portal-execution-plan.md +0 -408
  59. package/docs/plans/2026-01-09-project-delete-feature-design.md +0 -163
  60. package/docs/plans/2026-01-09-project-delete-implementation.md +0 -451
  61. package/docs/plans/2026-01-09-project-edit-delete-design.md +0 -52
  62. package/docs/plans/2026-01-09-settings-center-design.md +0 -114
  63. package/docs/plans/2026-01-09-settings-center.md +0 -948
  64. package/docs/plans/2026-01-10-organization-only-design.md +0 -66
  65. package/docs/plans/2026-01-10-organization-only-implementation.md +0 -433
  66. package/docs/plans/2026-01-10-portal-settings-restructure-plan.md +0 -18
  67. package/docs/plans/2026-01-10-project-settings-tabs-design-implementation.md +0 -296
  68. package/docs/plans/2026-01-14-e2e-playwright-feedback.md +0 -173
  69. package/docs/plans/2026-01-15-feedback-management-org-context-design.md +0 -82
  70. package/docs/plans/2026-01-15-feedback-management-org-context-implementation-plan.md +0 -521
  71. package/docs/plans/2026-01-16-admin-feedback-filters-design.md +0 -75
  72. package/docs/plans/2026-01-16-admin-feedback-filters-implementation.md +0 -293
  73. package/docs/plans/2026-01-16-admin-feedback-route-consolidation.md +0 -180
  74. package/docs/plans/2026-01-16-e2e-test-fixes.md +0 -158
  75. package/docs/plans/2026-01-17-admin-feedback-filters.md +0 -214
  76. package/docs/plans/2026-01-17-admin-feedback-improvements.md +0 -453
  77. package/docs/plans/2026-01-18-changesets-design.md +0 -40
  78. package/docs/product_changes.md +0 -37
  79. package/docs/project-overview.md +0 -159
  80. package/docs/project-scan-report.json +0 -104
  81. package/docs/route-role-visibility.md +0 -51
  82. package/docs/source-tree-analysis.md +0 -150
  83. package/docs/testing/delete-project-manual-tests.md +0 -18
  84. package/docs/user-story-tracking.md +0 -191
  85. package/eslint.config.mjs +0 -19
  86. package/lib/db/migrations/.gitkeep +0 -0
  87. package/lib/db/migrations/0000_cynical_gladiator.sql +0 -53
  88. package/lib/db/migrations/0001_wandering_sunfire.sql +0 -27
  89. package/lib/db/migrations/0002_shallow_speedball.sql +0 -1
  90. package/lib/db/migrations/0003_add_org_description.sql +0 -1
  91. package/lib/db/migrations/0003_boring_wild_pack.sql +0 -13
  92. package/lib/db/migrations/0004_windy_tyrannus.sql +0 -27
  93. package/lib/db/migrations/0005_perpetual_doorman.sql +0 -5
  94. package/lib/db/migrations/0006_aberrant_captain_midlands.sql +0 -13
  95. package/lib/db/migrations/0007_clever_captain_cross.sql +0 -14
  96. package/lib/db/migrations/0008_sparkling_pandemic.sql +0 -2
  97. package/lib/db/migrations/0009_happy_black_tom.sql +0 -29
  98. package/lib/db/migrations/0010_kind_junta.sql +0 -8
  99. package/lib/db/migrations/0011_mute_squadron_supreme.sql +0 -25
  100. package/lib/db/migrations/0012_giant_power_man.sql +0 -24
  101. package/lib/db/migrations/0013_damp_titanium_man.sql +0 -17
  102. package/lib/db/migrations/0014_blue_alice.sql +0 -18
  103. package/lib/db/migrations/0015_webhook_tables.sql +0 -41
  104. package/lib/db/migrations/0016_github_integration.sql +0 -30
  105. package/lib/db/migrations/0016_overjoyed_ghost_rider.sql +0 -22
  106. package/lib/db/migrations/0017_slimy_inhumans.sql +0 -6
  107. package/lib/db/migrations/0018_same_spitfire.sql +0 -1
  108. package/lib/db/migrations/0019_jittery_loners.sql +0 -16
  109. package/lib/db/migrations/0019_remove_projects_add_org_settings.sql +0 -14
  110. package/lib/db/migrations/meta/0001_snapshot.json +0 -553
  111. package/lib/db/migrations/meta/0002_snapshot.json +0 -560
  112. package/lib/db/migrations/meta/0003_snapshot.json +0 -650
  113. package/lib/db/migrations/meta/0004_snapshot.json +0 -852
  114. package/lib/db/migrations/meta/0005_snapshot.json +0 -900
  115. package/lib/db/migrations/meta/0006_snapshot.json +0 -1011
  116. package/lib/db/migrations/meta/0007_snapshot.json +0 -1125
  117. package/lib/db/migrations/meta/0008_snapshot.json +0 -1146
  118. package/lib/db/migrations/meta/0009_snapshot.json +0 -1386
  119. package/lib/db/migrations/meta/0010_snapshot.json +0 -1419
  120. package/lib/db/migrations/meta/0011_snapshot.json +0 -1615
  121. package/lib/db/migrations/meta/0012_snapshot.json +0 -1805
  122. package/lib/db/migrations/meta/0013_snapshot.json +0 -1948
  123. package/lib/db/migrations/meta/0014_snapshot.json +0 -2082
  124. package/lib/db/migrations/meta/0015_snapshot.json +0 -2476
  125. package/lib/db/migrations/meta/0016_snapshot.json +0 -2633
  126. package/lib/db/migrations/meta/0017_snapshot.json +0 -2680
  127. package/lib/db/migrations/meta/0018_snapshot.json +0 -2686
  128. package/lib/db/migrations/meta/0019_snapshot.json +0 -2741
  129. package/lib/db/schema/projects.ts +0 -145
  130. package/lib/db/schema/user-profiles.ts +0 -31
  131. package/lib/validations/projects.ts +0 -49
  132. package/next-env.d.ts +0 -6
  133. package/playwright.config.ts +0 -44
  134. package/proxy.test.ts +0 -131
  135. package/proxy.ts +0 -116
  136. package/scripts/backup-db.sh +0 -57
  137. package/scripts/backup-db.ts +0 -24
  138. package/scripts/generate-openapi.ts +0 -22
  139. package/scripts/migration-helper.ts +0 -39
  140. package/scripts/pre-deploy.ts +0 -75
  141. package/scripts/restore-db.sh +0 -60
  142. package/scripts/rollback.ts +0 -72
  143. package/scripts/seed-tags.ts +0 -48
  144. package/tests/api/feedback-bulk.test.ts +0 -47
  145. package/tests/api/feedback-by-id.test.ts +0 -67
  146. package/tests/api/feedback-comments-route-import.test.ts +0 -26
  147. package/tests/api/feedback-create.test.ts +0 -71
  148. package/tests/api/feedback-delete.test.ts +0 -160
  149. package/tests/api/feedback-filter.test.ts +0 -250
  150. package/tests/api/feedback-list.test.ts +0 -234
  151. package/tests/api/feedback-route-assignee-condition.test.ts +0 -32
  152. package/tests/api/feedback-similar.test.ts +0 -46
  153. package/tests/api/feedback-sort.test.ts +0 -261
  154. package/tests/api/feedback-status-enum.test.ts +0 -49
  155. package/tests/api/feedback-status-filter.test.ts +0 -117
  156. package/tests/api/feedback-submit-on-behalf.test.ts +0 -269
  157. package/tests/api/feedback.test.ts +0 -175
  158. package/tests/api/identify-jwt.test.ts +0 -25
  159. package/tests/api/invitation-accept.test.ts +0 -213
  160. package/tests/api/organization-invitations.test.ts +0 -186
  161. package/tests/api/organization-members-list.test.ts +0 -79
  162. package/tests/api/organization-members.test.ts +0 -340
  163. package/tests/api/organizations.test.ts +0 -149
  164. package/tests/api/register.test.ts +0 -112
  165. package/tests/api/upload.test.ts +0 -103
  166. package/tests/api/vote.test.ts +0 -82
  167. package/tests/app/admin-feedback-detail-page.test.tsx +0 -25
  168. package/tests/app/admin-feedback-list-page.test.tsx +0 -25
  169. package/tests/app/admin-feedback-new-page.test.tsx +0 -25
  170. package/tests/app/health-route-helpers.test.ts +0 -27
  171. package/tests/app/login-page.test.ts +0 -26
  172. package/tests/app/portal-page.test.ts +0 -29
  173. package/tests/app/project-portal-overview.test.tsx +0 -25
  174. package/tests/app/widget-page-import.test.ts +0 -25
  175. package/tests/components/create-post-dialog-defaults.test.ts +0 -43
  176. package/tests/components/feedback/duplicate-suggestions-inline.test.tsx +0 -27
  177. package/tests/components/feedback/embedded-feedback-form.test.tsx +0 -96
  178. package/tests/components/feedback/feedback-detail.test.tsx +0 -25
  179. package/tests/components/feedback/feedback-stats.test.tsx +0 -49
  180. package/tests/components/feedback-bulk-actions.test.tsx +0 -39
  181. package/tests/components/feedback-i18n-keys.test.ts +0 -70
  182. package/tests/components/feedback-list-controls-compile.test.ts +0 -25
  183. package/tests/components/feedback-list-controls.test.tsx +0 -204
  184. package/tests/components/feedback-list-item.test.tsx +0 -67
  185. package/tests/components/landing/hero.test.tsx +0 -46
  186. package/tests/components/layout/language-switcher.test.tsx +0 -25
  187. package/tests/components/layout/sidebar.test.tsx +0 -157
  188. package/tests/components/login-form.test.ts +0 -25
  189. package/tests/components/organization-form.test.ts +0 -32
  190. package/tests/components/organization-switcher.test.ts +0 -25
  191. package/tests/components/pagination.test.tsx +0 -43
  192. package/tests/components/portal-overview.test.tsx +0 -25
  193. package/tests/components/profile-form.test.tsx +0 -139
  194. package/tests/components/role-selector.test.ts +0 -31
  195. package/tests/components/status-chart.test.tsx +0 -90
  196. package/tests/e2e/auth.e2e.ts +0 -323
  197. package/tests/e2e/feedback-actions.e2e.ts +0 -471
  198. package/tests/e2e/feedback-attachment.e2e.ts +0 -168
  199. package/tests/e2e/feedback-customer.e2e.ts +0 -226
  200. package/tests/e2e/feedback-management.e2e.ts +0 -565
  201. package/tests/e2e/feedback-submit.e2e.ts +0 -133
  202. package/tests/e2e/feedback-view.e2e.ts +0 -297
  203. package/tests/e2e/fixtures/test-data.ts +0 -235
  204. package/tests/e2e/health-check.e2e.ts +0 -230
  205. package/tests/e2e/helpers/test-utils-helpers.test.ts +0 -43
  206. package/tests/e2e/helpers/test-utils.ts +0 -298
  207. package/tests/e2e/integration-placeholders.e2e.ts +0 -199
  208. package/tests/e2e/organization.e2e.ts +0 -292
  209. package/tests/e2e/permissions.e2e.ts +0 -424
  210. package/tests/e2e/project-widget.e2e.ts +0 -63
  211. package/tests/feedback/filters.test.ts +0 -29
  212. package/tests/hooks/use-permissions.test.ts +0 -52
  213. package/tests/lib/ai/classifier.test.ts +0 -104
  214. package/tests/lib/ai/duplicate-detector.test.ts +0 -234
  215. package/tests/lib/attachments-schema.test.ts +0 -30
  216. package/tests/lib/auth/session.test.ts +0 -49
  217. package/tests/lib/auth-client.test.ts +0 -37
  218. package/tests/lib/auth-config.test.ts +0 -26
  219. package/tests/lib/feedback-prefill.test.ts +0 -52
  220. package/tests/lib/feedback-processor.test.ts +0 -41
  221. package/tests/lib/feedback-schema.test.ts +0 -33
  222. package/tests/lib/file-validator.test.ts +0 -48
  223. package/tests/lib/get-feedback-by-id.test.ts +0 -37
  224. package/tests/lib/invitations.test.ts +0 -35
  225. package/tests/lib/login-schema.test.ts +0 -36
  226. package/tests/lib/org-context.test.ts +0 -95
  227. package/tests/lib/organization-access.test.ts +0 -44
  228. package/tests/lib/organization-member-role-schema.test.ts +0 -41
  229. package/tests/lib/permissions.test.ts +0 -88
  230. package/tests/lib/portal-analytics.test.ts +0 -25
  231. package/tests/lib/portal-contributors.test.ts +0 -25
  232. package/tests/lib/portal-copy.test.ts +0 -27
  233. package/tests/lib/portal-i18n.test.ts +0 -30
  234. package/tests/lib/portal-leaderboard-settings.test.ts +0 -25
  235. package/tests/lib/portal-modules.test.ts +0 -25
  236. package/tests/lib/portal-seo.test.ts +0 -25
  237. package/tests/lib/portal-sharing.test.ts +0 -25
  238. package/tests/lib/portal-sorting.test.ts +0 -25
  239. package/tests/lib/portal-theme.test.ts +0 -25
  240. package/tests/lib/rate-limit.test.ts +0 -142
  241. package/tests/lib/resolve-locale.test.ts +0 -34
  242. package/tests/lib/services/backup.test.ts +0 -145
  243. package/tests/lib/user-organizations.test.ts +0 -42
  244. package/tests/lib/user-role-schema.test.ts +0 -33
  245. package/tests/lib/user-schema.test.ts +0 -25
  246. package/tests/setup.ts +0 -74
  247. package/vercel.json +0 -4
@@ -1,296 +0,0 @@
1
- # Project Settings Tabs Implementation Plan
2
-
3
- > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
-
5
- **Goal:** Restructure project settings into three top-level tabs (通用设置 / Widget / Portal) and embed the Portal overview flow within the Portal tab.
6
-
7
- **Architecture:** Convert the project settings UI into a tabbed layout using the existing shadcn Tabs component. Extract the Portal overview into a reusable component so it can be rendered both inside the Portal tab and the dedicated Portal route pages.
8
-
9
- **Tech Stack:** Next.js App Router, React 19, TypeScript, shadcn/ui (Tabs, Card, Button, Badge, Switch), Tailwind CSS.
10
-
11
- ### Task 1: Add a reusable Portal overview component
12
-
13
- **Files:**
14
- - Create: `components/portal/portal-overview.tsx`
15
- - Modify: `app/(dashboard)/settings/projects/[projectSlug]/portal/page.tsx`
16
- - Test: `tests/components/portal-overview.test.tsx`
17
-
18
- **Step 1: Write the failing test**
19
-
20
- ```ts
21
- import { describe, expect, it } from "bun:test";
22
- import { PortalOverview } from "@/components/portal/portal-overview";
23
-
24
- describe("PortalOverview", () => {
25
- it("exports a component", () => {
26
- expect(typeof PortalOverview).toBe("function");
27
- });
28
- });
29
- ```
30
-
31
- **Step 2: Run test to verify it fails**
32
-
33
- Run: `bun test tests/components/portal-overview.test.tsx`
34
- Expected: FAIL with "Cannot find module '@/components/portal/portal-overview'"
35
-
36
- **Step 3: Write minimal implementation**
37
-
38
- ```tsx
39
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
40
- import { Badge } from "@/components/ui/badge";
41
- import { Button } from "@/components/ui/button";
42
- import { PortalModulesPanel } from "@/components/portal/portal-modules-panel";
43
- import { getDefaultPortalModules } from "@/lib/portal/modules";
44
- import type { PortalConfig } from "@/lib/db/schema/projects";
45
-
46
- export function PortalOverview({
47
- projectSlug,
48
- portalLink,
49
- portalConfig,
50
- projectId,
51
- }: {
52
- projectSlug: string;
53
- portalLink: string;
54
- portalConfig: PortalConfig | null;
55
- projectId: string;
56
- }) {
57
- const portalEnabled = portalConfig?.sharing?.enabled ?? true;
58
- const themeSummary = portalConfig?.theme?.primaryColor
59
- ? `主色 ${portalConfig.theme.primaryColor}`
60
- : "使用默认主题";
61
- const languageSummary = portalConfig?.defaultLanguage ?? "zh-CN";
62
- const seoSummary = portalConfig?.seo?.metaTitle
63
- ? portalConfig.seo.metaTitle
64
- : "未设置 SEO 标题";
65
-
66
- const modules = portalConfig?.modules ?? getDefaultPortalModules();
67
-
68
- return (
69
- <div className="space-y-8">
70
- <Card className="border-slate-200/80 bg-white/80 shadow-sm">
71
- <CardHeader className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
72
- <div>
73
- <CardTitle className="text-xl">Portal 状态</CardTitle>
74
- <CardDescription>当前项目的门户入口与可见性</CardDescription>
75
- </div>
76
- <div className="flex flex-wrap items-center gap-3">
77
- <Badge variant={portalEnabled ? "default" : "outline"}>
78
- {portalEnabled ? "公开中" : "已关闭"}
79
- </Badge>
80
- <Button asChild variant="outline" size="sm">
81
- <a href={portalLink}>预览 Portal</a>
82
- </Button>
83
- <Button asChild size="sm">
84
- <a href={portalLink}>打开 Portal</a>
85
- </Button>
86
- </div>
87
- </CardHeader>
88
- <CardContent>
89
- <div className="text-sm text-slate-600">
90
- 门户链接:<span className="font-medium text-slate-900">{portalLink}</span>
91
- </div>
92
- </CardContent>
93
- </Card>
94
-
95
- <div className="grid gap-6 lg:grid-cols-3">
96
- <PortalGroupCard
97
- title="体验"
98
- description="主题、文案与语言"
99
- items={[
100
- themeSummary,
101
- portalConfig?.copy?.title ? `标题:${portalConfig.copy.title}` : "未设置门户标题",
102
- `默认语言:${languageSummary}`,
103
- ]}
104
- href={`/settings/projects/${projectSlug}/portal/experience`}
105
- />
106
- <PortalGroupCard
107
- title="传播"
108
- description="分享与 SEO 配置"
109
- items={[
110
- seoSummary,
111
- portalConfig?.sharing?.socialSharing ? "社交分享已配置" : "未配置社交分享",
112
- portalConfig?.seo?.ogImage ? "已设置 OG 图片" : "未设置 OG 图片",
113
- ]}
114
- href={`/settings/projects/${projectSlug}/portal/growth`}
115
- />
116
- <PortalGroupCard
117
- title="可见性"
118
- description="公开权限与索引"
119
- items={[
120
- portalEnabled ? "门户公开" : "门户关闭",
121
- portalConfig?.sharing?.allowPublicVoting ? "允许公开投票" : "关闭公开投票",
122
- portalConfig?.seo?.noIndex ? "禁止索引" : "允许索引",
123
- ]}
124
- href={`/settings/projects/${projectSlug}/portal/access`}
125
- />
126
- </div>
127
-
128
- <details className="group">
129
- <summary className="cursor-pointer list-none text-sm font-semibold text-slate-700">
130
- <span className="inline-flex items-center gap-2">
131
- <span className="rounded-full bg-slate-200 px-2 py-0.5 text-xs font-medium text-slate-600">
132
- 模块开关
133
- </span>
134
- 管理 Portal 模块显示
135
- </span>
136
- </summary>
137
- <div className="mt-4">
138
- <PortalModulesPanel projectId={projectId} initialModules={modules} />
139
- </div>
140
- </details>
141
- </div>
142
- );
143
- }
144
-
145
- function PortalGroupCard({
146
- title,
147
- description,
148
- items,
149
- href,
150
- }: {
151
- title: string;
152
- description: string;
153
- items: string[];
154
- href: string;
155
- }) {
156
- return (
157
- <Card className="border-slate-200/80 bg-white/80 shadow-sm">
158
- <CardHeader>
159
- <CardTitle className="text-lg">{title}</CardTitle>
160
- <CardDescription>{description}</CardDescription>
161
- </CardHeader>
162
- <CardContent className="space-y-3">
163
- <ul className="space-y-1 text-sm text-slate-600">
164
- {items.map((item) => (
165
- <li key={item}>• {item}</li>
166
- ))}
167
- </ul>
168
- <Button asChild variant="outline" size="sm">
169
- <a href={href}>进入设置</a>
170
- </Button>
171
- </CardContent>
172
- </Card>
173
- );
174
- }
175
- ```
176
-
177
- **Step 4: Run test to verify it passes**
178
-
179
- Run: `bun test tests/components/portal-overview.test.tsx`
180
- Expected: PASS
181
-
182
- **Step 5: Commit**
183
-
184
- ```bash
185
- git add components/portal/portal-overview.tsx app/(dashboard)/settings/projects/[projectSlug]/portal/page.tsx tests/components/portal-overview.test.tsx
186
- git commit -m "feat: extract portal overview component"
187
- ```
188
-
189
- ### Task 2: Add tabs to project settings (通用设置 / Widget / Portal)
190
-
191
- **Files:**
192
- - Modify: `components/project/project-settings.tsx`
193
- - Modify: `app/(dashboard)/settings/projects/[projectSlug]/page.tsx`
194
- - Test: `tests/components/project-settings-tabs.test.tsx`
195
-
196
- **Step 1: Write the failing test**
197
-
198
- ```ts
199
- import { describe, expect, it } from "bun:test";
200
- import { ProjectSettings } from "@/components/project/project-settings";
201
-
202
- describe("ProjectSettings tabs", () => {
203
- it("exports a component", () => {
204
- expect(typeof ProjectSettings).toBe("function");
205
- });
206
- });
207
- ```
208
-
209
- **Step 2: Run test to verify it fails**
210
-
211
- Run: `bun test tests/components/project-settings-tabs.test.tsx`
212
- Expected: PASS (component exists) — update test to check for tabs labels after implementation
213
-
214
- **Step 3: Write minimal implementation**
215
-
216
- - Use `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent` from `components/ui/tabs.tsx`
217
- - Create three tabs: 通用设置 / Widget / Portal
218
- - Move existing Widget form + preview into Widget tab
219
- - Move Portal entry into Portal tab and render `PortalOverview` component
220
- - Keep “危险区域” inside 通用设置 tab
221
-
222
- **Step 4: Run test to verify it passes**
223
-
224
- Run: `bun test tests/components/project-settings-tabs.test.tsx`
225
- Expected: PASS
226
-
227
- **Step 5: Commit**
228
-
229
- ```bash
230
- git add components/project/project-settings.tsx app/(dashboard)/settings/projects/[projectSlug]/page.tsx tests/components/project-settings-tabs.test.tsx
231
- git commit -m "feat: add project settings tabs"
232
- ```
233
-
234
- ### Task 3: Wire Portal tab to new overview and subpages
235
-
236
- **Files:**
237
- - Modify: `components/project/project-settings.tsx`
238
- - Modify: `app/(dashboard)/settings/projects/[projectSlug]/portal/page.tsx`
239
- - Test: `tests/app/project-portal-overview.test.tsx`
240
-
241
- **Step 1: Write the failing test**
242
-
243
- ```ts
244
- import { describe, expect, it } from "bun:test";
245
- import { PortalOverview } from "@/components/portal/portal-overview";
246
-
247
- describe("Portal overview routing", () => {
248
- it("exports a component", () => {
249
- expect(typeof PortalOverview).toBe("function");
250
- });
251
- });
252
- ```
253
-
254
- **Step 2: Run test to verify it fails**
255
-
256
- Run: `bun test tests/app/project-portal-overview.test.tsx`
257
- Expected: PASS
258
-
259
- **Step 3: Write minimal implementation**
260
-
261
- - Replace inline overview markup in portal overview page with `PortalOverview`
262
- - Use same component in Portal tab content
263
-
264
- **Step 4: Run test to verify it passes**
265
-
266
- Run: `bun test tests/app/project-portal-overview.test.tsx`
267
- Expected: PASS
268
-
269
- **Step 5: Commit**
270
-
271
- ```bash
272
- git add components/project/project-settings.tsx app/(dashboard)/settings/projects/[projectSlug]/portal/page.tsx tests/app/project-portal-overview.test.tsx
273
- git commit -m "feat: embed portal overview in project settings"
274
- ```
275
-
276
- ### Task 4: Verification
277
-
278
- **Files:**
279
- - None
280
-
281
- **Step 1: Run lint**
282
-
283
- Run: `bun run lint`
284
- Expected: PASS with existing warnings only
285
-
286
- **Step 2: Manual check**
287
-
288
- - Visit `/settings/projects/[projectSlug]` and verify tabs and content
289
- - Visit `/settings/projects/[projectSlug]/portal` and verify overview + module toggle
290
- - Verify Portal subpages still accessible
291
-
292
- **Step 3: Commit (if needed)**
293
-
294
- ```bash
295
- git status -sb
296
- ```
@@ -1,173 +0,0 @@
1
- # Playwright Feedback Submission E2E Implementation Plan
2
-
3
- > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
-
5
- **Goal:** Add a Playwright E2E smoke test that registers a user, opens the organization portal, submits feedback, and confirms it appears in the list.
6
-
7
- **Architecture:** Use Playwright for browser automation and `page.request` to call `/api/auth/register` in the same browser context to seed a user+organization, then drive the portal UI to create a feedback post. Store test config in `playwright.config.ts` with a local dev server and standard artifacts.
8
-
9
- **Tech Stack:** Playwright, Bun, Next.js App Router, better-auth, Drizzle/Postgres.
10
-
11
- ### Task 1: Add the failing E2E test (red)
12
-
13
- **Files:**
14
- - Create: `tests/e2e/feedback-submit.spec.ts`
15
-
16
- **Step 1: Write the failing test**
17
-
18
- ```ts
19
- import { test, expect } from "@playwright/test";
20
-
21
- function uniqueEmail() {
22
- return `e2e+${Date.now()}@example.com`;
23
- }
24
-
25
- function uniqueTitle() {
26
- return `E2E feedback ${Date.now()}`;
27
- }
28
-
29
- test("feedback submission from portal creates a new post", async ({ page }) => {
30
- const email = uniqueEmail();
31
- const password = "StrongPass123!";
32
- const name = "E2E User";
33
-
34
- const register = await page.request.post("/api/auth/register", {
35
- data: { name, email, password },
36
- });
37
-
38
- expect(register.ok()).toBeTruthy();
39
- const json = await register.json();
40
- const slug = json?.data?.organization?.slug;
41
- expect(slug).toBeTruthy();
42
-
43
- const title = uniqueTitle();
44
-
45
- await page.goto(`/${slug}`);
46
- await page.getByRole("button", { name: "Submit Feedback" }).click();
47
-
48
- await page.getByLabel("Title").fill(title);
49
- await page.getByLabel("Description").fill("This is an E2E submission.");
50
- await page.getByRole("button", { name: "Create Post" }).click();
51
-
52
- await expect(page.getByRole("heading", { name: title })).toBeVisible();
53
- });
54
- ```
55
-
56
- **Step 2: Run test to verify it fails**
57
-
58
- Run: `bunx playwright test tests/e2e/feedback-submit.spec.ts`
59
- Expected: FAIL because Playwright is not installed/configured.
60
-
61
- ### Task 2: Add Playwright tooling/configuration (green for tooling)
62
-
63
- **Files:**
64
- - Create: `playwright.config.ts`
65
- - Modify: `package.json`
66
- - Modify: `.gitignore`
67
-
68
- **Step 1: Write minimal Playwright config**
69
-
70
- ```ts
71
- import { defineConfig, devices } from "@playwright/test";
72
-
73
- export default defineConfig({
74
- testDir: "./tests/e2e",
75
- timeout: 60_000,
76
- expect: { timeout: 10_000 },
77
- retries: 1,
78
- use: {
79
- baseURL: process.env.E2E_BASE_URL ?? "http://localhost:3000",
80
- trace: "retain-on-failure",
81
- screenshot: "only-on-failure",
82
- video: "retain-on-failure",
83
- },
84
- webServer: {
85
- command: "bun dev",
86
- url: "http://localhost:3000",
87
- reuseExistingServer: !process.env.CI,
88
- timeout: 120_000,
89
- },
90
- projects: [
91
- {
92
- name: "chromium",
93
- use: { ...devices["Desktop Chrome"] },
94
- },
95
- ],
96
- });
97
- ```
98
-
99
- **Step 2: Add scripts/deps**
100
-
101
- - Add devDependency: `@playwright/test`
102
- - Add script: `"test:e2e": "bunx playwright test"`
103
- - Add script: `"test:e2e:ui": "bunx playwright test --ui"`
104
-
105
- **Step 3: Ignore Playwright artifacts**
106
-
107
- Add to `.gitignore`:
108
- ```
109
- playwright-report/
110
- test-results/
111
- .playwright/
112
- ```
113
-
114
- **Step 4: Run the failing test again**
115
-
116
- Run: `bunx playwright test tests/e2e/feedback-submit.spec.ts`
117
- Expected: FAIL due to missing/incorrect behavior in the app (if any), or PASS if flow works end-to-end.
118
-
119
- ### Task 3: Make the test pass (minimal changes)
120
-
121
- **Files:**
122
- - Modify (if needed): `components/portal/create-post-dialog.tsx`
123
- - Modify (if needed): `components/portal/feedback-board.tsx`
124
- - Modify (if needed): `app/[organizationSlug]/page.tsx`
125
- - Modify (if needed): `tests/e2e/feedback-submit.spec.ts`
126
-
127
- **Step 1: Observe failure and identify the minimal fix**
128
-
129
- Run: `bunx playwright test tests/e2e/feedback-submit.spec.ts`
130
- Expected: FAIL with a clear reason (e.g., button labels differ, missing fields, redirect to login).
131
-
132
- **Step 2: Apply minimal code change**
133
-
134
- Examples (only if needed):
135
- - If button label mismatches, prefer role/label selectors or update labels.
136
- - If dialog doesn’t open due to auth, ensure register call sets cookies (use `page.request` only, not a separate request context).
137
-
138
- **Step 3: Re-run the test**
139
-
140
- Run: `bunx playwright test tests/e2e/feedback-submit.spec.ts`
141
- Expected: PASS.
142
-
143
- **Step 4: Commit**
144
-
145
- ```bash
146
- git add playwright.config.ts package.json .gitignore tests/e2e/feedback-submit.spec.ts
147
- # plus any modified app files if needed
148
- git commit -m "test: add Playwright E2E feedback submission"
149
- ```
150
-
151
- ### Task 4: Document local E2E prerequisites
152
-
153
- **Files:**
154
- - Modify: `README.md`
155
-
156
- **Step 1: Add E2E run instructions**
157
-
158
- Add a short section:
159
- - Set `DATABASE_URL` and `BETTER_AUTH_SECRET`
160
- - Run migrations: `bun run db:migrate`
161
- - Run: `bun run test:e2e`
162
-
163
- **Step 2: Verify docs**
164
-
165
- Run: `rg -n "E2E" README.md`
166
- Expected: section present.
167
-
168
- **Step 3: Commit**
169
-
170
- ```bash
171
- git add README.md
172
- git commit -m "docs: add Playwright E2E setup"
173
- ```
@@ -1,82 +0,0 @@
1
- # Feedback Management Organization Context - Design
2
-
3
- Date: 2026-01-15
4
-
5
- ## Summary
6
- Refactor Feedback Management to be organization-aware by default. Introduce a unified organization context resolver that reads organization from URL, headers, and cookies (in that priority) and enforces organization membership on server-side. Add an organization switcher only on `/dashboard` that writes the selected org to cookie and URL. All feedback-related pages and APIs must use the resolver to prevent cross-organization data access.
7
-
8
- ## Goals
9
- - Enforce organization isolation across all feedback pages and APIs.
10
- - Centralize organization resolution and authorization on the server.
11
- - Support multi-organization users with a dashboard switcher.
12
- - Persist organization selection with cookie and allow URL override.
13
-
14
- ## Non-Goals
15
- - No global organization picker outside `/dashboard`.
16
- - No super-admin global org visibility.
17
- - No UI redesign beyond adding the switcher on `/dashboard`.
18
-
19
- ## Current State
20
- - Some endpoints accept `organizationId` query/header but not consistently enforced.
21
- - `/dashboard` uses a single organization and some pages still hardcode IDs.
22
- - Multiple feedback APIs and pages query feedback by ID without consistent org checks.
23
-
24
- ## Proposed Architecture
25
- ### Organization Context Resolver
26
- Add a new module (e.g. `lib/auth/org-context.ts`) that provides:
27
- - `getOrgContext(request, options)`
28
- - Resolves `organizationId` from:
29
- 1) URL query (`organizationId`)
30
- 2) Header (`x-organization-id`)
31
- 3) Cookie (`orgId`)
32
- - Optionally reads `organizationSlug` for portal routes when provided.
33
- - Returns `{ organizationId, memberRole, source }`.
34
- - If user is logged in and `organizationId` is present, verifies membership.
35
- - If user is anonymous, allows read-only contexts for public routes.
36
- - `requireOrgContext(request, options)`
37
- - Same as above, but throws/returns 403 when membership is missing or invalid.
38
-
39
- ### Enforcement
40
- - All feedback list, detail, stats, comments, votes, duplicates, and similarity APIs must call `requireOrgContext`.
41
- - All feedback pages (dashboard, admin, feedback detail) must use resolver-derived org.
42
- - Portal pages use slug -> org ID and bypass membership checks for read-only views.
43
-
44
- ## Data Flow
45
- 1. User lands on `/dashboard`.
46
- 2. Dashboard requests user organizations and renders a switcher.
47
- 3. Switcher selection writes `orgId` cookie and updates URL query (`?organizationId=...`).
48
- 4. Server components and API routes resolve org via URL > header > cookie.
49
- 5. Feedback queries always include `organizationId` constraint and org membership checks.
50
-
51
- ## Error Handling
52
- - Missing org: return 400 `MISSING_ORG_ID`.
53
- - Not a member: return 403 `FORBIDDEN`.
54
- - Feedback not in org: return 404.
55
- - Anonymous access to protected endpoints: return 401/403.
56
-
57
- ## Compatibility
58
- - Existing callers that pass `organizationId` continue to work.
59
- - New behavior is more strict; missing org will now fail with 400.
60
- - Portal routes remain public read-only with explicit org resolution.
61
-
62
- ## Implementation Scope
63
- - Add org context resolver and membership utilities.
64
- - Update all feedback APIs to use resolver.
65
- - Update feedback pages to consume resolver-derived org ID.
66
- - Add dashboard organization switcher (only on `/dashboard`).
67
- - Cookie handling (set on dashboard, read everywhere).
68
-
69
- ## Risks & Mitigations
70
- - **Default org on first load**: if cookie is missing, select the first org and persist it.
71
- - **Portal contamination**: portal routes should not read org cookie; rely on slug-derived org ID.
72
- - **Incomplete enforcement**: audit all feedback routes and service-layer queries.
73
-
74
- ## Testing
75
- - Unit tests for resolver precedence (URL > header > cookie) and membership check.
76
- - API tests for org isolation on list/detail endpoints.
77
- - Dashboard switcher test to verify cookie + URL update.
78
-
79
- ## Rollout
80
- 1. Ship resolver + API enforcement.
81
- 2. Ship dashboard switcher and default org behavior.
82
- 3. Add logging for missing org or forbidden access to validate adoption.