@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.
- package/CHANGELOG.md +29 -0
- package/app/(auth)/login/page.tsx +1 -1
- package/app/(auth)/register/page.tsx +1 -1
- package/app/(auth)/sign-in/page.tsx +1 -1
- package/app/(dashboard)/admin/feedback/[id]/edit/page.tsx +13 -7
- package/app/(dashboard)/admin/feedback/[id]/page.tsx +1 -1
- package/app/(dashboard)/admin/feedback/new/page.tsx +20 -18
- package/app/(dashboard)/admin/feedback/page.tsx +1 -1
- package/app/(dashboard)/admin/layout.tsx +17 -7
- package/app/(dashboard)/analytics/portal/page.tsx +1 -1
- package/app/(dashboard)/dashboard/page.tsx +1 -1
- package/app/(dashboard)/layout.tsx +5 -3
- package/app/(dashboard)/no-access/page.tsx +1 -1
- package/app/(dashboard)/settings/access/page.tsx +1 -1
- package/app/(dashboard)/settings/api-keys/page.tsx +14 -4
- package/app/(dashboard)/settings/appearance/page.tsx +1 -1
- package/app/(dashboard)/settings/branding/page.tsx +1 -1
- package/app/(dashboard)/settings/changelog/page.tsx +1 -1
- package/app/(dashboard)/settings/danger-zone/page.tsx +1 -1
- package/app/(dashboard)/settings/feedback/page.tsx +1 -1
- package/app/(dashboard)/settings/integrations/page.tsx +1 -1
- package/app/(dashboard)/settings/layout.tsx +26 -3
- package/app/(dashboard)/settings/modules/page.tsx +1 -1
- package/app/(dashboard)/settings/notifications/page.tsx +1 -1
- package/app/(dashboard)/settings/organization/page.tsx +9 -10
- package/app/(dashboard)/settings/organization/portal/access/page.tsx +1 -1
- package/app/(dashboard)/settings/organization/portal/experience/page.tsx +1 -1
- package/app/(dashboard)/settings/organization/portal/growth/page.tsx +1 -1
- package/app/(dashboard)/settings/organization/portal/layout.tsx +1 -1
- package/app/(dashboard)/settings/organization/portal/page.tsx +1 -1
- package/app/(dashboard)/settings/organizations/[orgId]/members/page.tsx +1 -1
- package/app/(dashboard)/settings/organizations/new/page.tsx +1 -1
- package/app/(dashboard)/settings/page.tsx +1 -1
- package/app/(dashboard)/settings/portal-access/page.tsx +1 -1
- package/app/(dashboard)/settings/portal-branding/page.tsx +1 -1
- package/app/(dashboard)/settings/portal-growth/page.tsx +1 -1
- package/app/(dashboard)/settings/portal-modules/page.tsx +1 -1
- package/app/(dashboard)/settings/portal-resources/page.tsx +1 -1
- package/app/(dashboard)/settings/profile/page.tsx +1 -1
- package/app/(dashboard)/settings/widgets/page.tsx +1 -1
- package/app/(public)/[organizationSlug]/changelog/page.tsx +1 -1
- package/app/(public)/[organizationSlug]/feedback/[id]/page.tsx +1 -1
- package/app/(public)/[organizationSlug]/page.tsx +1 -1
- package/app/(public)/[organizationSlug]/roadmap/page.tsx +1 -1
- package/app/(public)/docs/page.tsx +1 -1
- package/app/(public)/feedback/[id]/not-found.tsx +1 -1
- package/app/(public)/feedback/[id]/page.tsx +1 -1
- package/app/(public)/invite/[token]/page.tsx +1 -1
- package/app/(public)/page.tsx +1 -1
- package/app/(public)/widget/[organizationId]/page.tsx +1 -1
- package/app/api/_utils.ts +1 -1
- package/app/api/admin/backup/route.ts +1 -1
- package/app/api/api-keys/[keyId]/route.ts +1 -1
- package/app/api/api-keys/route.ts +1 -1
- package/app/api/auth/[...all]/route.ts +1 -1
- package/app/api/auth/clear-session/route.ts +1 -1
- package/app/api/auth/register/handler.ts +1 -1
- package/app/api/auth/register/route.ts +1 -1
- package/app/api/docs/route.ts +1 -1
- package/app/api/feedback/[id]/comments/[commentId]/route.ts +14 -5
- package/app/api/feedback/[id]/comments/route.ts +1 -1
- package/app/api/feedback/[id]/duplicates/route.ts +1 -1
- package/app/api/feedback/[id]/handler.ts +1 -1
- package/app/api/feedback/[id]/processing-status/route.ts +1 -1
- package/app/api/feedback/[id]/reclassify/route.ts +5 -5
- package/app/api/feedback/[id]/route.ts +1 -1
- package/app/api/feedback/[id]/suggest-tags/route.ts +1 -1
- package/app/api/feedback/[id]/sync-github/route.ts +1 -1
- package/app/api/feedback/[id]/vote/route.ts +1 -1
- package/app/api/feedback/bulk/route.ts +1 -1
- package/app/api/feedback/handler.ts +1 -1
- package/app/api/feedback/route.ts +1 -1
- package/app/api/feedback/similar/route.ts +1 -1
- package/app/api/health/route.test.ts +1 -1
- package/app/api/health/route.ts +1 -1
- package/app/api/identify/jwt/route.ts +1 -1
- package/app/api/integrations/github/route.ts +1 -1
- package/app/api/invitations/accept/handler.ts +1 -1
- package/app/api/invitations/accept/route.ts +1 -1
- package/app/api/notifications/preferences/route.ts +1 -1
- package/app/api/organizations/[orgId]/handler.ts +1 -1
- package/app/api/organizations/[orgId]/invitations/handler.ts +1 -1
- package/app/api/organizations/[orgId]/invitations/route.ts +1 -1
- package/app/api/organizations/[orgId]/members/[memberId]/handler.ts +1 -1
- package/app/api/organizations/[orgId]/members/[memberId]/route.ts +1 -1
- package/app/api/organizations/[orgId]/members/handler.ts +1 -1
- package/app/api/organizations/[orgId]/members/route.ts +1 -1
- package/app/api/organizations/[orgId]/route.ts +1 -1
- package/app/api/organizations/handler.ts +3 -5
- package/app/api/organizations/route.ts +1 -1
- package/app/api/tags/sync/route.ts +1 -1
- package/app/api/upload/handler.ts +1 -1
- package/app/api/upload/route.ts +1 -1
- package/app/api/v1/feedback/[id]/route.ts +1 -1
- package/app/api/v1/feedback/route.ts +1 -1
- package/app/api/v1/spec/route.ts +1 -1
- package/app/api/webhooks/[webhookId]/route.ts +1 -1
- package/app/api/webhooks/github/route.ts +1 -1
- package/app/api/webhooks/route.ts +1 -1
- package/app/health/route.ts +1 -1
- package/app/layout.tsx +11 -3
- package/components/api/rate-limit-info.tsx +1 -1
- package/components/api-keys/api-key-manager.tsx +1 -1
- package/components/auth/login-form.tsx +1 -1
- package/components/auth/register-form.tsx +1 -1
- package/components/comment/comment-form.tsx +1 -1
- package/components/comment/internal-notes.tsx +1 -1
- package/components/comment/public-comments.tsx +1 -1
- package/components/component-example-client-only.tsx +1 -1
- package/components/component-example.tsx +1 -1
- package/components/dashboard/index.ts +1 -1
- package/components/dashboard/organization-switcher.tsx +1 -1
- package/components/dashboard/quick-actions.tsx +1 -1
- package/components/dashboard/recent-feedback-list.tsx +1 -1
- package/components/dashboard/stats-cards.tsx +1 -1
- package/components/dashboard/status-chart.tsx +1 -1
- package/components/example.tsx +1 -1
- package/components/feedback/attachment-list.tsx +1 -1
- package/components/feedback/auto-classification-badge.tsx +1 -1
- package/components/feedback/classification-override.tsx +1 -1
- package/components/feedback/duplicate-suggestions-inline.tsx +1 -1
- package/components/feedback/duplicate-suggestions.tsx +1 -1
- package/components/feedback/embedded-feedback-form.tsx +1 -1
- package/components/feedback/feedback-actions.tsx +1 -1
- package/components/feedback/feedback-bulk-actions.tsx +1 -1
- package/components/feedback/feedback-detail-view.tsx +1 -1
- package/components/feedback/feedback-detail.tsx +1 -1
- package/components/feedback/feedback-edit-form.tsx +1 -1
- package/components/feedback/feedback-filters.tsx +1 -1
- package/components/feedback/feedback-list-controls.tsx +1 -1
- package/components/feedback/feedback-list-item.tsx +1 -1
- package/components/feedback/feedback-list-skeleton.tsx +1 -1
- package/components/feedback/feedback-list.tsx +1 -1
- package/components/feedback/feedback-sorter.tsx +1 -1
- package/components/feedback/feedback-stats.tsx +1 -1
- package/components/feedback/file-upload.tsx +1 -1
- package/components/feedback/processing-status.tsx +1 -1
- package/components/feedback/status-history.tsx +1 -1
- package/components/feedback/status-selector.tsx +1 -1
- package/components/feedback/submit-on-behalf-form.tsx +1 -1
- package/components/feedback/tag-suggestions.tsx +1 -1
- package/components/feedback/vote-button.tsx +1 -1
- package/components/feedback/vote-list.tsx +1 -1
- package/components/integrations/github-config.tsx +1 -1
- package/components/landing/hero.tsx +1 -1
- package/components/layout/dashboard-layout.tsx +1 -1
- package/components/layout/index.ts +1 -1
- package/components/layout/language-switcher.tsx +1 -1
- package/components/layout/mobile-sidebar.tsx +1 -1
- package/components/layout/sidebar.tsx +1 -1
- package/components/portal/changelog-entry.tsx +1 -1
- package/components/portal/changelog-list.tsx +1 -1
- package/components/portal/contributor-badge.tsx +1 -1
- package/components/portal/contributors-sidebar.tsx +1 -1
- package/components/portal/create-post-dialog.tsx +1 -1
- package/components/portal/feedback-board.tsx +1 -1
- package/components/portal/feedback-post-card.tsx +1 -1
- package/components/portal/help-center.tsx +1 -1
- package/components/portal/leaderboard.tsx +1 -1
- package/components/portal/portal-header.tsx +1 -1
- package/components/portal/portal-layout.tsx +1 -1
- package/components/portal/portal-modules-panel.tsx +1 -1
- package/components/portal/portal-nav.tsx +1 -1
- package/components/portal/portal-overview.tsx +1 -1
- package/components/portal/portal-settings-nav.tsx +1 -1
- package/components/portal/portal-settings-shell.tsx +1 -1
- package/components/portal/portal-shell.tsx +1 -1
- package/components/portal/portal-tab-nav.tsx +1 -1
- package/components/portal/roadmap-board.tsx +1 -1
- package/components/portal/roadmap-card.tsx +1 -1
- package/components/portal/roadmap-column.tsx +1 -1
- package/components/portal/settings-forms/access-form.tsx +1 -1
- package/components/portal/settings-forms/copy-form.tsx +1 -1
- package/components/portal/settings-forms/index.ts +1 -1
- package/components/portal/settings-forms/languages-form.tsx +1 -1
- package/components/portal/settings-forms/seo-form.tsx +1 -1
- package/components/portal/settings-forms/sharing-form.tsx +1 -1
- package/components/portal/settings-forms/theme-form.tsx +1 -1
- package/components/settings/api-keys-list.tsx +1 -1
- package/components/settings/appearance-form.tsx +4 -6
- package/components/settings/index.ts +1 -1
- package/components/settings/invite-member-form.tsx +1 -1
- package/components/settings/notification-preferences.tsx +1 -1
- package/components/settings/organization-form.tsx +1 -1
- package/components/settings/organization-members-list.tsx +1 -1
- package/components/settings/profile-form.tsx +1 -1
- package/components/settings/role-selector.tsx +1 -1
- package/components/settings/settings-sidebar.tsx +5 -5
- package/components/shared/pagination.tsx +1 -1
- package/components/theme-provider.tsx +28 -0
- package/components/ui/alert-dialog.tsx +1 -1
- package/components/ui/alert.tsx +1 -1
- package/components/ui/avatar.tsx +1 -1
- package/components/ui/badge.tsx +1 -1
- package/components/ui/button.tsx +1 -1
- package/components/ui/card.tsx +1 -1
- package/components/ui/combobox.tsx +1 -1
- package/components/ui/dialog.tsx +1 -1
- package/components/ui/dropdown-menu.tsx +1 -1
- package/components/ui/field.tsx +1 -1
- package/components/ui/input-group.tsx +1 -1
- package/components/ui/input.tsx +1 -1
- package/components/ui/label.tsx +1 -1
- package/components/ui/pagination.tsx +1 -1
- package/components/ui/select.tsx +1 -1
- package/components/ui/separator.tsx +1 -1
- package/components/ui/sheet.tsx +1 -1
- package/components/ui/skeleton.tsx +1 -1
- package/components/ui/switch.tsx +1 -1
- package/components/ui/table.tsx +1 -1
- package/components/ui/tabs.tsx +1 -1
- package/components/ui/textarea.tsx +1 -1
- package/components/ui/tooltip.tsx +1 -1
- package/components/widget/widget-form.tsx +1 -1
- package/drizzle.config.ts +1 -1
- package/hooks/use-organization.tsx +116 -0
- package/hooks/use-permissions.ts +25 -12
- package/i18n/config.ts +1 -1
- package/i18n/request.ts +1 -1
- package/i18n/resolve-locale.ts +1 -1
- package/lib/api/errors.ts +1 -1
- package/lib/auth/cli-config.ts +1 -1
- package/lib/auth/client.ts +1 -1
- package/lib/auth/config.ts +1 -1
- package/lib/auth/jwt-identity.ts +1 -1
- package/lib/auth/org-context.ts +1 -1
- package/lib/auth/organization.ts +21 -1
- package/lib/auth/permissions.ts +11 -1
- package/lib/auth/session.ts +1 -1
- package/lib/config/rate-limits.ts +1 -1
- package/lib/dashboard/get-dashboard-stats.ts +1 -1
- package/lib/db/index.ts +1 -1
- package/lib/db/migrate.test.ts +1 -1
- package/lib/db/migrate.ts +1 -1
- package/lib/db/schema/ai-processing.ts +1 -1
- package/lib/db/schema/api-keys.ts +1 -1
- package/lib/db/schema/attachments.ts +1 -1
- package/lib/db/schema/auth.ts +1 -1
- package/lib/db/schema/comments.ts +1 -1
- package/lib/db/schema/duplicates.ts +1 -1
- package/lib/db/schema/feedback.ts +1 -1
- package/lib/db/schema/github-integrations.ts +1 -1
- package/lib/db/schema/index.ts +1 -1
- package/lib/db/schema/invitations.ts +1 -1
- package/lib/db/schema/notifications.ts +1 -1
- package/lib/db/schema/organization-members.ts +1 -1
- package/lib/db/schema/organization-settings.ts +1 -1
- package/lib/db/schema/organizations.ts +1 -1
- package/lib/db/schema/status-history.ts +1 -1
- package/lib/db/schema/tags.ts +1 -1
- package/lib/db/schema/votes.ts +1 -1
- package/lib/db/schema/webhooks.ts +1 -1
- package/lib/feedback/filters.ts +1 -1
- package/lib/feedback/find-similar.ts +1 -1
- package/lib/feedback/get-feedback-by-id.ts +1 -1
- package/lib/feedback/prefill.ts +1 -1
- package/lib/http/get-request-url.ts +1 -1
- package/lib/integrations/github.ts +1 -1
- package/lib/invitations.ts +1 -1
- package/lib/logger.test.ts +1 -1
- package/lib/logger.ts +1 -1
- package/lib/middleware/api-key.ts +1 -1
- package/lib/middleware/rate-limit-keys.ts +1 -1
- package/lib/middleware/rate-limit.ts +1 -1
- package/lib/middleware/rbac.ts +1 -1
- package/lib/middleware/request-id.test.ts +1 -1
- package/lib/middleware/request-id.ts +1 -1
- package/lib/middleware/request-logger.test.ts +1 -1
- package/lib/middleware/request-logger.ts +1 -1
- package/lib/middleware/with-rate-limit.ts +1 -1
- package/lib/portal/analytics.ts +1 -1
- package/lib/portal/contributors.ts +1 -1
- package/lib/portal/i18n.ts +1 -1
- package/lib/portal/leaderboard-settings.ts +1 -1
- package/lib/portal/modules.ts +1 -1
- package/lib/portal/portal-copy.ts +1 -1
- package/lib/portal/public-context.tsx +1 -1
- package/lib/portal/seo.ts +1 -1
- package/lib/portal/settings-context.ts +1 -1
- package/lib/portal/sharing.ts +1 -1
- package/lib/portal/sorting.ts +1 -1
- package/lib/portal/theme.ts +1 -1
- package/lib/services/ai/classifier.ts +1 -1
- package/lib/services/ai/duplicate-detector.ts +1 -1
- package/lib/services/ai/tag-suggester.ts +1 -1
- package/lib/services/api-keys.ts +1 -1
- package/lib/services/backup.ts +1 -1
- package/lib/services/email/templates.ts +1 -1
- package/lib/services/email.ts +1 -1
- package/lib/services/github-sync.ts +1 -1
- package/lib/services/notifications/index.ts +1 -1
- package/lib/services/portal-settings.ts +1 -1
- package/lib/swagger/config.ts +1 -1
- package/lib/swagger/generate.ts +1 -1
- package/lib/upload/file-validator.ts +1 -1
- package/lib/upload/storage.ts +1 -1
- package/lib/utils/format.ts +1 -1
- package/lib/utils/slug.ts +1 -1
- package/lib/utils.ts +1 -1
- package/lib/validations/auth.ts +1 -1
- package/lib/validations/comment.ts +1 -1
- package/lib/validations/feedback.ts +1 -1
- package/lib/validations/invitations.ts +1 -1
- package/lib/validations/organizations.ts +1 -1
- package/lib/validators/feedback.ts +1 -1
- package/lib/validators/index.ts +1 -1
- package/lib/webhooks/events.ts +1 -1
- package/lib/webhooks/index.ts +1 -1
- package/lib/webhooks/retry.ts +1 -1
- package/lib/webhooks/sender.ts +1 -1
- package/lib/webhooks/verify.ts +1 -1
- package/lib/workers/feedback-processor.ts +1 -1
- package/next.config.ts +1 -1
- package/package.json +2 -1
- 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
|
|
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
|
|
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
|
|
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
|
-
|
|
34
|
-
|
|
33
|
+
if (!db) {
|
|
34
|
+
throw new Error("Database not configured");
|
|
35
|
+
}
|
|
35
36
|
|
|
36
|
-
|
|
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-
|
|
41
|
-
<h1 className="text-xl font-semibold text-
|
|
42
|
-
<p className="mt-2 text-sm text-
|
|
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
|
-
|
|
52
|
-
|
|
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 (!
|
|
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-
|
|
62
|
-
<h1 className="text-xl font-semibold text-
|
|
63
|
-
<p className="mt-2 text-sm text-
|
|
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
|
|
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
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
// Get current organization ID from cookie
|
|
41
|
+
const cookieStore = await cookies();
|
|
42
|
+
const currentOrgId = cookieStore.get("orgId")?.value;
|
|
42
43
|
|
|
43
|
-
if (
|
|
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
|
|
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
|
|
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
|
-
|
|
42
|
+
// Get user role from current organization
|
|
43
|
+
const cookieStore = await cookies();
|
|
44
|
+
const currentOrgId = cookieStore.get("orgId")?.value;
|
|
41
45
|
|
|
42
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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");
|