@nexttylabs/echo 0.5.0 → 0.7.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 (315) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/app/(auth)/login/page.tsx +1 -1
  3. package/app/(auth)/register/page.tsx +1 -1
  4. package/app/(auth)/sign-in/page.tsx +1 -1
  5. package/app/(dashboard)/admin/feedback/[id]/edit/page.tsx +13 -7
  6. package/app/(dashboard)/admin/feedback/[id]/page.tsx +1 -1
  7. package/app/(dashboard)/admin/feedback/new/page.tsx +20 -18
  8. package/app/(dashboard)/admin/feedback/page.tsx +1 -1
  9. package/app/(dashboard)/admin/layout.tsx +17 -7
  10. package/app/(dashboard)/analytics/portal/page.tsx +1 -1
  11. package/app/(dashboard)/dashboard/page.tsx +1 -1
  12. package/app/(dashboard)/layout.tsx +5 -3
  13. package/app/(dashboard)/no-access/page.tsx +1 -1
  14. package/app/(dashboard)/settings/access/page.tsx +1 -1
  15. package/app/(dashboard)/settings/api-keys/page.tsx +14 -4
  16. package/app/(dashboard)/settings/appearance/page.tsx +1 -1
  17. package/app/(dashboard)/settings/branding/page.tsx +1 -1
  18. package/app/(dashboard)/settings/changelog/page.tsx +1 -1
  19. package/app/(dashboard)/settings/danger-zone/page.tsx +1 -1
  20. package/app/(dashboard)/settings/feedback/page.tsx +1 -1
  21. package/app/(dashboard)/settings/integrations/page.tsx +1 -1
  22. package/app/(dashboard)/settings/layout.tsx +26 -3
  23. package/app/(dashboard)/settings/modules/page.tsx +1 -1
  24. package/app/(dashboard)/settings/notifications/page.tsx +1 -1
  25. package/app/(dashboard)/settings/organization/page.tsx +9 -10
  26. package/app/(dashboard)/settings/organization/portal/access/page.tsx +1 -1
  27. package/app/(dashboard)/settings/organization/portal/experience/page.tsx +1 -1
  28. package/app/(dashboard)/settings/organization/portal/growth/page.tsx +1 -1
  29. package/app/(dashboard)/settings/organization/portal/layout.tsx +1 -1
  30. package/app/(dashboard)/settings/organization/portal/page.tsx +1 -1
  31. package/app/(dashboard)/settings/organizations/[orgId]/members/page.tsx +1 -1
  32. package/app/(dashboard)/settings/organizations/new/page.tsx +1 -1
  33. package/app/(dashboard)/settings/page.tsx +1 -1
  34. package/app/(dashboard)/settings/portal-access/page.tsx +1 -1
  35. package/app/(dashboard)/settings/portal-branding/page.tsx +1 -1
  36. package/app/(dashboard)/settings/portal-growth/page.tsx +1 -1
  37. package/app/(dashboard)/settings/portal-modules/page.tsx +1 -1
  38. package/app/(dashboard)/settings/portal-resources/page.tsx +1 -1
  39. package/app/(dashboard)/settings/profile/page.tsx +1 -1
  40. package/app/(dashboard)/settings/widgets/page.tsx +1 -1
  41. package/app/(public)/[organizationSlug]/changelog/page.tsx +1 -1
  42. package/app/(public)/[organizationSlug]/feedback/[id]/page.tsx +1 -1
  43. package/app/(public)/[organizationSlug]/page.tsx +1 -1
  44. package/app/(public)/[organizationSlug]/roadmap/page.tsx +1 -1
  45. package/app/(public)/docs/page.tsx +1 -1
  46. package/app/(public)/feedback/[id]/not-found.tsx +1 -1
  47. package/app/(public)/feedback/[id]/page.tsx +1 -1
  48. package/app/(public)/invite/[token]/page.tsx +1 -1
  49. package/app/(public)/page.tsx +1 -1
  50. package/app/(public)/widget/[organizationId]/page.tsx +1 -1
  51. package/app/api/_utils.ts +1 -1
  52. package/app/api/admin/backup/route.ts +1 -1
  53. package/app/api/api-keys/[keyId]/route.ts +1 -1
  54. package/app/api/api-keys/route.ts +1 -1
  55. package/app/api/auth/[...all]/route.ts +1 -1
  56. package/app/api/auth/clear-session/route.ts +1 -1
  57. package/app/api/auth/register/handler.ts +1 -1
  58. package/app/api/auth/register/route.ts +1 -1
  59. package/app/api/docs/route.ts +1 -1
  60. package/app/api/feedback/[id]/comments/[commentId]/route.ts +14 -5
  61. package/app/api/feedback/[id]/comments/route.ts +1 -1
  62. package/app/api/feedback/[id]/duplicates/route.ts +1 -1
  63. package/app/api/feedback/[id]/handler.ts +1 -1
  64. package/app/api/feedback/[id]/processing-status/route.ts +1 -1
  65. package/app/api/feedback/[id]/reclassify/route.ts +5 -5
  66. package/app/api/feedback/[id]/route.ts +1 -1
  67. package/app/api/feedback/[id]/suggest-tags/route.ts +1 -1
  68. package/app/api/feedback/[id]/sync-github/route.ts +1 -1
  69. package/app/api/feedback/[id]/vote/route.ts +1 -1
  70. package/app/api/feedback/bulk/route.ts +1 -1
  71. package/app/api/feedback/handler.ts +1 -1
  72. package/app/api/feedback/route.ts +1 -1
  73. package/app/api/feedback/similar/route.ts +1 -1
  74. package/app/api/health/route.test.ts +1 -1
  75. package/app/api/health/route.ts +1 -1
  76. package/app/api/identify/jwt/route.ts +1 -1
  77. package/app/api/integrations/github/route.ts +1 -1
  78. package/app/api/invitations/accept/handler.ts +1 -1
  79. package/app/api/invitations/accept/route.ts +1 -1
  80. package/app/api/notifications/preferences/route.ts +1 -1
  81. package/app/api/organizations/[orgId]/handler.ts +1 -1
  82. package/app/api/organizations/[orgId]/invitations/handler.ts +1 -1
  83. package/app/api/organizations/[orgId]/invitations/route.ts +1 -1
  84. package/app/api/organizations/[orgId]/members/[memberId]/handler.ts +1 -1
  85. package/app/api/organizations/[orgId]/members/[memberId]/route.ts +1 -1
  86. package/app/api/organizations/[orgId]/members/handler.ts +1 -1
  87. package/app/api/organizations/[orgId]/members/route.ts +1 -1
  88. package/app/api/organizations/[orgId]/route.ts +1 -1
  89. package/app/api/organizations/handler.ts +3 -5
  90. package/app/api/organizations/route.ts +1 -1
  91. package/app/api/tags/sync/route.ts +1 -1
  92. package/app/api/upload/handler.ts +1 -1
  93. package/app/api/upload/route.ts +1 -1
  94. package/app/api/v1/feedback/[id]/route.ts +1 -1
  95. package/app/api/v1/feedback/route.ts +1 -1
  96. package/app/api/v1/spec/route.ts +1 -1
  97. package/app/api/webhooks/[webhookId]/route.ts +1 -1
  98. package/app/api/webhooks/github/route.ts +1 -1
  99. package/app/api/webhooks/route.ts +1 -1
  100. package/app/health/route.ts +1 -1
  101. package/app/layout.tsx +11 -3
  102. package/components/api/rate-limit-info.tsx +1 -1
  103. package/components/api-keys/api-key-manager.tsx +1 -1
  104. package/components/auth/login-form.tsx +1 -1
  105. package/components/auth/register-form.tsx +1 -1
  106. package/components/comment/comment-form.tsx +1 -1
  107. package/components/comment/internal-notes.tsx +1 -1
  108. package/components/comment/public-comments.tsx +1 -1
  109. package/components/component-example-client-only.tsx +1 -1
  110. package/components/component-example.tsx +1 -1
  111. package/components/dashboard/index.ts +1 -1
  112. package/components/dashboard/organization-switcher.tsx +1 -1
  113. package/components/dashboard/quick-actions.tsx +1 -1
  114. package/components/dashboard/recent-feedback-list.tsx +1 -1
  115. package/components/dashboard/stats-cards.tsx +1 -1
  116. package/components/dashboard/status-chart.tsx +1 -1
  117. package/components/example.tsx +1 -1
  118. package/components/feedback/attachment-list.tsx +1 -1
  119. package/components/feedback/auto-classification-badge.tsx +1 -1
  120. package/components/feedback/classification-override.tsx +1 -1
  121. package/components/feedback/duplicate-suggestions-inline.tsx +1 -1
  122. package/components/feedback/duplicate-suggestions.tsx +1 -1
  123. package/components/feedback/embedded-feedback-form.tsx +1 -1
  124. package/components/feedback/feedback-actions.tsx +1 -1
  125. package/components/feedback/feedback-bulk-actions.tsx +1 -1
  126. package/components/feedback/feedback-detail-view.tsx +1 -1
  127. package/components/feedback/feedback-detail.tsx +1 -1
  128. package/components/feedback/feedback-edit-form.tsx +1 -1
  129. package/components/feedback/feedback-filters.tsx +1 -1
  130. package/components/feedback/feedback-list-controls.tsx +1 -1
  131. package/components/feedback/feedback-list-item.tsx +1 -1
  132. package/components/feedback/feedback-list-skeleton.tsx +1 -1
  133. package/components/feedback/feedback-list.tsx +1 -1
  134. package/components/feedback/feedback-sorter.tsx +1 -1
  135. package/components/feedback/feedback-stats.tsx +1 -1
  136. package/components/feedback/file-upload.tsx +1 -1
  137. package/components/feedback/processing-status.tsx +1 -1
  138. package/components/feedback/status-history.tsx +1 -1
  139. package/components/feedback/status-selector.tsx +1 -1
  140. package/components/feedback/submit-on-behalf-form.tsx +1 -1
  141. package/components/feedback/tag-suggestions.tsx +1 -1
  142. package/components/feedback/vote-button.tsx +1 -1
  143. package/components/feedback/vote-list.tsx +1 -1
  144. package/components/integrations/github-config.tsx +1 -1
  145. package/components/landing/hero.tsx +1 -1
  146. package/components/layout/dashboard-layout.tsx +1 -1
  147. package/components/layout/index.ts +1 -1
  148. package/components/layout/language-switcher.tsx +1 -1
  149. package/components/layout/mobile-sidebar.tsx +1 -1
  150. package/components/layout/sidebar.tsx +1 -1
  151. package/components/portal/changelog-entry.tsx +1 -1
  152. package/components/portal/changelog-list.tsx +1 -1
  153. package/components/portal/contributor-badge.tsx +1 -1
  154. package/components/portal/contributors-sidebar.tsx +1 -1
  155. package/components/portal/create-post-dialog.tsx +1 -1
  156. package/components/portal/feedback-board.tsx +1 -1
  157. package/components/portal/feedback-post-card.tsx +1 -1
  158. package/components/portal/help-center.tsx +1 -1
  159. package/components/portal/leaderboard.tsx +1 -1
  160. package/components/portal/portal-header.tsx +1 -1
  161. package/components/portal/portal-layout.tsx +1 -1
  162. package/components/portal/portal-modules-panel.tsx +1 -1
  163. package/components/portal/portal-nav.tsx +1 -1
  164. package/components/portal/portal-overview.tsx +1 -1
  165. package/components/portal/portal-settings-nav.tsx +1 -1
  166. package/components/portal/portal-settings-shell.tsx +1 -1
  167. package/components/portal/portal-shell.tsx +1 -1
  168. package/components/portal/portal-tab-nav.tsx +1 -1
  169. package/components/portal/roadmap-board.tsx +1 -1
  170. package/components/portal/roadmap-card.tsx +1 -1
  171. package/components/portal/roadmap-column.tsx +1 -1
  172. package/components/portal/settings-forms/access-form.tsx +1 -1
  173. package/components/portal/settings-forms/copy-form.tsx +1 -1
  174. package/components/portal/settings-forms/index.ts +1 -1
  175. package/components/portal/settings-forms/languages-form.tsx +1 -1
  176. package/components/portal/settings-forms/seo-form.tsx +1 -1
  177. package/components/portal/settings-forms/sharing-form.tsx +1 -1
  178. package/components/portal/settings-forms/theme-form.tsx +1 -1
  179. package/components/settings/api-keys-list.tsx +1 -1
  180. package/components/settings/appearance-form.tsx +4 -6
  181. package/components/settings/index.ts +1 -1
  182. package/components/settings/invite-member-form.tsx +1 -1
  183. package/components/settings/notification-preferences.tsx +1 -1
  184. package/components/settings/organization-form.tsx +1 -1
  185. package/components/settings/organization-members-list.tsx +1 -1
  186. package/components/settings/profile-form.tsx +1 -1
  187. package/components/settings/role-selector.tsx +1 -1
  188. package/components/settings/settings-sidebar.tsx +5 -5
  189. package/components/shared/pagination.tsx +1 -1
  190. package/components/theme-provider.tsx +28 -0
  191. package/components/ui/alert-dialog.tsx +1 -1
  192. package/components/ui/alert.tsx +1 -1
  193. package/components/ui/avatar.tsx +1 -1
  194. package/components/ui/badge.tsx +1 -1
  195. package/components/ui/button.tsx +1 -1
  196. package/components/ui/card.tsx +1 -1
  197. package/components/ui/combobox.tsx +1 -1
  198. package/components/ui/dialog.tsx +1 -1
  199. package/components/ui/dropdown-menu.tsx +1 -1
  200. package/components/ui/field.tsx +1 -1
  201. package/components/ui/input-group.tsx +1 -1
  202. package/components/ui/input.tsx +1 -1
  203. package/components/ui/label.tsx +1 -1
  204. package/components/ui/pagination.tsx +1 -1
  205. package/components/ui/select.tsx +1 -1
  206. package/components/ui/separator.tsx +1 -1
  207. package/components/ui/sheet.tsx +1 -1
  208. package/components/ui/skeleton.tsx +1 -1
  209. package/components/ui/switch.tsx +1 -1
  210. package/components/ui/table.tsx +1 -1
  211. package/components/ui/tabs.tsx +1 -1
  212. package/components/ui/textarea.tsx +1 -1
  213. package/components/ui/tooltip.tsx +1 -1
  214. package/components/widget/widget-form.tsx +1 -1
  215. package/drizzle.config.ts +1 -1
  216. package/hooks/use-organization.tsx +116 -0
  217. package/hooks/use-permissions.ts +25 -12
  218. package/i18n/config.ts +1 -1
  219. package/i18n/request.ts +1 -1
  220. package/i18n/resolve-locale.ts +1 -1
  221. package/lib/api/errors.ts +1 -1
  222. package/lib/auth/cli-config.ts +1 -1
  223. package/lib/auth/client.ts +1 -1
  224. package/lib/auth/config.ts +1 -1
  225. package/lib/auth/jwt-identity.ts +1 -1
  226. package/lib/auth/org-context.ts +1 -1
  227. package/lib/auth/organization.ts +21 -1
  228. package/lib/auth/permissions.ts +11 -1
  229. package/lib/auth/session.ts +1 -1
  230. package/lib/config/rate-limits.ts +1 -1
  231. package/lib/dashboard/get-dashboard-stats.ts +1 -1
  232. package/lib/db/index.ts +1 -1
  233. package/lib/db/migrate.test.ts +1 -1
  234. package/lib/db/migrate.ts +1 -1
  235. package/lib/db/schema/ai-processing.ts +1 -1
  236. package/lib/db/schema/api-keys.ts +1 -1
  237. package/lib/db/schema/attachments.ts +1 -1
  238. package/lib/db/schema/auth.ts +1 -1
  239. package/lib/db/schema/comments.ts +1 -1
  240. package/lib/db/schema/duplicates.ts +1 -1
  241. package/lib/db/schema/feedback.ts +1 -1
  242. package/lib/db/schema/github-integrations.ts +1 -1
  243. package/lib/db/schema/index.ts +1 -1
  244. package/lib/db/schema/invitations.ts +1 -1
  245. package/lib/db/schema/notifications.ts +1 -1
  246. package/lib/db/schema/organization-members.ts +1 -1
  247. package/lib/db/schema/organization-settings.ts +1 -1
  248. package/lib/db/schema/organizations.ts +1 -1
  249. package/lib/db/schema/status-history.ts +1 -1
  250. package/lib/db/schema/tags.ts +1 -1
  251. package/lib/db/schema/votes.ts +1 -1
  252. package/lib/db/schema/webhooks.ts +1 -1
  253. package/lib/feedback/filters.ts +1 -1
  254. package/lib/feedback/find-similar.ts +1 -1
  255. package/lib/feedback/get-feedback-by-id.ts +1 -1
  256. package/lib/feedback/prefill.ts +1 -1
  257. package/lib/http/get-request-url.ts +1 -1
  258. package/lib/integrations/github.ts +1 -1
  259. package/lib/invitations.ts +1 -1
  260. package/lib/logger.test.ts +1 -1
  261. package/lib/logger.ts +1 -1
  262. package/lib/middleware/api-key.ts +1 -1
  263. package/lib/middleware/rate-limit-keys.ts +1 -1
  264. package/lib/middleware/rate-limit.ts +1 -1
  265. package/lib/middleware/rbac.ts +1 -1
  266. package/lib/middleware/request-id.test.ts +1 -1
  267. package/lib/middleware/request-id.ts +1 -1
  268. package/lib/middleware/request-logger.test.ts +1 -1
  269. package/lib/middleware/request-logger.ts +1 -1
  270. package/lib/middleware/with-rate-limit.ts +1 -1
  271. package/lib/portal/analytics.ts +1 -1
  272. package/lib/portal/contributors.ts +1 -1
  273. package/lib/portal/i18n.ts +1 -1
  274. package/lib/portal/leaderboard-settings.ts +1 -1
  275. package/lib/portal/modules.ts +1 -1
  276. package/lib/portal/portal-copy.ts +1 -1
  277. package/lib/portal/public-context.tsx +1 -1
  278. package/lib/portal/seo.ts +1 -1
  279. package/lib/portal/settings-context.ts +1 -1
  280. package/lib/portal/sharing.ts +1 -1
  281. package/lib/portal/sorting.ts +1 -1
  282. package/lib/portal/theme.ts +1 -1
  283. package/lib/services/ai/classifier.ts +1 -1
  284. package/lib/services/ai/duplicate-detector.ts +1 -1
  285. package/lib/services/ai/tag-suggester.ts +1 -1
  286. package/lib/services/api-keys.ts +1 -1
  287. package/lib/services/backup.ts +1 -1
  288. package/lib/services/email/templates.ts +1 -1
  289. package/lib/services/email.ts +1 -1
  290. package/lib/services/github-sync.ts +1 -1
  291. package/lib/services/notifications/index.ts +1 -1
  292. package/lib/services/portal-settings.ts +1 -1
  293. package/lib/swagger/config.ts +1 -1
  294. package/lib/swagger/generate.ts +1 -1
  295. package/lib/upload/file-validator.ts +1 -1
  296. package/lib/upload/storage.ts +1 -1
  297. package/lib/utils/format.ts +1 -1
  298. package/lib/utils/slug.ts +1 -1
  299. package/lib/utils.ts +1 -1
  300. package/lib/validations/auth.ts +1 -1
  301. package/lib/validations/comment.ts +1 -1
  302. package/lib/validations/feedback.ts +1 -1
  303. package/lib/validations/invitations.ts +1 -1
  304. package/lib/validations/organizations.ts +1 -1
  305. package/lib/validators/feedback.ts +1 -1
  306. package/lib/validators/index.ts +1 -1
  307. package/lib/webhooks/events.ts +1 -1
  308. package/lib/webhooks/index.ts +1 -1
  309. package/lib/webhooks/retry.ts +1 -1
  310. package/lib/webhooks/sender.ts +1 -1
  311. package/lib/webhooks/verify.ts +1 -1
  312. package/lib/workers/feedback-processor.ts +1 -1
  313. package/next.config.ts +1 -1
  314. package/package.json +2 -1
  315. package/types/bun-test.d.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @nexttylabs/echo
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - fc3d8f1: support light/dark theme
8
+ - cc04d9f: Use the organization member role uniformly.
9
+ - 5cfdeb7: feat: support editing user profile
10
+
11
+ ### Patch Changes
12
+
13
+ - 7dba561: remove legacy changeset files
14
+ - 1e798f1: remove unuse tables and published files
15
+ - e3c4e1c: fix workflow errors
16
+ - daf9abb: first release
17
+
18
+ ## 0.6.0
19
+
20
+ ### Minor Changes
21
+
22
+ - cc04d9f: Use the organization member role uniformly.
23
+ - 5cfdeb7: feat: support editing user profile
24
+
25
+ ### Patch Changes
26
+
27
+ - 7dba561: remove legacy changeset files
28
+ - 1e798f1: remove unuse tables and published files
29
+ - e3c4e1c: fix workflow errors
30
+ - daf9abb: first release
31
+
3
32
  ## 0.5.0
