@nexttylabs/echo 0.11.0 → 0.13.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 +36 -0
- package/app/(auth)/login/page.tsx +3 -3
- package/app/(auth)/register/page.tsx +3 -3
- package/app/(dashboard)/admin/feedback/new/page.tsx +11 -11
- package/app/(dashboard)/settings/branding/page.tsx +2 -2
- package/app/(dashboard)/settings/changelog/page.tsx +2 -2
- package/app/(dashboard)/settings/feedback/page.tsx +4 -4
- package/app/(dashboard)/settings/integrations/page.tsx +9 -9
- package/app/(dashboard)/settings/modules/page.tsx +1 -1
- package/app/(dashboard)/settings/organizations/[orgId]/members/page.tsx +3 -3
- package/app/(dashboard)/settings/organizations/new/page.tsx +3 -3
- package/app/(dashboard)/settings/portal-access/page.tsx +1 -1
- package/app/(dashboard)/settings/portal-branding/page.tsx +2 -2
- package/app/(dashboard)/settings/portal-growth/page.tsx +2 -2
- package/app/(dashboard)/settings/portal-modules/page.tsx +1 -1
- package/app/(dashboard)/settings/portal-resources/page.tsx +3 -3
- package/app/(dashboard)/settings/widgets/page.tsx +4 -4
- package/components/landing/hero.tsx +16 -16
- package/components/layout/language-switcher.tsx +27 -0
- package/components/layout/sidebar.tsx +1 -0
- package/components/portal/portal-modules-panel.tsx +7 -7
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# @nexttylabs/echo
|
|
2
2
|
|
|
3
|
+
## 0.13.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
|
+
- 76fda4d: fix organizational setting i18n
|
|
14
|
+
- 7dba561: remove legacy changeset files
|
|
15
|
+
- 158b254: show current orgnization
|
|
16
|
+
- 1e798f1: remove unuse tables and published files
|
|
17
|
+
- e3c4e1c: fix workflow errors
|
|
18
|
+
- d315d85: Fix the theme display issue.
|
|
19
|
+
- daf9abb: first release
|
|
20
|
+
|
|
21
|
+
## 0.12.0
|
|
22
|
+
|
|
23
|
+
### Minor Changes
|
|
24
|
+
|
|
25
|
+
- fc3d8f1: support light/dark theme
|
|
26
|
+
- cc04d9f: Use the organization member role uniformly.
|
|
27
|
+
- 5cfdeb7: feat: support editing user profile
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- 76fda4d: fix organizational setting i18n
|
|
32
|
+
- 7dba561: remove legacy changeset files
|
|
33
|
+
- 158b254: show current orgnization
|
|
34
|
+
- 1e798f1: remove unuse tables and published files
|
|
35
|
+
- e3c4e1c: fix workflow errors
|
|
36
|
+
- d315d85: Fix the theme display issue.
|
|
37
|
+
- daf9abb: first release
|
|
38
|
+
|
|
3
39
|
## 0.11.0
|
|
4
40
|
|
|
5
41
|
### Minor Changes
|
|
@@ -36,13 +36,13 @@ export default async function LoginPage() {
|
|
|
36
36
|
const t = await getTranslations("auth.login");
|
|
37
37
|
|
|
38
38
|
return (
|
|
39
|
-
<div className="min-h-screen bg-
|
|
39
|
+
<div className="min-h-screen bg-background px-4 py-12">
|
|
40
40
|
<div className="mx-auto flex w-full max-w-md flex-col gap-6">
|
|
41
41
|
<div className="text-center">
|
|
42
|
-
<h1 className="text-3xl font-semibold tracking-tight text-
|
|
42
|
+
<h1 className="text-3xl font-semibold tracking-tight text-foreground">
|
|
43
43
|
{t("pageTitle")}
|
|
44
44
|
</h1>
|
|
45
|
-
<p className="mt-2 text-sm text-
|
|
45
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
46
46
|
{t("pageSubtitle")}
|
|
47
47
|
</p>
|
|
48
48
|
</div>
|
|
@@ -31,13 +31,13 @@ export default async function RegisterPage() {
|
|
|
31
31
|
const t = await getTranslations("auth.register");
|
|
32
32
|
|
|
33
33
|
return (
|
|
34
|
-
<div className="min-h-screen bg-
|
|
34
|
+
<div className="min-h-screen bg-background px-4 py-12">
|
|
35
35
|
<div className="mx-auto flex w-full max-w-md flex-col gap-6">
|
|
36
36
|
<div className="text-center">
|
|
37
|
-
<h1 className="text-3xl font-semibold tracking-tight text-
|
|
37
|
+
<h1 className="text-3xl font-semibold tracking-tight text-foreground">
|
|
38
38
|
{t("pageTitle")}
|
|
39
39
|
</h1>
|
|
40
|
-
<p className="mt-2 text-sm text-
|
|
40
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
41
41
|
{t("pageSubtitle")}
|
|
42
42
|
</p>
|
|
43
43
|
</div>
|
|
@@ -39,11 +39,11 @@ export default async function NewFeedbackPage() {
|
|
|
39
39
|
|
|
40
40
|
if (!organization) {
|
|
41
41
|
return (
|
|
42
|
-
<div className="min-h-screen bg-
|
|
42
|
+
<div className="min-h-screen bg-background px-4 py-12">
|
|
43
43
|
<div className="mx-auto flex w-full max-w-2xl flex-col gap-6">
|
|
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">
|
|
44
|
+
<div className="rounded-lg border border-amber-200 bg-amber-50 p-6 dark:border-amber-800 dark:bg-amber-950">
|
|
45
|
+
<h1 className="text-xl font-semibold text-amber-800 dark:text-amber-200">未找到组织</h1>
|
|
46
|
+
<p className="mt-2 text-sm text-amber-700 dark:text-amber-300">
|
|
47
47
|
请先加入组织后再代客户提交反馈。
|
|
48
48
|
</p>
|
|
49
49
|
</div>
|
|
@@ -58,11 +58,11 @@ export default async function NewFeedbackPage() {
|
|
|
58
58
|
|
|
59
59
|
if (!hasSubmitOnBehalfPermission) {
|
|
60
60
|
return (
|
|
61
|
-
<div className="min-h-screen bg-
|
|
61
|
+
<div className="min-h-screen bg-background px-4 py-12">
|
|
62
62
|
<div className="mx-auto flex w-full max-w-2xl flex-col gap-6">
|
|
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">
|
|
63
|
+
<div className="rounded-lg border border-red-200 bg-red-50 p-6 dark:border-red-800 dark:bg-red-950">
|
|
64
|
+
<h1 className="text-xl font-semibold text-red-800 dark:text-red-200">权限不足</h1>
|
|
65
|
+
<p className="mt-2 text-sm text-red-600 dark:text-red-300">
|
|
66
66
|
您没有代客户提交反馈的权限。请联系管理员获取相应权限。
|
|
67
67
|
</p>
|
|
68
68
|
</div>
|
|
@@ -72,13 +72,13 @@ export default async function NewFeedbackPage() {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
return (
|
|
75
|
-
<div className="min-h-screen bg-
|
|
75
|
+
<div className="min-h-screen bg-background px-4 py-12">
|
|
76
76
|
<div className="mx-auto flex w-full max-w-2xl flex-col gap-6">
|
|
77
77
|
<div>
|
|
78
|
-
<h1 className="text-3xl font-semibold tracking-tight text-
|
|
78
|
+
<h1 className="text-3xl font-semibold tracking-tight text-foreground">
|
|
79
79
|
代客户提交反馈
|
|
80
80
|
</h1>
|
|
81
|
-
<p className="mt-2 text-sm text-
|
|
81
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
82
82
|
以客服身份代表客户提交反馈,系统将同时记录客服和客户信息
|
|
83
83
|
</p>
|
|
84
84
|
</div>
|
|
@@ -38,7 +38,7 @@ export default async function BrandingSettingsPage() {
|
|
|
38
38
|
<p className="text-muted-foreground">{t("pageDescription")}</p>
|
|
39
39
|
</div>
|
|
40
40
|
|
|
41
|
-
<Card
|
|
41
|
+
<Card>
|
|
42
42
|
<CardHeader>
|
|
43
43
|
<CardTitle>{t("themeTitle")}</CardTitle>
|
|
44
44
|
<CardDescription>{t("themeDesc")}</CardDescription>
|
|
@@ -48,7 +48,7 @@ export default async function BrandingSettingsPage() {
|
|
|
48
48
|
</CardContent>
|
|
49
49
|
</Card>
|
|
50
50
|
|
|
51
|
-
<Card
|
|
51
|
+
<Card>
|
|
52
52
|
<CardHeader>
|
|
53
53
|
<CardTitle>{t("copyTitle")}</CardTitle>
|
|
54
54
|
<CardDescription>{t("copyDesc")}</CardDescription>
|
|
@@ -35,13 +35,13 @@ export default async function ChangelogSettingsPage() {
|
|
|
35
35
|
<p className="text-muted-foreground">{t("pageDescription")}</p>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
|
-
<Card
|
|
38
|
+
<Card>
|
|
39
39
|
<CardHeader>
|
|
40
40
|
<CardTitle>{t("settingsTitle")}</CardTitle>
|
|
41
41
|
<CardDescription>{t("settingsDesc")}</CardDescription>
|
|
42
42
|
</CardHeader>
|
|
43
43
|
<CardContent>
|
|
44
|
-
<div className="rounded-md border border-dashed
|
|
44
|
+
<div className="rounded-md border border-dashed px-4 py-8 text-center text-sm text-muted-foreground">
|
|
45
45
|
{t("settingsComingSoon")}
|
|
46
46
|
</div>
|
|
47
47
|
</CardContent>
|
|
@@ -35,25 +35,25 @@ export default async function FeedbackSettingsPage() {
|
|
|
35
35
|
<p className="text-muted-foreground">{t("pageDescription")}</p>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
|
-
<Card
|
|
38
|
+
<Card>
|
|
39
39
|
<CardHeader>
|
|
40
40
|
<CardTitle>{t("settingsTitle")}</CardTitle>
|
|
41
41
|
<CardDescription>{t("settingsDesc")}</CardDescription>
|
|
42
42
|
</CardHeader>
|
|
43
43
|
<CardContent>
|
|
44
|
-
<div className="rounded-md border border-dashed
|
|
44
|
+
<div className="rounded-md border border-dashed px-4 py-8 text-center text-sm text-muted-foreground">
|
|
45
45
|
{t("settingsComingSoon")}
|
|
46
46
|
</div>
|
|
47
47
|
</CardContent>
|
|
48
48
|
</Card>
|
|
49
49
|
|
|
50
|
-
<Card
|
|
50
|
+
<Card>
|
|
51
51
|
<CardHeader>
|
|
52
52
|
<CardTitle>{t("roadmapTitle")}</CardTitle>
|
|
53
53
|
<CardDescription>{t("roadmapDesc")}</CardDescription>
|
|
54
54
|
</CardHeader>
|
|
55
55
|
<CardContent>
|
|
56
|
-
<div className="rounded-md border border-dashed
|
|
56
|
+
<div className="rounded-md border border-dashed px-4 py-8 text-center text-sm text-muted-foreground">
|
|
57
57
|
{t("roadmapComingSoon")}
|
|
58
58
|
</div>
|
|
59
59
|
</CardContent>
|
|
@@ -35,16 +35,16 @@ export default async function IntegrationsSettingsPage() {
|
|
|
35
35
|
<p className="text-muted-foreground">{t("pageDescription")}</p>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
|
-
<Card
|
|
38
|
+
<Card>
|
|
39
39
|
<CardHeader>
|
|
40
40
|
<CardTitle>{t("availableTitle")}</CardTitle>
|
|
41
41
|
<CardDescription>{t("availableDesc")}</CardDescription>
|
|
42
42
|
</CardHeader>
|
|
43
43
|
<CardContent>
|
|
44
44
|
<div className="grid gap-4 md:grid-cols-2">
|
|
45
|
-
<div className="rounded-lg border
|
|
45
|
+
<div className="rounded-lg border p-4">
|
|
46
46
|
<div className="flex items-center gap-3">
|
|
47
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-
|
|
47
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-muted">
|
|
48
48
|
<span className="text-lg">🔗</span>
|
|
49
49
|
</div>
|
|
50
50
|
<div>
|
|
@@ -53,9 +53,9 @@ export default async function IntegrationsSettingsPage() {
|
|
|
53
53
|
</div>
|
|
54
54
|
</div>
|
|
55
55
|
</div>
|
|
56
|
-
<div className="rounded-lg border
|
|
56
|
+
<div className="rounded-lg border p-4">
|
|
57
57
|
<div className="flex items-center gap-3">
|
|
58
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-
|
|
58
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-muted">
|
|
59
59
|
<span className="text-lg">📋</span>
|
|
60
60
|
</div>
|
|
61
61
|
<div>
|
|
@@ -64,9 +64,9 @@ export default async function IntegrationsSettingsPage() {
|
|
|
64
64
|
</div>
|
|
65
65
|
</div>
|
|
66
66
|
</div>
|
|
67
|
-
<div className="rounded-lg border
|
|
67
|
+
<div className="rounded-lg border p-4">
|
|
68
68
|
<div className="flex items-center gap-3">
|
|
69
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-
|
|
69
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-muted">
|
|
70
70
|
<span className="text-lg">📊</span>
|
|
71
71
|
</div>
|
|
72
72
|
<div>
|
|
@@ -75,9 +75,9 @@ export default async function IntegrationsSettingsPage() {
|
|
|
75
75
|
</div>
|
|
76
76
|
</div>
|
|
77
77
|
</div>
|
|
78
|
-
<div className="rounded-lg border
|
|
78
|
+
<div className="rounded-lg border p-4">
|
|
79
79
|
<div className="flex items-center gap-3">
|
|
80
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-
|
|
80
|
+
<div className="flex h-10 w-10 items-center justify-center rounded-md bg-muted">
|
|
81
81
|
<span className="text-lg">💬</span>
|
|
82
82
|
</div>
|
|
83
83
|
<div>
|
|
@@ -40,7 +40,7 @@ export default async function ModulesSettingsPage() {
|
|
|
40
40
|
<p className="text-muted-foreground">{t("pageDescription")}</p>
|
|
41
41
|
</div>
|
|
42
42
|
|
|
43
|
-
<Card
|
|
43
|
+
<Card>
|
|
44
44
|
<CardHeader>
|
|
45
45
|
<CardTitle>{t("configTitle")}</CardTitle>
|
|
46
46
|
<CardDescription>{t("configDesc")}</CardDescription>
|
|
@@ -46,13 +46,13 @@ export default async function OrganizationMembersPage({
|
|
|
46
46
|
: [];
|
|
47
47
|
|
|
48
48
|
return (
|
|
49
|
-
<div className="min-h-screen bg-
|
|
49
|
+
<div className="min-h-screen bg-background px-4 py-12">
|
|
50
50
|
<div className="mx-auto flex w-full max-w-3xl flex-col gap-6">
|
|
51
51
|
<div>
|
|
52
|
-
<h1 className="text-3xl font-semibold tracking-tight text-
|
|
52
|
+
<h1 className="text-3xl font-semibold tracking-tight text-foreground">
|
|
53
53
|
成员管理
|
|
54
54
|
</h1>
|
|
55
|
-
<p className="mt-2 text-sm text-
|
|
55
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
56
56
|
管理当前组织成员并发送新的邀请
|
|
57
57
|
</p>
|
|
58
58
|
</div>
|
|
@@ -19,13 +19,13 @@ import { OrganizationForm } from "@/components/settings/organization-form";
|
|
|
19
19
|
|
|
20
20
|
export default function NewOrganizationPage() {
|
|
21
21
|
return (
|
|
22
|
-
<div className="min-h-screen bg-
|
|
22
|
+
<div className="min-h-screen bg-background px-4 py-12">
|
|
23
23
|
<div className="mx-auto flex w-full max-w-2xl flex-col gap-6">
|
|
24
24
|
<div>
|
|
25
|
-
<h1 className="text-3xl font-semibold tracking-tight text-
|
|
25
|
+
<h1 className="text-3xl font-semibold tracking-tight text-foreground">
|
|
26
26
|
创建组织
|
|
27
27
|
</h1>
|
|
28
|
-
<p className="mt-2 text-sm text-
|
|
28
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
29
29
|
为新的团队或项目创建独立空间
|
|
30
30
|
</p>
|
|
31
31
|
</div>
|
|
@@ -35,7 +35,7 @@ export default async function PortalAccessSettingsPage() {
|
|
|
35
35
|
<p className="text-muted-foreground">控制 Portal 的公开访问与权限范围</p>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
|
-
<Card
|
|
38
|
+
<Card>
|
|
39
39
|
<CardHeader>
|
|
40
40
|
<CardTitle>可见性与权限</CardTitle>
|
|
41
41
|
<CardDescription>设置公开访问、投票与索引策略</CardDescription>
|
|
@@ -35,7 +35,7 @@ export default async function PortalBrandingSettingsPage() {
|
|
|
35
35
|
<p className="text-muted-foreground">{t("description")}</p>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
|
-
<Card
|
|
38
|
+
<Card>
|
|
39
39
|
<CardHeader>
|
|
40
40
|
<CardTitle>{t("theme.title")}</CardTitle>
|
|
41
41
|
<CardDescription>{t("theme.description")}</CardDescription>
|
|
@@ -45,7 +45,7 @@ export default async function PortalBrandingSettingsPage() {
|
|
|
45
45
|
</CardContent>
|
|
46
46
|
</Card>
|
|
47
47
|
|
|
48
|
-
<Card
|
|
48
|
+
<Card>
|
|
49
49
|
<CardHeader>
|
|
50
50
|
<CardTitle>{t("copy.title")}</CardTitle>
|
|
51
51
|
<CardDescription>{t("copy.description")}</CardDescription>
|
|
@@ -33,7 +33,7 @@ export default async function PortalGrowthSettingsPage() {
|
|
|
33
33
|
<p className="text-muted-foreground">配置分享入口与 SEO 优化</p>
|
|
34
34
|
</div>
|
|
35
35
|
|
|
36
|
-
<Card
|
|
36
|
+
<Card>
|
|
37
37
|
<CardHeader>
|
|
38
38
|
<CardTitle>分享设置</CardTitle>
|
|
39
39
|
<CardDescription>配置社交分享与传播渠道</CardDescription>
|
|
@@ -43,7 +43,7 @@ export default async function PortalGrowthSettingsPage() {
|
|
|
43
43
|
</CardContent>
|
|
44
44
|
</Card>
|
|
45
45
|
|
|
46
|
-
<Card
|
|
46
|
+
<Card>
|
|
47
47
|
<CardHeader>
|
|
48
48
|
<CardTitle>SEO 设置</CardTitle>
|
|
49
49
|
<CardDescription>搜索引擎与社交分享优化</CardDescription>
|
|
@@ -35,7 +35,7 @@ export default async function PortalModulesSettingsPage() {
|
|
|
35
35
|
<p className="text-muted-foreground">自定义 Portal 的功能模块开关</p>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
|
-
<Card
|
|
38
|
+
<Card>
|
|
39
39
|
<CardHeader>
|
|
40
40
|
<CardTitle>模块配置</CardTitle>
|
|
41
41
|
<CardDescription>按需启用反馈、路线图与更新日志</CardDescription>
|
|
@@ -37,7 +37,7 @@ export default async function PortalResourcesSettingsPage() {
|
|
|
37
37
|
<p className="text-muted-foreground">门户入口与相关资源链接</p>
|
|
38
38
|
</div>
|
|
39
39
|
|
|
40
|
-
<Card
|
|
40
|
+
<Card>
|
|
41
41
|
<CardHeader className="flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
|
42
42
|
<div>
|
|
43
43
|
<CardTitle>Portal 链接</CardTitle>
|
|
@@ -48,8 +48,8 @@ export default async function PortalResourcesSettingsPage() {
|
|
|
48
48
|
</Badge>
|
|
49
49
|
</CardHeader>
|
|
50
50
|
<CardContent className="space-y-4">
|
|
51
|
-
<div className="text-sm text-
|
|
52
|
-
门户链接:<span className="font-medium text-
|
|
51
|
+
<div className="text-sm text-muted-foreground">
|
|
52
|
+
门户链接:<span className="font-medium text-foreground">{portalLink}</span>
|
|
53
53
|
</div>
|
|
54
54
|
<div className="flex flex-wrap gap-3">
|
|
55
55
|
<Button asChild variant="outline" size="sm">
|
|
@@ -35,25 +35,25 @@ export default async function WidgetsSettingsPage() {
|
|
|
35
35
|
<p className="text-muted-foreground">{t("pageDescription")}</p>
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
|
-
<Card
|
|
38
|
+
<Card>
|
|
39
39
|
<CardHeader>
|
|
40
40
|
<CardTitle>{t("feedbackWidget")}</CardTitle>
|
|
41
41
|
<CardDescription>{t("feedbackWidgetDesc")}</CardDescription>
|
|
42
42
|
</CardHeader>
|
|
43
43
|
<CardContent>
|
|
44
|
-
<div className="rounded-md border border-dashed
|
|
44
|
+
<div className="rounded-md border border-dashed px-4 py-8 text-center text-sm text-muted-foreground">
|
|
45
45
|
{t("feedbackWidgetComingSoon")}
|
|
46
46
|
</div>
|
|
47
47
|
</CardContent>
|
|
48
48
|
</Card>
|
|
49
49
|
|
|
50
|
-
<Card
|
|
50
|
+
<Card>
|
|
51
51
|
<CardHeader>
|
|
52
52
|
<CardTitle>{t("changelogWidget")}</CardTitle>
|
|
53
53
|
<CardDescription>{t("changelogWidgetDesc")}</CardDescription>
|
|
54
54
|
</CardHeader>
|
|
55
55
|
<CardContent>
|
|
56
|
-
<div className="rounded-md border border-dashed
|
|
56
|
+
<div className="rounded-md border border-dashed px-4 py-8 text-center text-sm text-muted-foreground">
|
|
57
57
|
{t("changelogWidgetComingSoon")}
|
|
58
58
|
</div>
|
|
59
59
|
</CardContent>
|
|
@@ -25,11 +25,11 @@ export function Hero() {
|
|
|
25
25
|
const t = useTranslations("hero");
|
|
26
26
|
|
|
27
27
|
return (
|
|
28
|
-
<div className="min-h-screen bg-
|
|
28
|
+
<div className="min-h-screen bg-background">
|
|
29
29
|
<header className="container mx-auto flex items-center justify-between px-4 py-6">
|
|
30
30
|
<div className="flex items-center gap-2">
|
|
31
31
|
<MessageSquare className="size-8 text-primary" />
|
|
32
|
-
<span className="text-2xl font-bold text-
|
|
32
|
+
<span className="text-2xl font-bold text-foreground">{t("title")}</span>
|
|
33
33
|
</div>
|
|
34
34
|
<nav className="flex items-center gap-4">
|
|
35
35
|
<LanguageSwitcher variant="icon" />
|
|
@@ -44,13 +44,13 @@ export function Hero() {
|
|
|
44
44
|
|
|
45
45
|
<main className="container mx-auto px-4 py-20">
|
|
46
46
|
<section className="mx-auto max-w-4xl text-center">
|
|
47
|
-
<h1 className="text-5xl font-bold tracking-tight text-
|
|
47
|
+
<h1 className="text-5xl font-bold tracking-tight text-foreground sm:text-6xl">
|
|
48
48
|
{t("headline")}
|
|
49
49
|
</h1>
|
|
50
|
-
<p className="mt-6 text-xl text-
|
|
50
|
+
<p className="mt-6 text-xl text-muted-foreground">
|
|
51
51
|
{t("description")}
|
|
52
52
|
<br />
|
|
53
|
-
<span className="font-medium text-
|
|
53
|
+
<span className="font-medium text-foreground/80">{t("subtitle")}</span>
|
|
54
54
|
</p>
|
|
55
55
|
<div className="mt-10 flex flex-wrap items-center justify-center gap-4">
|
|
56
56
|
<Button size="lg" asChild>
|
|
@@ -63,7 +63,7 @@ export function Hero() {
|
|
|
63
63
|
</section>
|
|
64
64
|
|
|
65
65
|
<section className="mx-auto mt-32 max-w-5xl">
|
|
66
|
-
<h2 className="text-center text-3xl font-bold text-
|
|
66
|
+
<h2 className="text-center text-3xl font-bold text-foreground">{t("coreValues")}</h2>
|
|
67
67
|
<div className="mt-12 grid gap-8 sm:grid-cols-2 lg:grid-cols-4">
|
|
68
68
|
<FeatureCard
|
|
69
69
|
icon={<Zap className="size-6" />}
|
|
@@ -89,8 +89,8 @@ export function Hero() {
|
|
|
89
89
|
</section>
|
|
90
90
|
|
|
91
91
|
<section className="mx-auto mt-32 max-w-3xl text-center">
|
|
92
|
-
<h2 className="text-3xl font-bold text-
|
|
93
|
-
<p className="mt-4 text-lg text-
|
|
92
|
+
<h2 className="text-3xl font-bold text-foreground">{t("ready.title")}</h2>
|
|
93
|
+
<p className="mt-4 text-lg text-muted-foreground">
|
|
94
94
|
{t("ready.description")}
|
|
95
95
|
</p>
|
|
96
96
|
<div className="mt-8 flex flex-wrap items-center justify-center gap-4">
|
|
@@ -104,21 +104,21 @@ export function Hero() {
|
|
|
104
104
|
</section>
|
|
105
105
|
</main>
|
|
106
106
|
|
|
107
|
-
<footer className="container mx-auto border-t border-
|
|
107
|
+
<footer className="container mx-auto border-t border-border px-4 py-8">
|
|
108
108
|
<div className="flex flex-col items-center justify-between gap-4 sm:flex-row">
|
|
109
|
-
<div className="flex items-center gap-2 text-sm text-
|
|
109
|
+
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
110
110
|
<MessageSquare className="size-4" />
|
|
111
111
|
<span>{t("footer.title")}</span>
|
|
112
112
|
</div>
|
|
113
|
-
<div className="flex gap-6 text-sm text-
|
|
114
|
-
<Link href="/docs" className="hover:text-
|
|
113
|
+
<div className="flex gap-6 text-sm text-muted-foreground">
|
|
114
|
+
<Link href="/docs" className="hover:text-foreground">
|
|
115
115
|
{t("footer.docs")}
|
|
116
116
|
</Link>
|
|
117
117
|
<a
|
|
118
118
|
href="https://github.com"
|
|
119
119
|
target="_blank"
|
|
120
120
|
rel="noopener noreferrer"
|
|
121
|
-
className="hover:text-
|
|
121
|
+
className="hover:text-foreground"
|
|
122
122
|
>
|
|
123
123
|
GitHub
|
|
124
124
|
</a>
|
|
@@ -139,12 +139,12 @@ function FeatureCard({
|
|
|
139
139
|
description: string;
|
|
140
140
|
}) {
|
|
141
141
|
return (
|
|
142
|
-
<div className="rounded-xl border border-
|
|
142
|
+
<div className="rounded-xl border border-border bg-card p-6 shadow-sm">
|
|
143
143
|
<div className="flex size-12 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
|
144
144
|
{icon}
|
|
145
145
|
</div>
|
|
146
|
-
<h3 className="mt-4 font-semibold text-
|
|
147
|
-
<p className="mt-2 text-sm text-
|
|
146
|
+
<h3 className="mt-4 font-semibold text-card-foreground">{title}</h3>
|
|
147
|
+
<p className="mt-2 text-sm text-muted-foreground">{description}</p>
|
|
148
148
|
</div>
|
|
149
149
|
);
|
|
150
150
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
3
4
|
|
|
4
5
|
/*
|
|
5
6
|
* Copyright (c) 2026 Nexttylabs Team
|
|
@@ -66,6 +67,32 @@ function useLocaleSwitcher() {
|
|
|
66
67
|
|
|
67
68
|
export function LanguageSwitcher({ variant = "text" }: LanguageSwitcherProps) {
|
|
68
69
|
const { locale, t, isPending, handleSelect } = useLocaleSwitcher();
|
|
70
|
+
const [mounted, setMounted] = useState(false);
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
setMounted(true);
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
// Render a placeholder button during SSR to avoid hydration mismatch
|
|
77
|
+
// caused by Radix UI's dynamic ID generation
|
|
78
|
+
if (!mounted) {
|
|
79
|
+
return variant === "icon" ? (
|
|
80
|
+
<Button
|
|
81
|
+
variant="ghost"
|
|
82
|
+
size="icon"
|
|
83
|
+
disabled
|
|
84
|
+
aria-label={t("label")}
|
|
85
|
+
>
|
|
86
|
+
<Languages className="h-4 w-4" />
|
|
87
|
+
<span className="sr-only">{t("label")}</span>
|
|
88
|
+
</Button>
|
|
89
|
+
) : (
|
|
90
|
+
<Button variant="ghost" size="sm" disabled>
|
|
91
|
+
<Languages className="h-4 w-4 mr-2" />
|
|
92
|
+
{t(locale)}
|
|
93
|
+
</Button>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
69
96
|
|
|
70
97
|
return (
|
|
71
98
|
<DropdownMenu>
|
|
@@ -120,6 +120,7 @@ export function Sidebar({ user, organizations = [], currentOrgId, onClose }: Sid
|
|
|
120
120
|
const handleLocaleChange = (nextLocale: AppLocale) => {
|
|
121
121
|
if (nextLocale === locale) return;
|
|
122
122
|
const secure = window.location.protocol === "https:" ? ";secure" : "";
|
|
123
|
+
// eslint-disable-next-line react-hooks/immutability
|
|
123
124
|
document.cookie = `${LOCALE_COOKIE_NAME}=${nextLocale};path=/;max-age=${60 * 60 * 24 * 365};samesite=lax${secure}`;
|
|
124
125
|
startTransition(() => {
|
|
125
126
|
router.refresh();
|
|
@@ -56,10 +56,10 @@ export function PortalModulesPanel({ organizationId, initialModules }: PortalMod
|
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
return (
|
|
59
|
-
<div className="space-y-4 rounded-xl border bg-
|
|
59
|
+
<div className="space-y-4 rounded-xl border bg-muted/50 p-5 shadow-sm">
|
|
60
60
|
<div>
|
|
61
|
-
<h3 className="text-sm font-semibold text-
|
|
62
|
-
<p className="mt-1 text-sm text-
|
|
61
|
+
<h3 className="text-sm font-semibold text-foreground">{t("panelTitle")}</h3>
|
|
62
|
+
<p className="mt-1 text-sm text-muted-foreground">
|
|
63
63
|
{t("panelDescription")}
|
|
64
64
|
</p>
|
|
65
65
|
</div>
|
|
@@ -89,7 +89,7 @@ export function PortalModulesPanel({ organizationId, initialModules }: PortalMod
|
|
|
89
89
|
<Button onClick={onSave} disabled={saving}>
|
|
90
90
|
{saving ? t("saving") : t("saveButton")}
|
|
91
91
|
</Button>
|
|
92
|
-
{message && <span className="text-sm text-
|
|
92
|
+
{message && <span className="text-sm text-muted-foreground">{message}</span>}
|
|
93
93
|
</div>
|
|
94
94
|
</div>
|
|
95
95
|
);
|
|
@@ -107,10 +107,10 @@ function ModuleRow({
|
|
|
107
107
|
onChange: (value: boolean) => void;
|
|
108
108
|
}) {
|
|
109
109
|
return (
|
|
110
|
-
<div className="flex items-center justify-between gap-4 rounded-lg border
|
|
110
|
+
<div className="flex items-center justify-between gap-4 rounded-lg border bg-background px-4 py-3">
|
|
111
111
|
<div>
|
|
112
|
-
<p className="text-sm font-medium text-
|
|
113
|
-
<p className="text-xs text-
|
|
112
|
+
<p className="text-sm font-medium text-foreground">{label}</p>
|
|
113
|
+
<p className="text-xs text-muted-foreground">{description}</p>
|
|
114
114
|
</div>
|
|
115
115
|
<Switch checked={checked} onCheckedChange={onChange} />
|
|
116
116
|
</div>
|