@cogito.ai/cli 0.4.1 → 0.4.3
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/README.md +29 -22
- package/dist/index.js +9 -15
- package/dist/templates/web-nextjs/.github/copilot-instructions.md +5 -6
- package/dist/templates/web-nextjs/README.md +25 -24
- package/dist/templates/web-nextjs/apps/docs/.source/browser.ts +18 -8
- package/dist/templates/web-nextjs/apps/docs/.source/dynamic.ts +11 -5
- package/dist/templates/web-nextjs/apps/docs/.source/server.ts +37 -17
- package/dist/templates/web-nextjs/apps/docs/app/docs/[[...slug]]/page.tsx +1 -6
- package/dist/templates/web-nextjs/apps/docs/app/docs/layout.tsx +1 -4
- package/dist/templates/web-nextjs/apps/docs/app/llms-full.txt/route.ts +3 -1
- package/dist/templates/web-nextjs/apps/docs/next-env.d.ts +1 -1
- package/dist/templates/web-nextjs/apps/web/.github/copilot-instructions.md +53 -6
- package/dist/templates/web-nextjs/apps/web/.github/skills/impeccable/SKILL.md +55 -0
- package/dist/templates/web-nextjs/apps/web/DESIGN.md +65 -0
- package/dist/templates/web-nextjs/apps/web/messages/en.json +127 -1
- package/dist/templates/web-nextjs/apps/web/messages/zh.json +127 -1
- package/dist/templates/web-nextjs/apps/web/next-env.d.ts +1 -1
- package/dist/templates/web-nextjs/apps/web/next.config.ts +3 -3
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/forgot-password/page.tsx +265 -0
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/login/page.tsx +20 -4
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/reset-password/page.tsx +115 -0
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/signup/page.tsx +18 -17
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/dashboard/page.tsx +1 -5
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/settings/layout.tsx +5 -0
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/settings/page.tsx +9 -0
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/settings/profile/page.tsx +91 -0
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/about/page.tsx +22 -0
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/globals.css +17 -5
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/help/page.tsx +21 -0
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/layout.tsx +10 -8
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/page.tsx +22 -6
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/privacy/page.tsx +14 -15
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/terms/page.tsx +1 -5
- package/dist/templates/web-nextjs/apps/web/src/app/auth/callback/route.ts +7 -2
- package/dist/templates/web-nextjs/apps/web/src/components/dashboard/app-sidebar.tsx +37 -137
- package/dist/templates/web-nextjs/apps/web/src/components/dashboard/chart-area-interactive.tsx +122 -146
- package/dist/templates/web-nextjs/apps/web/src/components/dashboard/data-table.tsx +84 -149
- package/dist/templates/web-nextjs/apps/web/src/components/dashboard/nav-documents.tsx +7 -16
- package/dist/templates/web-nextjs/apps/web/src/components/dashboard/nav-main.tsx +4 -4
- package/dist/templates/web-nextjs/apps/web/src/components/dashboard/nav-secondary.tsx +4 -4
- package/dist/templates/web-nextjs/apps/web/src/components/dashboard/nav-user.tsx +17 -33
- package/dist/templates/web-nextjs/apps/web/src/components/dashboard/page.tsx +10 -13
- package/dist/templates/web-nextjs/apps/web/src/components/dashboard/section-cards.tsx +5 -9
- package/dist/templates/web-nextjs/apps/web/src/components/dashboard/site-header.tsx +6 -7
- package/dist/templates/web-nextjs/apps/web/src/components/landing/features.tsx +63 -0
- package/dist/templates/web-nextjs/apps/web/src/components/landing/footer.tsx +48 -0
- package/dist/templates/web-nextjs/apps/web/src/components/landing/header.tsx +97 -0
- package/dist/templates/web-nextjs/apps/web/src/components/landing/hero.tsx +45 -0
- package/dist/templates/web-nextjs/apps/web/src/components/landing/how-it-works.tsx +35 -0
- package/dist/templates/web-nextjs/apps/web/src/components/landing/pricing-teaser.tsx +23 -0
- package/dist/templates/web-nextjs/apps/web/src/components/profile/profile-form.tsx +66 -0
- package/dist/templates/web-nextjs/apps/web/src/components/providers/theme-provider.tsx +16 -0
- package/dist/templates/web-nextjs/apps/web/src/components/ui/alert-dialog.tsx +32 -49
- package/dist/templates/web-nextjs/apps/web/src/components/ui/alert.tsx +16 -23
- package/dist/templates/web-nextjs/apps/web/src/components/ui/avatar.tsx +25 -38
- package/dist/templates/web-nextjs/apps/web/src/components/ui/badge.tsx +16 -18
- package/dist/templates/web-nextjs/apps/web/src/components/ui/breadcrumb.tsx +19 -26
- package/dist/templates/web-nextjs/apps/web/src/components/ui/button.tsx +23 -24
- package/dist/templates/web-nextjs/apps/web/src/components/ui/card.tsx +19 -36
- package/dist/templates/web-nextjs/apps/web/src/components/ui/chart.tsx +60 -94
- package/dist/templates/web-nextjs/apps/web/src/components/ui/checkbox.tsx +8 -11
- package/dist/templates/web-nextjs/apps/web/src/components/ui/collapsible.tsx +5 -17
- package/dist/templates/web-nextjs/apps/web/src/components/ui/command.tsx +25 -48
- package/dist/templates/web-nextjs/apps/web/src/components/ui/dialog.tsx +21 -35
- package/dist/templates/web-nextjs/apps/web/src/components/ui/drawer.tsx +24 -35
- package/dist/templates/web-nextjs/apps/web/src/components/ui/dropdown-menu.tsx +26 -55
- package/dist/templates/web-nextjs/apps/web/src/components/ui/field.tsx +62 -76
- package/dist/templates/web-nextjs/apps/web/src/components/ui/form.tsx +19 -34
- package/dist/templates/web-nextjs/apps/web/src/components/ui/input-otp.tsx +13 -20
- package/dist/templates/web-nextjs/apps/web/src/components/ui/input.tsx +6 -6
- package/dist/templates/web-nextjs/apps/web/src/components/ui/label.tsx +6 -6
- package/dist/templates/web-nextjs/apps/web/src/components/ui/pagination.tsx +21 -42
- package/dist/templates/web-nextjs/apps/web/src/components/ui/popover.tsx +16 -31
- package/dist/templates/web-nextjs/apps/web/src/components/ui/progress.tsx +5 -8
- package/dist/templates/web-nextjs/apps/web/src/components/ui/radio-group.tsx +8 -8
- package/dist/templates/web-nextjs/apps/web/src/components/ui/scroll-area.tsx +10 -12
- package/dist/templates/web-nextjs/apps/web/src/components/ui/select.tsx +26 -41
- package/dist/templates/web-nextjs/apps/web/src/components/ui/separator.tsx +7 -7
- package/dist/templates/web-nextjs/apps/web/src/components/ui/sheet.tsx +29 -38
- package/dist/templates/web-nextjs/apps/web/src/components/ui/sidebar.tsx +157 -189
- package/dist/templates/web-nextjs/apps/web/src/components/ui/skeleton.tsx +3 -3
- package/dist/templates/web-nextjs/apps/web/src/components/ui/slider.tsx +10 -15
- package/dist/templates/web-nextjs/apps/web/src/components/ui/sonner.tsx +13 -7
- package/dist/templates/web-nextjs/apps/web/src/components/ui/switch.tsx +9 -9
- package/dist/templates/web-nextjs/apps/web/src/components/ui/table.tsx +24 -48
- package/dist/templates/web-nextjs/apps/web/src/components/ui/tabs.tsx +21 -31
- package/dist/templates/web-nextjs/apps/web/src/components/ui/textarea.tsx +5 -5
- package/dist/templates/web-nextjs/apps/web/src/components/ui/theme-toggle.tsx +23 -0
- package/dist/templates/web-nextjs/apps/web/src/components/ui/toggle-group.tsx +15 -16
- package/dist/templates/web-nextjs/apps/web/src/components/ui/toggle.tsx +14 -15
- package/dist/templates/web-nextjs/apps/web/src/components/ui/tooltip.tsx +8 -12
- package/dist/templates/web-nextjs/apps/web/src/core/repositories/IAuthRepository.ts +5 -0
- package/dist/templates/web-nextjs/apps/web/src/core/types/auth.ts +1 -3
- package/dist/templates/web-nextjs/apps/web/src/features/auth/__contract__.ts +6 -0
- package/dist/templates/web-nextjs/apps/web/src/features/auth/actions.ts +126 -1
- package/dist/templates/web-nextjs/apps/web/src/features/auth/index.ts +12 -1
- package/dist/templates/web-nextjs/apps/web/src/features/auth/server.ts +3 -0
- package/dist/templates/web-nextjs/apps/web/src/hooks/use-mobile.ts +4 -4
- package/dist/templates/web-nextjs/apps/web/src/i18n/config.ts +1 -1
- package/dist/templates/web-nextjs/apps/web/src/infra/db/SupabaseAuthRepository.ts +73 -4
- package/dist/templates/web-nextjs/apps/web/src/infra/db/client.ts +1 -4
- package/dist/templates/web-nextjs/apps/web/src/lib/utils.ts +2 -2
- package/dist/templates/web-nextjs/apps/web/src/lib/validations/auth.ts +34 -0
- package/dist/templates/web-nextjs/apps/web/src/styles/tokens.css +58 -0
- package/package.json +1 -1
|
@@ -3,6 +3,73 @@
|
|
|
3
3
|
"title": "Welcome to AgentDock Web Template",
|
|
4
4
|
"description": "A Next.js scaffold with four-layer architecture."
|
|
5
5
|
},
|
|
6
|
+
"landing": {
|
|
7
|
+
"nav": {
|
|
8
|
+
"features": "Features",
|
|
9
|
+
"pricing": "Pricing",
|
|
10
|
+
"docs": "Docs",
|
|
11
|
+
"login": "Log in",
|
|
12
|
+
"getStarted": "Get Started"
|
|
13
|
+
},
|
|
14
|
+
"hero": {
|
|
15
|
+
"badge": "Open Source",
|
|
16
|
+
"title": "Build SaaS Faster with AgentDock",
|
|
17
|
+
"subtitle": "A production-ready Next.js scaffold with four-layer architecture, Supabase auth, i18n, and AI coding agent integration. Ship your next product in days, not months.",
|
|
18
|
+
"ctaPrimary": "Get Started Free",
|
|
19
|
+
"ctaSecondary": "View Docs"
|
|
20
|
+
},
|
|
21
|
+
"features": {
|
|
22
|
+
"title": "Everything you need to ship",
|
|
23
|
+
"subtitle": "Built-in features that let you focus on your product, not the boilerplate.",
|
|
24
|
+
"items": {
|
|
25
|
+
"auth": {
|
|
26
|
+
"title": "Authentication",
|
|
27
|
+
"description": "Complete auth flow with email/password, OAuth, and password reset. Secured by Supabase."
|
|
28
|
+
},
|
|
29
|
+
"monorepo": {
|
|
30
|
+
"title": "Monorepo Ready",
|
|
31
|
+
"description": "Turborepo + pnpm workspace with shared packages for types, ESLint, and tooling."
|
|
32
|
+
},
|
|
33
|
+
"aiCoding": {
|
|
34
|
+
"title": "AI Coding Ready",
|
|
35
|
+
"description": "Structured for AI agents with clear contracts, design system, and copilot instructions."
|
|
36
|
+
},
|
|
37
|
+
"docs": {
|
|
38
|
+
"title": "Docs Co-evolution",
|
|
39
|
+
"description": "Fumadocs-powered documentation that grows alongside your application."
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"howItWorks": {
|
|
44
|
+
"title": "How it works",
|
|
45
|
+
"subtitle": "Get up and running in three simple steps.",
|
|
46
|
+
"steps": {
|
|
47
|
+
"step1": {
|
|
48
|
+
"title": "Scaffold",
|
|
49
|
+
"description": "Run the CLI to generate your project with all batteries included."
|
|
50
|
+
},
|
|
51
|
+
"step2": {
|
|
52
|
+
"title": "Customize",
|
|
53
|
+
"description": "Adjust tokens, colors, and content to match your brand."
|
|
54
|
+
},
|
|
55
|
+
"step3": {
|
|
56
|
+
"title": "Ship",
|
|
57
|
+
"description": "Deploy to Vercel with one command and start building."
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"pricing": {
|
|
62
|
+
"title": "Pricing",
|
|
63
|
+
"description": "Pricing plans coming soon. Contact us for early access.",
|
|
64
|
+
"cta": "Contact Us"
|
|
65
|
+
},
|
|
66
|
+
"footer": {
|
|
67
|
+
"help": "Help",
|
|
68
|
+
"privacy": "Privacy",
|
|
69
|
+
"about": "About",
|
|
70
|
+
"copyright": "© {year} AgentDock. All rights reserved."
|
|
71
|
+
}
|
|
72
|
+
},
|
|
6
73
|
"auth": {
|
|
7
74
|
"loginTitle": "Welcome back",
|
|
8
75
|
"loginSubtitle": "Sign in to your account",
|
|
@@ -33,6 +100,65 @@
|
|
|
33
100
|
"termsText": "By clicking continue, you agree to our",
|
|
34
101
|
"termsLink": "Terms of Service",
|
|
35
102
|
"andText": "and",
|
|
36
|
-
"privacyLink": "Privacy Policy"
|
|
103
|
+
"privacyLink": "Privacy Policy",
|
|
104
|
+
"forgotPasswordLink": "Forgot password?",
|
|
105
|
+
"forgotPasswordTitle": "Reset your password",
|
|
106
|
+
"forgotPasswordSubtitle": "Enter your email and we'll send you a verification code.",
|
|
107
|
+
"sendVerificationCode": "Send verification code",
|
|
108
|
+
"verificationCodeLabel": "Email verification code",
|
|
109
|
+
"verificationCodePlaceholder": "Enter 6-digit code",
|
|
110
|
+
"resendIn": "Resend in {seconds}s",
|
|
111
|
+
"resendCode": "Resend code",
|
|
112
|
+
"resetPasswordTitle": "Set new password",
|
|
113
|
+
"resetPasswordSubtitle": "Enter your new password below.",
|
|
114
|
+
"newPasswordLabel": "New password",
|
|
115
|
+
"newPasswordPlaceholder": "••••••••",
|
|
116
|
+
"confirmPasswordLabel": "Confirm new password",
|
|
117
|
+
"confirmPasswordPlaceholder": "••••••••",
|
|
118
|
+
"resetPassword": "Reset password",
|
|
119
|
+
"changeEmail": "Change email",
|
|
120
|
+
"forgotPasswordSent": "A reset link has been sent to {email}. Please check your inbox.",
|
|
121
|
+
"sendResetLink": "Send reset link",
|
|
122
|
+
"backToLogin": "Back to login",
|
|
123
|
+
"resetLinkExpired": "The reset link has expired. Please request a new one.",
|
|
124
|
+
"resetPasswordSuccess": "Password updated. Please log in."
|
|
125
|
+
},
|
|
126
|
+
"settings": {
|
|
127
|
+
"profile": {
|
|
128
|
+
"title": "Profile Settings",
|
|
129
|
+
"subtitle": "Manage your account settings and preferences.",
|
|
130
|
+
"profileCardTitle": "Profile",
|
|
131
|
+
"profileCardDescription": "Your public profile information.",
|
|
132
|
+
"avatarUploadComingSoon": "Avatar upload feature coming soon.",
|
|
133
|
+
"displayNameCardTitle": "Display Name",
|
|
134
|
+
"displayNameCardDescription": "Update your display name shown across the app.",
|
|
135
|
+
"displayNameLabel": "Display Name",
|
|
136
|
+
"displayNamePlaceholder": "Enter your display name",
|
|
137
|
+
"saveChanges": "Save Changes",
|
|
138
|
+
"displayNameUpdated": "Display name updated successfully",
|
|
139
|
+
"emailCardTitle": "Email Address",
|
|
140
|
+
"emailCardDescription": "Your primary email address.",
|
|
141
|
+
"contactSupportToChange": "To change your email, please contact support.",
|
|
142
|
+
"passwordCardTitle": "Password",
|
|
143
|
+
"passwordCardDescription": "Change your password.",
|
|
144
|
+
"changePassword": "Change Password"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"pages": {
|
|
148
|
+
"help": {
|
|
149
|
+
"title": "Help Center",
|
|
150
|
+
"description": "Find answers to common questions.",
|
|
151
|
+
"placeholder": "We are currently compiling our help documentation. Please check back later."
|
|
152
|
+
},
|
|
153
|
+
"privacy": {
|
|
154
|
+
"title": "Privacy Policy",
|
|
155
|
+
"description": "How we handle your data.",
|
|
156
|
+
"placeholder": "This template page is a placeholder. Replace it with your actual privacy policy before shipping."
|
|
157
|
+
},
|
|
158
|
+
"about": {
|
|
159
|
+
"title": "About Us",
|
|
160
|
+
"description": "Learn more about AgentDock.",
|
|
161
|
+
"placeholder": "This page is a placeholder. Replace it with your actual about information before shipping."
|
|
162
|
+
}
|
|
37
163
|
}
|
|
38
164
|
}
|
|
@@ -3,6 +3,73 @@
|
|
|
3
3
|
"title": "欢迎使用 AgentDock Web 模板",
|
|
4
4
|
"description": "基于四层架构的 Next.js 脚手架。"
|
|
5
5
|
},
|
|
6
|
+
"landing": {
|
|
7
|
+
"nav": {
|
|
8
|
+
"features": "功能",
|
|
9
|
+
"pricing": "定价",
|
|
10
|
+
"docs": "文档",
|
|
11
|
+
"login": "登录",
|
|
12
|
+
"getStarted": "开始使用"
|
|
13
|
+
},
|
|
14
|
+
"hero": {
|
|
15
|
+
"badge": "开源",
|
|
16
|
+
"title": "用 AgentDock 更快地构建 SaaS",
|
|
17
|
+
"subtitle": "一个生产就绪的 Next.js 脚手架,包含四层架构、Supabase 认证、国际化和 AI 编码代理集成。",
|
|
18
|
+
"ctaPrimary": "免费开始",
|
|
19
|
+
"ctaSecondary": "查看文档"
|
|
20
|
+
},
|
|
21
|
+
"features": {
|
|
22
|
+
"title": "开箱即用的功能",
|
|
23
|
+
"subtitle": "内置功能让你专注于产品,而非样板代码。",
|
|
24
|
+
"items": {
|
|
25
|
+
"auth": {
|
|
26
|
+
"title": "认证系统",
|
|
27
|
+
"description": "完整的认证流程,支持邮箱/密码、OAuth 和密码重置。由 Supabase 保障安全。"
|
|
28
|
+
},
|
|
29
|
+
"monorepo": {
|
|
30
|
+
"title": "Monorepo 就绪",
|
|
31
|
+
"description": "Turborepo + pnpm 工作区,包含共享的类型、ESLint 和工具包。"
|
|
32
|
+
},
|
|
33
|
+
"aiCoding": {
|
|
34
|
+
"title": "AI 编码就绪",
|
|
35
|
+
"description": "为 AI 代理优化的结构,包含清晰的契约、设计系统和 Copilot 指令。"
|
|
36
|
+
},
|
|
37
|
+
"docs": {
|
|
38
|
+
"title": "文档协同进化",
|
|
39
|
+
"description": "基于 Fumadocs 的文档系统,与应用共同成长。"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"howItWorks": {
|
|
44
|
+
"title": "工作原理",
|
|
45
|
+
"subtitle": "三个简单步骤即可启动运行。",
|
|
46
|
+
"steps": {
|
|
47
|
+
"step1": {
|
|
48
|
+
"title": "脚手架",
|
|
49
|
+
"description": "运行 CLI 生成包含所有功能的初始项目。"
|
|
50
|
+
},
|
|
51
|
+
"step2": {
|
|
52
|
+
"title": "自定义",
|
|
53
|
+
"description": "调整 token、颜色和内容以匹配你的品牌。"
|
|
54
|
+
},
|
|
55
|
+
"step3": {
|
|
56
|
+
"title": "发布",
|
|
57
|
+
"description": "一条命令部署到 Vercel,开始构建。"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"pricing": {
|
|
62
|
+
"title": "定价",
|
|
63
|
+
"description": "定价方案即将推出,欢迎联系我们获取早期访问。",
|
|
64
|
+
"cta": "联系我们"
|
|
65
|
+
},
|
|
66
|
+
"footer": {
|
|
67
|
+
"help": "帮助",
|
|
68
|
+
"privacy": "隐私",
|
|
69
|
+
"about": "关于",
|
|
70
|
+
"copyright": "© {year} AgentDock。保留所有权利。"
|
|
71
|
+
}
|
|
72
|
+
},
|
|
6
73
|
"auth": {
|
|
7
74
|
"loginTitle": "欢迎回来",
|
|
8
75
|
"loginSubtitle": "登录您的账号",
|
|
@@ -33,6 +100,65 @@
|
|
|
33
100
|
"termsText": "点击继续即表示您同意我们的",
|
|
34
101
|
"termsLink": "服务条款",
|
|
35
102
|
"andText": "和",
|
|
36
|
-
"privacyLink": "隐私政策"
|
|
103
|
+
"privacyLink": "隐私政策",
|
|
104
|
+
"forgotPasswordLink": "忘记密码?",
|
|
105
|
+
"forgotPasswordTitle": "重置密码",
|
|
106
|
+
"forgotPasswordSubtitle": "输入您的邮箱,我们将发送验证码。",
|
|
107
|
+
"sendVerificationCode": "发送验证码",
|
|
108
|
+
"verificationCodeLabel": "邮箱验证码",
|
|
109
|
+
"verificationCodePlaceholder": "输入 6 位验证码",
|
|
110
|
+
"resendIn": "{seconds} 秒后重新发送",
|
|
111
|
+
"resendCode": "重新发送验证码",
|
|
112
|
+
"resetPasswordTitle": "设置新密码",
|
|
113
|
+
"resetPasswordSubtitle": "在下方输入您的新密码。",
|
|
114
|
+
"newPasswordLabel": "新密码",
|
|
115
|
+
"newPasswordPlaceholder": "••••••••",
|
|
116
|
+
"confirmPasswordLabel": "确认新密码",
|
|
117
|
+
"confirmPasswordPlaceholder": "••••••••",
|
|
118
|
+
"resetPassword": "立即重置",
|
|
119
|
+
"changeEmail": "更换邮箱",
|
|
120
|
+
"forgotPasswordSent": "重置链接已发送至 {email},请查收邮件。",
|
|
121
|
+
"sendResetLink": "发送重置链接",
|
|
122
|
+
"backToLogin": "返回登录",
|
|
123
|
+
"resetLinkExpired": "重置链接已过期,请重新申请。",
|
|
124
|
+
"resetPasswordSuccess": "密码已更新,请登录。"
|
|
125
|
+
},
|
|
126
|
+
"settings": {
|
|
127
|
+
"profile": {
|
|
128
|
+
"title": "个人资料设置",
|
|
129
|
+
"subtitle": "管理您的账号设置和偏好。",
|
|
130
|
+
"profileCardTitle": "个人资料",
|
|
131
|
+
"profileCardDescription": "您的公开资料信息。",
|
|
132
|
+
"avatarUploadComingSoon": "头像上传功能即将推出。",
|
|
133
|
+
"displayNameCardTitle": "显示名",
|
|
134
|
+
"displayNameCardDescription": "更新您在应用中显示的昵称。",
|
|
135
|
+
"displayNameLabel": "显示名",
|
|
136
|
+
"displayNamePlaceholder": "输入您的显示名",
|
|
137
|
+
"saveChanges": "保存更改",
|
|
138
|
+
"displayNameUpdated": "显示名已更新",
|
|
139
|
+
"emailCardTitle": "邮箱地址",
|
|
140
|
+
"emailCardDescription": "您的主要邮箱地址。",
|
|
141
|
+
"contactSupportToChange": "如需修改邮箱,请联系支持。",
|
|
142
|
+
"passwordCardTitle": "密码",
|
|
143
|
+
"passwordCardDescription": "修改您的密码。",
|
|
144
|
+
"changePassword": "修改密码"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"pages": {
|
|
148
|
+
"help": {
|
|
149
|
+
"title": "帮助中心",
|
|
150
|
+
"description": "查找常见问题的答案。",
|
|
151
|
+
"placeholder": "我们正在整理帮助文档,请稍后查看。"
|
|
152
|
+
},
|
|
153
|
+
"privacy": {
|
|
154
|
+
"title": "隐私政策",
|
|
155
|
+
"description": "我们如何处理您的数据。",
|
|
156
|
+
"placeholder": "此页面为占位符。在发布前请替换为实际的隐私政策。"
|
|
157
|
+
},
|
|
158
|
+
"about": {
|
|
159
|
+
"title": "关于我们",
|
|
160
|
+
"description": "了解更多关于 AgentDock 的信息。",
|
|
161
|
+
"placeholder": "此页面为占位符。在发布前请替换为实际的关于我们信息。"
|
|
162
|
+
}
|
|
37
163
|
}
|
|
38
164
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="next" />
|
|
2
2
|
/// <reference types="next/image-types/global" />
|
|
3
|
-
import "./.next/types/routes.d.ts";
|
|
3
|
+
import "./.next/dev/types/routes.d.ts";
|
|
4
4
|
|
|
5
5
|
// NOTE: This file should not be edited
|
|
6
6
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
|
@@ -5,9 +5,9 @@ import path from 'node:path'
|
|
|
5
5
|
const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts')
|
|
6
6
|
|
|
7
7
|
const nextConfig: NextConfig = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
turbopack: {
|
|
9
|
+
root: path.resolve(process.cwd(), '../..'),
|
|
10
|
+
},
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export default withNextIntl(nextConfig)
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useActionState, useEffect, useState, startTransition } from 'react'
|
|
4
|
+
import { useParams } from 'next/navigation'
|
|
5
|
+
import Link from 'next/link'
|
|
6
|
+
import { useTranslations } from 'next-intl'
|
|
7
|
+
import { useForm } from 'react-hook-form'
|
|
8
|
+
import { zodResolver } from '@hookform/resolvers/zod'
|
|
9
|
+
import { GalleryVerticalEnd } from 'lucide-react'
|
|
10
|
+
import { toast } from 'sonner'
|
|
11
|
+
import { Button } from '@/components/ui/button'
|
|
12
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
|
|
13
|
+
import { Input } from '@/components/ui/input'
|
|
14
|
+
import { Field, FieldDescription, FieldGroup, FieldLabel } from '@/components/ui/field'
|
|
15
|
+
import { sendPasswordResetOTP, resetPasswordWithOTP } from '@/features/auth'
|
|
16
|
+
import { resetPasswordWithOTPSchema, type ResetPasswordWithOTPInput } from '@/lib/validations/auth'
|
|
17
|
+
import type { ActionResult } from '@/core/types/auth'
|
|
18
|
+
|
|
19
|
+
export default function ForgotPasswordPage() {
|
|
20
|
+
const t = useTranslations('auth')
|
|
21
|
+
const routeParams = useParams<{ locale: string }>()
|
|
22
|
+
const locale = routeParams.locale ?? 'en'
|
|
23
|
+
const [step, setStep] = useState<'send' | 'verify'>('send')
|
|
24
|
+
const [email, setEmail] = useState('')
|
|
25
|
+
const [countdown, setCountdown] = useState(0)
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
register,
|
|
29
|
+
handleSubmit,
|
|
30
|
+
formState: { errors },
|
|
31
|
+
reset,
|
|
32
|
+
setValue,
|
|
33
|
+
} = useForm<ResetPasswordWithOTPInput>({
|
|
34
|
+
resolver: zodResolver(resetPasswordWithOTPSchema),
|
|
35
|
+
defaultValues: { email: '', token: '', password: '', confirmPassword: '' },
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const [sendState, sendAction, isSendPending] = useActionState<ActionResult | null, FormData>(
|
|
39
|
+
sendPasswordResetOTP,
|
|
40
|
+
null,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
const [verifyState, verifyAction, isVerifyPending] = useActionState<
|
|
44
|
+
ActionResult | null,
|
|
45
|
+
FormData
|
|
46
|
+
>(resetPasswordWithOTP, null)
|
|
47
|
+
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (sendState?.error) {
|
|
50
|
+
toast.error(sendState.error)
|
|
51
|
+
} else if (sendState?.data !== undefined && sendState.error === null) {
|
|
52
|
+
toast.success('验证码已发送,请检查邮箱')
|
|
53
|
+
setStep('verify')
|
|
54
|
+
startCountdown()
|
|
55
|
+
}
|
|
56
|
+
}, [sendState])
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (verifyState?.error) {
|
|
60
|
+
toast.error(verifyState.error)
|
|
61
|
+
}
|
|
62
|
+
}, [verifyState])
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (countdown > 0) {
|
|
66
|
+
const timer = setTimeout(() => setCountdown(countdown - 1), 1000)
|
|
67
|
+
return () => clearTimeout(timer)
|
|
68
|
+
}
|
|
69
|
+
return undefined
|
|
70
|
+
}, [countdown])
|
|
71
|
+
|
|
72
|
+
function startCountdown() {
|
|
73
|
+
setCountdown(60)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function onSendOTP(data: { email: string }) {
|
|
77
|
+
const formData = new FormData()
|
|
78
|
+
formData.append('email', data.email)
|
|
79
|
+
setEmail(data.email)
|
|
80
|
+
setValue('email', data.email)
|
|
81
|
+
startTransition(async () => {
|
|
82
|
+
await sendAction(formData)
|
|
83
|
+
})
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function onResendOTP() {
|
|
87
|
+
if (countdown > 0) return
|
|
88
|
+
const formData = new FormData()
|
|
89
|
+
formData.append('email', email)
|
|
90
|
+
startTransition(async () => {
|
|
91
|
+
await sendAction(formData)
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function onSubmit(data: ResetPasswordWithOTPInput) {
|
|
96
|
+
const formData = new FormData()
|
|
97
|
+
formData.append('email', data.email)
|
|
98
|
+
formData.append('token', data.token)
|
|
99
|
+
formData.append('password', data.password)
|
|
100
|
+
formData.append('confirmPassword', data.confirmPassword)
|
|
101
|
+
formData.append('locale', locale)
|
|
102
|
+
startTransition(async () => {
|
|
103
|
+
await verifyAction(formData)
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (step === 'send') {
|
|
108
|
+
return (
|
|
109
|
+
<div className="flex min-h-svh flex-col items-center justify-center gap-6 bg-muted p-6 md:p-10">
|
|
110
|
+
<div className="flex w-full max-w-sm flex-col gap-6">
|
|
111
|
+
<Link href={`/${locale}`} className="flex items-center gap-2 self-center font-medium">
|
|
112
|
+
<div className="flex size-6 items-center justify-center rounded-md bg-primary text-primary-foreground">
|
|
113
|
+
<GalleryVerticalEnd className="size-4" />
|
|
114
|
+
</div>
|
|
115
|
+
AgentDock
|
|
116
|
+
</Link>
|
|
117
|
+
|
|
118
|
+
<Card>
|
|
119
|
+
<CardHeader className="text-center">
|
|
120
|
+
<CardTitle className="text-xl">{t('forgotPasswordTitle')}</CardTitle>
|
|
121
|
+
<CardDescription>{t('forgotPasswordSubtitle')}</CardDescription>
|
|
122
|
+
</CardHeader>
|
|
123
|
+
<CardContent>
|
|
124
|
+
<form onSubmit={handleSubmit(onSendOTP)}>
|
|
125
|
+
<FieldGroup>
|
|
126
|
+
<Field>
|
|
127
|
+
<FieldLabel htmlFor="email">{t('emailLabel')}</FieldLabel>
|
|
128
|
+
<Input
|
|
129
|
+
id="email"
|
|
130
|
+
type="email"
|
|
131
|
+
placeholder={t('emailPlaceholder')}
|
|
132
|
+
autoComplete="email"
|
|
133
|
+
{...register('email')}
|
|
134
|
+
/>
|
|
135
|
+
{errors.email && (
|
|
136
|
+
<FieldDescription className="text-destructive">
|
|
137
|
+
{errors.email.message}
|
|
138
|
+
</FieldDescription>
|
|
139
|
+
)}
|
|
140
|
+
</Field>
|
|
141
|
+
<Field>
|
|
142
|
+
<Button type="submit" className="w-full" disabled={isSendPending}>
|
|
143
|
+
{isSendPending ? '\u2026' : t('sendVerificationCode')}
|
|
144
|
+
</Button>
|
|
145
|
+
<FieldDescription className="text-center">
|
|
146
|
+
<Link href={`/${locale}/login`} className="underline underline-offset-4">
|
|
147
|
+
{t('backToLogin')}
|
|
148
|
+
</Link>
|
|
149
|
+
</FieldDescription>
|
|
150
|
+
</Field>
|
|
151
|
+
</FieldGroup>
|
|
152
|
+
</form>
|
|
153
|
+
</CardContent>
|
|
154
|
+
</Card>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// step === 'verify'
|
|
161
|
+
return (
|
|
162
|
+
<div className="flex min-h-svh flex-col items-center justify-center gap-6 bg-muted p-6 md:p-10">
|
|
163
|
+
<div className="flex w-full max-w-sm flex-col gap-6">
|
|
164
|
+
<Link href={`/${locale}`} className="flex items-center gap-2 self-center font-medium">
|
|
165
|
+
<div className="flex size-6 items-center justify-center rounded-md bg-primary text-primary-foreground">
|
|
166
|
+
<GalleryVerticalEnd className="size-4" />
|
|
167
|
+
</div>
|
|
168
|
+
AgentDock
|
|
169
|
+
</Link>
|
|
170
|
+
|
|
171
|
+
<Card>
|
|
172
|
+
<CardHeader className="text-center">
|
|
173
|
+
<CardTitle className="text-xl">{t('resetPasswordTitle')}</CardTitle>
|
|
174
|
+
<CardDescription>{t('resetPasswordSubtitle')}</CardDescription>
|
|
175
|
+
</CardHeader>
|
|
176
|
+
<CardContent>
|
|
177
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
178
|
+
<input type="hidden" name="locale" value={locale} />
|
|
179
|
+
<FieldGroup>
|
|
180
|
+
<Field>
|
|
181
|
+
<FieldLabel htmlFor="email">{t('emailLabel')}</FieldLabel>
|
|
182
|
+
<Input id="email" type="email" readOnly value={email} {...register('email')} />
|
|
183
|
+
</Field>
|
|
184
|
+
<Field>
|
|
185
|
+
<FieldLabel htmlFor="token">{t('verificationCodeLabel')}</FieldLabel>
|
|
186
|
+
<Input
|
|
187
|
+
id="token"
|
|
188
|
+
type="text"
|
|
189
|
+
placeholder={t('verificationCodePlaceholder')}
|
|
190
|
+
maxLength={6}
|
|
191
|
+
{...register('token')}
|
|
192
|
+
/>
|
|
193
|
+
{errors.token && (
|
|
194
|
+
<FieldDescription className="text-destructive">
|
|
195
|
+
{errors.token.message}
|
|
196
|
+
</FieldDescription>
|
|
197
|
+
)}
|
|
198
|
+
<FieldDescription className="mt-1">
|
|
199
|
+
{countdown > 0 ? (
|
|
200
|
+
<span className="text-muted-foreground">
|
|
201
|
+
{t('resendIn', { seconds: countdown })}
|
|
202
|
+
</span>
|
|
203
|
+
) : (
|
|
204
|
+
<button
|
|
205
|
+
type="button"
|
|
206
|
+
onClick={onResendOTP}
|
|
207
|
+
className="text-sm text-primary hover:underline"
|
|
208
|
+
>
|
|
209
|
+
{t('resendCode')}
|
|
210
|
+
</button>
|
|
211
|
+
)}
|
|
212
|
+
</FieldDescription>
|
|
213
|
+
</Field>
|
|
214
|
+
<Field>
|
|
215
|
+
<FieldLabel htmlFor="password">{t('newPasswordLabel')}</FieldLabel>
|
|
216
|
+
<Input
|
|
217
|
+
id="password"
|
|
218
|
+
type="password"
|
|
219
|
+
placeholder={t('newPasswordPlaceholder')}
|
|
220
|
+
autoComplete="new-password"
|
|
221
|
+
{...register('password')}
|
|
222
|
+
/>
|
|
223
|
+
{errors.password && (
|
|
224
|
+
<FieldDescription className="text-destructive">
|
|
225
|
+
{errors.password.message}
|
|
226
|
+
</FieldDescription>
|
|
227
|
+
)}
|
|
228
|
+
</Field>
|
|
229
|
+
<Field>
|
|
230
|
+
<FieldLabel htmlFor="confirmPassword">{t('confirmPasswordLabel')}</FieldLabel>
|
|
231
|
+
<Input
|
|
232
|
+
id="confirmPassword"
|
|
233
|
+
type="password"
|
|
234
|
+
placeholder={t('confirmPasswordPlaceholder')}
|
|
235
|
+
autoComplete="new-password"
|
|
236
|
+
{...register('confirmPassword')}
|
|
237
|
+
/>
|
|
238
|
+
{errors.confirmPassword && (
|
|
239
|
+
<FieldDescription className="text-destructive">
|
|
240
|
+
{errors.confirmPassword.message}
|
|
241
|
+
</FieldDescription>
|
|
242
|
+
)}
|
|
243
|
+
</Field>
|
|
244
|
+
<Field>
|
|
245
|
+
<Button type="submit" className="w-full" disabled={isVerifyPending}>
|
|
246
|
+
{isVerifyPending ? '\u2026' : t('resetPassword')}
|
|
247
|
+
</Button>
|
|
248
|
+
<FieldDescription className="text-center">
|
|
249
|
+
<button
|
|
250
|
+
type="button"
|
|
251
|
+
onClick={() => setStep('send')}
|
|
252
|
+
className="text-sm text-muted-foreground hover:text-foreground"
|
|
253
|
+
>
|
|
254
|
+
{t('changeEmail')}
|
|
255
|
+
</button>
|
|
256
|
+
</FieldDescription>
|
|
257
|
+
</Field>
|
|
258
|
+
</FieldGroup>
|
|
259
|
+
</form>
|
|
260
|
+
</CardContent>
|
|
261
|
+
</Card>
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
)
|
|
265
|
+
}
|
|
@@ -28,7 +28,10 @@ export default function LoginPage() {
|
|
|
28
28
|
const routeParams = useParams<{ locale: string }>()
|
|
29
29
|
const locale = routeParams.locale ?? 'en'
|
|
30
30
|
|
|
31
|
-
const {
|
|
31
|
+
const {
|
|
32
|
+
register,
|
|
33
|
+
formState: { errors },
|
|
34
|
+
} = useForm<SignInInput>({
|
|
32
35
|
resolver: zodResolver(signInSchema),
|
|
33
36
|
defaultValues: { email: '', password: '' },
|
|
34
37
|
})
|
|
@@ -41,7 +44,10 @@ export default function LoginPage() {
|
|
|
41
44
|
|
|
42
45
|
async function handleGithub() {
|
|
43
46
|
const result = await signInWithGithubForLocale(locale)
|
|
44
|
-
if (result.error) {
|
|
47
|
+
if (result.error) {
|
|
48
|
+
toast.error(result.error)
|
|
49
|
+
return
|
|
50
|
+
}
|
|
45
51
|
if (result.data?.url) router.push(result.data.url)
|
|
46
52
|
}
|
|
47
53
|
|
|
@@ -72,7 +78,11 @@ export default function LoginPage() {
|
|
|
72
78
|
className="w-full"
|
|
73
79
|
onClick={handleGithub}
|
|
74
80
|
>
|
|
75
|
-
<svg
|
|
81
|
+
<svg
|
|
82
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
83
|
+
viewBox="0 0 24 24"
|
|
84
|
+
className="size-4"
|
|
85
|
+
>
|
|
76
86
|
<path
|
|
77
87
|
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
|
78
88
|
fill="currentColor"
|
|
@@ -98,8 +108,14 @@ export default function LoginPage() {
|
|
|
98
108
|
)}
|
|
99
109
|
</Field>
|
|
100
110
|
<Field>
|
|
101
|
-
<div className="flex items-center">
|
|
111
|
+
<div className="flex items-center justify-between">
|
|
102
112
|
<FieldLabel htmlFor="password">{t('passwordLabel')}</FieldLabel>
|
|
113
|
+
<Link
|
|
114
|
+
href={`/${locale}/forgot-password`}
|
|
115
|
+
className="text-sm text-muted-foreground underline underline-offset-4 hover:text-foreground"
|
|
116
|
+
>
|
|
117
|
+
{t('forgotPasswordLink')}
|
|
118
|
+
</Link>
|
|
103
119
|
</div>
|
|
104
120
|
<Input
|
|
105
121
|
id="password"
|