4
33
 
5
34
  ### Minor Changes
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -22,7 +22,7 @@ import { auth } from "@/lib/auth/config";
22
22
  import { db } from "@/lib/db";
23
23
  import { feedback } from "@/lib/db/schema";
24
24
  import { FeedbackEditForm } from "@/components/feedback/feedback-edit-form";
25
- import { canEditFeedback, type UserRole } from "@/lib/auth/permissions";
25
+ import { canEditFeedback } from "@/lib/auth/permissions";
26
26
  import { getOrgContext } from "@/lib/auth/org-context";
27
27
  import { getRequestUrl } from "@/lib/http/get-request-url";
28
28
 
@@ -39,11 +39,6 @@ export default async function FeedbackEditPage({ params }: PageProps) {
39
39
  redirect("/login");
40
40
  }
41
41
 
42
- const userRole = (session.user as { role?: string }).role as UserRole | undefined;
43
- if (!userRole || !canEditFeedback(userRole)) {
44
- redirect("/admin/feedback");
45
- }
46
-
47
42
  const { id } = await params;
48
43
  const feedbackId = parseInt(id);
49
44
 
@@ -55,6 +50,7 @@ export default async function FeedbackEditPage({ params }: PageProps) {
55
50
  throw new Error("Database not configured");
56
51
  }
57
52
 
53
+ // Get organization context first
58
54
  let organizationId: string | null = null;
59
55
  try {
60
56
  const url = getRequestUrl(
@@ -72,6 +68,16 @@ export default async function FeedbackEditPage({ params }: PageProps) {
72
68
  notFound();
73
69
  }
74
70
 
71
+ // Get user role from organization membership
72
+ const { getUserRoleInOrganization } = await import("@/lib/auth/organization");
73
+ const userRole = organizationId
74
+ ? await getUserRoleInOrganization(db, session.user.id, organizationId)
75
+ : null;
76
+
77
+ if (!userRole || !canEditFeedback(userRole)) {
78
+ redirect("/admin/feedback");
79
+ }
80
+
75
81
  const [row] = await db
76
82
  .select({
77
83
  title: feedback.title,
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -30,17 +30,21 @@ export default async function NewFeedbackPage() {
30
30
  redirect("/login");
31
31
  }
32
32
 
33
- const userRole = ((session.user as { role?: string }).role ?? "customer") as UserRole;
34
- const hasSubmitOnBehalfPermission = canSubmitOnBehalf(userRole);
33
+ if (!db) {
34
+ throw new Error("Database not configured");
35
+ }
35
36
 
36
- if (!hasSubmitOnBehalfPermission) {
37
+ // Get user's organization first (which includes their role)
38
+ const organization = await getUserOrganization(db, session.user.id);
39
+
40
+ if (!organization) {
37
41
  return (
38
42
  <div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-slate-100 px-4 py-12">
39
43
  <div className="mx-auto flex w-full max-w-2xl flex-col gap-6">
40
- <div className="rounded-lg border border-red-200 bg-red-50 p-6">
41
- <h1 className="text-xl font-semibold text-red-800">权限不足</h1>
42
- <p className="mt-2 text-sm text-red-600">
43
- 您没有代客户提交反馈的权限。请联系管理员获取相应权限。
44
+ <div className="rounded-lg border border-amber-200 bg-amber-50 p-6">
45
+ <h1 className="text-xl font-semibold text-amber-800">未找到组织</h1>
46
+ <p className="mt-2 text-sm text-amber-700">
47
+ 请先加入组织后再代客户提交反馈。
44
48
  </p>
45
49
  </div>
46
50
  </div>
@@ -48,20 +52,18 @@ export default async function NewFeedbackPage() {
48
52
  );
49
53
  }
50
54
 
51
- if (!db) {
52
- throw new Error("Database not configured");
53
- }
54
-
55
- const organization = await getUserOrganization(db, session.user.id);
55
+ // Get user role from organization membership
56
+ const userRole = (organization.role as UserRole) || "customer";
57
+ const hasSubmitOnBehalfPermission = canSubmitOnBehalf(userRole);
56
58
 
57
- if (!organization) {
59
+ if (!hasSubmitOnBehalfPermission) {
58
60
  return (
59
61
  <div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-slate-100 px-4 py-12">
60
62
  <div className="mx-auto flex w-full max-w-2xl flex-col gap-6">
61
- <div className="rounded-lg border border-amber-200 bg-amber-50 p-6">
62
- <h1 className="text-xl font-semibold text-amber-800">未找到组织</h1>
63
- <p className="mt-2 text-sm text-amber-700">
64
- 请先加入组织后再代客户提交反馈。
63
+ <div className="rounded-lg border border-red-200 bg-red-50 p-6">
64
+ <h1 className="text-xl font-semibold text-red-800">权限不足</h1>
65
+ <p className="mt-2 text-sm text-red-600">
66
+ 您没有代客户提交反馈的权限。请联系管理员获取相应权限。
65
67
  </p>
66
68
  </div>
67
69
  </div>
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -15,10 +15,11 @@
15
15
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
16
  */
17
17
 
18
- import { headers } from "next/headers";
18
+ import { cookies, headers } from "next/headers";
19
19
  import { redirect } from "next/navigation";
20
20
  import { auth } from "@/lib/auth/config";
21
- import type { UserRole } from "@/lib/auth/permissions";
21
+ import { db } from "@/lib/db";
22
+ import { getUserRoleInOrganization } from "@/lib/auth/organization";
22
23
 
23
24
  export default async function AdminLayout({
24
25
  children,
@@ -36,13 +37,22 @@ export default async function AdminLayout({
36
37
  redirect("/login");
37
38
  }
38
39
 
39
- const role = (session.user as { role?: string }).role as
40
- | UserRole
41
- | undefined;
40
+ // Get current organization ID from cookie
41
+ const cookieStore = await cookies();
42
+ const currentOrgId = cookieStore.get("orgId")?.value;
42
43
 
43
- if (role !== "admin") {
44
+ if (!db || !currentOrgId) {
45
+ redirect("/no-access");
46
+ }
47
+
48
+ // Get user's role in the current organization
49
+ const role = await getUserRoleInOrganization(db, session.user.id, currentOrgId);
50
+
51
+ // Only admin or owner can access admin pages
52
+ if (role !== "admin" && role !== "owner" && role !== "product_manager") {
44
53
  redirect("/no-access");
45
54
  }
46
55
 
47
56
  return <>{children}</>;
48
57
  }
58
+
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -39,8 +39,6 @@ export default async function DashboardRootLayout({
39
39
  redirect("/login");
40
40
  }
41
41
 
42
- const userRole = (session.user as { role?: string }).role as UserRole || "customer";
43
-
44
42
  // Fetch organizations
45
43
  let organizations: Array<{ id: string; name: string; slug: string; role: string }> = [];
46
44
  let currentOrgId: string | null = null;
@@ -52,6 +50,10 @@ export default async function DashboardRootLayout({
52
50
  currentOrgId = cookieOrgId || organizations[0]?.id || null;
53
51
  }
54
52
 
53
+ // Get user role from current organization membership
54
+ const currentOrg = organizations.find((org) => org.id === currentOrgId);
55
+ const userRole = (currentOrg?.role as UserRole) || "customer";
56
+
55
57
  return (
56
58
  <DashboardLayout
57
59
  user={{
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -15,11 +15,13 @@
15
15
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
16
  */
17
17
 
18
- import { headers } from "next/headers";
18
+ import { cookies, headers } from "next/headers";
19
19
  import { redirect } from "next/navigation";
20
20
  import { getTranslations } from "next-intl/server";
21
21
  import { auth } from "@/lib/auth/config";
22
+ import { db } from "@/lib/db";
22
23
  import { ApiKeysList } from "@/components/settings/api-keys-list";
24
+ import { getUserRoleInOrganization } from "@/lib/auth/organization";
23
25
  import type { UserRole } from "@/lib/auth/permissions";
24
26
 
25
27
  export async function generateMetadata() {
@@ -37,9 +39,17 @@ export default async function ApiKeysSettingsPage() {
37
39
  redirect("/login");
38
40
  }
39
41
 
40
- const userRole = (session.user as { role?: string }).role as UserRole || "customer";
42
+ // Get user role from current organization
43
+ const cookieStore = await cookies();
44
+ const currentOrgId = cookieStore.get("orgId")?.value;
41
45
 
42
- if (userRole !== "admin" && userRole !== "product_manager") {
46
+ let userRole: UserRole = "customer";
47
+ if (db && currentOrgId) {
48
+ const role = await getUserRoleInOrganization(db, session.user.id, currentOrgId);
49
+ userRole = role || "customer";
50
+ }
51
+
52
+ if (userRole !== "owner" && userRole !== "admin" && userRole !== "product_manager") {
43
53
  redirect("/settings/profile");
44
54
  }
45
55
 
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -15,10 +15,12 @@
15
15
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
16
  */
17
17
 
18
- import { headers } from "next/headers";
18
+ import { cookies, headers } from "next/headers";
19
19
  import { redirect } from "next/navigation";
20
20
  import { auth } from "@/lib/auth/config";
21
+ import { db } from "@/lib/db";
21
22
  import { SettingsSidebar } from "@/components/settings";
23
+ import { getUserOrganizations } from "@/lib/auth/organization";
22
24
  import type { UserRole } from "@/lib/auth/permissions";
23
25
 
24
26
  export default async function SettingsLayout({
@@ -32,7 +34,27 @@ export default async function SettingsLayout({
32
34
  redirect("/login");
33
35
  }
34
36
 
35
- const userRole = (session.user as { role?: string }).role as UserRole || "customer";
37
+ // Get user role from current organization (same logic as dashboard layout)
38
+ let userRole: UserRole = "customer";
39
+
40
+ if (db) {
41
+ const organizations = await getUserOrganizations(db, session.user.id);
42
+ const cookieStore = await cookies();
43
+ const cookieOrgId = cookieStore.get("orgId")?.value ?? null;
44
+
45
+ // Check if the cookie org exists in user's organizations
46
+ // If not, fall back to the first available organization
47
+ let currentOrg = cookieOrgId
48
+ ? organizations.find((org) => org.id === cookieOrgId)
49
+ : null;
50
+
51
+ // Fallback to first org if cookie org is not found (stale cookie)
52
+ if (!currentOrg && organizations.length > 0) {
53
+ currentOrg = organizations[0];
54
+ }
55
+
56
+ userRole = (currentOrg?.role as UserRole) || "customer";
57
+ }
36
58
 
37
59
  return (
38
60
  <div className="flex min-h-[calc(100vh-3.5rem)]">
@@ -41,3 +63,4 @@ export default async function SettingsLayout({
41
63
  </div>
42
64
  );
43
65
  }
66
+
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -38,30 +38,29 @@ export default async function OrganizationSettingsPage() {
38
38
  redirect("/login");
39
39
  }
40
40
 
41
- const userRole = (session.user as { role?: string }).role as UserRole || "customer";
42
-
43
- if (userRole !== "admin") {
44
- redirect("/settings/profile");
45
- }
46
-
47
41
  if (!db) {
48
42
  throw new Error("Database connection not available");
49
43
  }
50
44
 
51
- // Get all user organizations
45
+ // Get all user organizations and find current one
52
46
  const organizations = await getUserOrganizations(db, session.user.id);
53
47
 
54
48
  if (organizations.length === 0) {
55
49
  redirect("/settings/organizations/new");
56
50
  }
57
51
 
58
- // Get current organization from cookie (same logic as dashboard layout)
52
+ // Get current organization from cookie
59
53
  const cookieStore = await cookies();
60
54
  const cookieOrgId = cookieStore.get("orgId")?.value ?? null;
61
55
  const currentOrgId = cookieOrgId || organizations[0]?.id || null;
62
56
 
63
- // Find the current organization
57
+ // Find the current organization and get role from it
64
58
  const organization = organizations.find(org => org.id === currentOrgId) || organizations[0];
59
+ const userRole = (organization?.role as UserRole) || "customer";
60
+
61
+ if (userRole !== "owner" && userRole !== "admin") {
62
+ redirect("/settings/profile");
63
+ }
65
64
 
66
65
  if (!organization) {
67
66
  redirect("/settings/organizations/new");
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Copyright (c) 2026 Echo Team
2
+ * Copyright (c) 2026 Nexttylabs Team
3
3
  *
4
4
  * This program is free software: you can redistribute it and/or modify
5
5
  * it under the terms of the GNU Affero General Public License as published by