@cogito.ai/cli 0.4.2 → 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 +81 -5
- package/dist/templates/web-nextjs/apps/web/messages/zh.json +81 -5
- 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 +174 -39
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/login/page.tsx +13 -3
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/reset-password/page.tsx +4 -1
- 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 +1 -5
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/settings/profile/page.tsx +2 -8
- package/dist/templates/web-nextjs/apps/web/src/app/[locale]/about/page.tsx +3 -6
- 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 +1 -5
- 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 +3 -6
- 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 +2 -3
- package/dist/templates/web-nextjs/apps/web/src/components/dashboard/app-sidebar.tsx +13 -16
- 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 +12 -21
- 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 +6 -4
- 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 +2 -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/actions.ts +57 -1
- package/dist/templates/web-nextjs/apps/web/src/features/auth/index.ts +2 -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 +48 -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 +13 -0
- package/dist/templates/web-nextjs/apps/web/src/styles/tokens.css +58 -0
- package/package.json +1 -1
|
@@ -2,7 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
import { redirect } from 'next/navigation'
|
|
4
4
|
import { getAuthRepository } from '@/infra/providers'
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
signInSchema,
|
|
7
|
+
signUpSchema,
|
|
8
|
+
forgotPasswordSchema,
|
|
9
|
+
resetPasswordWithOTPSchema,
|
|
10
|
+
resetPasswordSchema,
|
|
11
|
+
displayNameSchema,
|
|
12
|
+
} from '@/lib/validations/auth'
|
|
6
13
|
import type { ActionResult } from '@/core/types/auth'
|
|
7
14
|
import { defaultLocale, isLocale } from '@/i18n/config'
|
|
8
15
|
import type { SignUpSuccessData, OAuthData } from './__contract__'
|
|
@@ -153,3 +160,52 @@ export async function updateDisplayName(
|
|
|
153
160
|
|
|
154
161
|
return { data: undefined as void, error: null }
|
|
155
162
|
}
|
|
163
|
+
|
|
164
|
+
export async function sendPasswordResetOTP(
|
|
165
|
+
_prevState: ActionResult | null,
|
|
166
|
+
formData: FormData,
|
|
167
|
+
): Promise<ActionResult<void>> {
|
|
168
|
+
const email = formData.get('email')
|
|
169
|
+
if (!email || typeof email !== 'string') {
|
|
170
|
+
return { data: null, error: '邮箱不能为空' }
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const repo = getAuthRepository()
|
|
174
|
+
const result = await repo.sendPasswordResetOTP(email)
|
|
175
|
+
|
|
176
|
+
if (result.error) {
|
|
177
|
+
return { data: null, error: result.error }
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return { data: undefined as void, error: null }
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export async function resetPasswordWithOTP(
|
|
184
|
+
_prevState: ActionResult | null,
|
|
185
|
+
formData: FormData,
|
|
186
|
+
): Promise<ActionResult<void>> {
|
|
187
|
+
const parsed = resetPasswordWithOTPSchema.safeParse({
|
|
188
|
+
email: formData.get('email'),
|
|
189
|
+
token: formData.get('token'),
|
|
190
|
+
password: formData.get('password'),
|
|
191
|
+
confirmPassword: formData.get('confirmPassword'),
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
if (!parsed.success) {
|
|
195
|
+
return { data: null, error: parsed.error.issues[0]?.message ?? '输入无效' }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const repo = getAuthRepository()
|
|
199
|
+
const result = await repo.verifyPasswordResetOTP(
|
|
200
|
+
parsed.data.email,
|
|
201
|
+
parsed.data.token,
|
|
202
|
+
parsed.data.password,
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if (result.error) {
|
|
206
|
+
return { data: null, error: result.error }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const locale = normalizeLocale(formData.get('locale'))
|
|
210
|
+
redirect(`/${locale}/login`)
|
|
211
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
|
-
import * as React from
|
|
3
|
+
import * as React from 'react'
|
|
4
4
|
|
|
5
5
|
const MOBILE_BREAKPOINT = 768
|
|
6
6
|
|
|
@@ -12,9 +12,9 @@ export function useIsMobile() {
|
|
|
12
12
|
const onChange = () => {
|
|
13
13
|
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
14
14
|
}
|
|
15
|
-
mql.addEventListener(
|
|
15
|
+
mql.addEventListener('change', onChange)
|
|
16
16
|
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
|
17
|
-
return () => mql.removeEventListener(
|
|
17
|
+
return () => mql.removeEventListener('change', onChange)
|
|
18
18
|
}, [])
|
|
19
19
|
|
|
20
20
|
return !!isMobile
|
|
@@ -20,9 +20,7 @@ export class SupabaseAuthRepository implements IAuthRepository {
|
|
|
20
20
|
const supabase = await getServerClient()
|
|
21
21
|
const { data, error } = await supabase.auth.signUp({ email, password })
|
|
22
22
|
if (error) {
|
|
23
|
-
const msg = error.message.toLowerCase().includes('already')
|
|
24
|
-
? '该邮箱已被注册'
|
|
25
|
-
: error.message
|
|
23
|
+
const msg = error.message.toLowerCase().includes('already') ? '该邮箱已被注册' : error.message
|
|
26
24
|
return { user: null, error: msg }
|
|
27
25
|
}
|
|
28
26
|
const u = data.user
|
|
@@ -39,7 +37,9 @@ export class SupabaseAuthRepository implements IAuthRepository {
|
|
|
39
37
|
|
|
40
38
|
async getSession(): Promise<AuthUser | null> {
|
|
41
39
|
const supabase = await getServerClient()
|
|
42
|
-
const {
|
|
40
|
+
const {
|
|
41
|
+
data: { user },
|
|
42
|
+
} = await supabase.auth.getUser()
|
|
43
43
|
if (!user) return null
|
|
44
44
|
return { id: user.id, email: user.email ?? '', createdAt: user.created_at }
|
|
45
45
|
}
|
|
@@ -85,4 +85,48 @@ export class SupabaseAuthRepository implements IAuthRepository {
|
|
|
85
85
|
}
|
|
86
86
|
return { user: null, error: null }
|
|
87
87
|
}
|
|
88
|
+
|
|
89
|
+
async sendPasswordResetOTP(email: string): Promise<AuthResult> {
|
|
90
|
+
const supabase = await getServerClient()
|
|
91
|
+
const { error } = await supabase.auth.signInWithOtp({
|
|
92
|
+
email,
|
|
93
|
+
options: {
|
|
94
|
+
shouldCreateUser: false, // Don't create user if not exists
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
if (error) {
|
|
98
|
+
return { user: null, error: error.message }
|
|
99
|
+
}
|
|
100
|
+
return { user: null, error: null }
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async verifyPasswordResetOTP(
|
|
104
|
+
email: string,
|
|
105
|
+
token: string,
|
|
106
|
+
newPassword: string,
|
|
107
|
+
): Promise<AuthResult> {
|
|
108
|
+
const supabase = await getServerClient()
|
|
109
|
+
const { data, error } = await supabase.auth.verifyOtp({
|
|
110
|
+
email,
|
|
111
|
+
token,
|
|
112
|
+
type: 'email',
|
|
113
|
+
})
|
|
114
|
+
if (error) {
|
|
115
|
+
return { user: null, error: error.message }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// After verifying OTP, update the password
|
|
119
|
+
const { error: updateError } = await supabase.auth.updateUser({
|
|
120
|
+
password: newPassword,
|
|
121
|
+
})
|
|
122
|
+
if (updateError) {
|
|
123
|
+
return { user: null, error: updateError.message }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const u = data.user
|
|
127
|
+
return {
|
|
128
|
+
user: u ? { id: u.id, email: u.email ?? '', createdAt: u.created_at } : null,
|
|
129
|
+
error: null,
|
|
130
|
+
}
|
|
131
|
+
}
|
|
88
132
|
}
|
|
@@ -66,10 +66,7 @@ export function getBrowserClient() {
|
|
|
66
66
|
* await supabase.auth.getUser() // refreshes session token
|
|
67
67
|
* ```
|
|
68
68
|
*/
|
|
69
|
-
export function createMiddlewareClient(
|
|
70
|
-
request: NextRequest,
|
|
71
|
-
response: NextResponse,
|
|
72
|
-
) {
|
|
69
|
+
export function createMiddlewareClient(request: NextRequest, response: NextResponse) {
|
|
73
70
|
const supabase = createServerClient(
|
|
74
71
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
75
72
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
@@ -20,6 +20,18 @@ export const forgotPasswordSchema = z.object({
|
|
|
20
20
|
email: z.string().email('请输入有效邮箱'),
|
|
21
21
|
})
|
|
22
22
|
|
|
23
|
+
export const resetPasswordWithOTPSchema = z
|
|
24
|
+
.object({
|
|
25
|
+
email: z.string().email('请输入有效邮箱'),
|
|
26
|
+
token: z.string().min(6, '验证码至少 6 位'),
|
|
27
|
+
password: z.string().min(8, '密码至少 8 位'),
|
|
28
|
+
confirmPassword: z.string(),
|
|
29
|
+
})
|
|
30
|
+
.refine((data) => data.password === data.confirmPassword, {
|
|
31
|
+
message: '两次密码不一致',
|
|
32
|
+
path: ['confirmPassword'],
|
|
33
|
+
})
|
|
34
|
+
|
|
23
35
|
export const resetPasswordSchema = z
|
|
24
36
|
.object({
|
|
25
37
|
password: z.string().min(8, '密码至少 8 位'),
|
|
@@ -37,5 +49,6 @@ export const displayNameSchema = z.object({
|
|
|
37
49
|
export type SignInInput = z.infer<typeof signInSchema>
|
|
38
50
|
export type SignUpInput = z.infer<typeof signUpSchema>
|
|
39
51
|
export type ForgotPasswordInput = z.infer<typeof forgotPasswordSchema>
|
|
52
|
+
export type ResetPasswordWithOTPInput = z.infer<typeof resetPasswordWithOTPSchema>
|
|
40
53
|
export type ResetPasswordInput = z.infer<typeof resetPasswordSchema>
|
|
41
54
|
export type DisplayNameInput = z.infer<typeof displayNameSchema>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
@layer base {
|
|
2
|
+
:root {
|
|
3
|
+
/* Brand tokens — downstream projects override these three variables to change the theme color */
|
|
4
|
+
--color-brand-h: 262;
|
|
5
|
+
--color-brand-c: 0.26;
|
|
6
|
+
--color-brand-l: 0.56;
|
|
7
|
+
|
|
8
|
+
/* Semantic color tokens */
|
|
9
|
+
--color-primary: oklch(var(--color-brand-l) var(--color-brand-c) var(--color-brand-h));
|
|
10
|
+
--color-primary-foreground: oklch(0.98 0 0);
|
|
11
|
+
|
|
12
|
+
/* Neutral surface tokens (with subtle hue, not pure gray) */
|
|
13
|
+
--color-surface: oklch(0.985 0.003 262);
|
|
14
|
+
--color-surface-elevated: oklch(1 0 0);
|
|
15
|
+
--color-muted: oklch(0.94 0.004 262);
|
|
16
|
+
--color-muted-foreground: oklch(0.52 0.012 262);
|
|
17
|
+
--color-border: oklch(0.88 0.006 262);
|
|
18
|
+
|
|
19
|
+
/* Spacing tokens (4px baseline) */
|
|
20
|
+
--space-1: 4px;
|
|
21
|
+
--space-2: 8px;
|
|
22
|
+
--space-3: 12px;
|
|
23
|
+
--space-4: 16px;
|
|
24
|
+
--space-5: 20px;
|
|
25
|
+
--space-6: 24px;
|
|
26
|
+
--space-8: 32px;
|
|
27
|
+
--space-10: 40px;
|
|
28
|
+
--space-12: 48px;
|
|
29
|
+
--space-16: 64px;
|
|
30
|
+
--space-20: 80px;
|
|
31
|
+
--space-24: 96px;
|
|
32
|
+
--space-section: clamp(64px, 10vw, 120px);
|
|
33
|
+
|
|
34
|
+
/* Font tokens */
|
|
35
|
+
--font-heading: var(--font-geist, 'Geist', system-ui, sans-serif);
|
|
36
|
+
--font-body: var(--font-geist, 'Geist', system-ui, sans-serif);
|
|
37
|
+
|
|
38
|
+
/* Text size tokens */
|
|
39
|
+
--text-xs: 0.75rem;
|
|
40
|
+
--text-sm: 0.875rem;
|
|
41
|
+
--text-base: 1rem;
|
|
42
|
+
--text-lg: 1.125rem;
|
|
43
|
+
--text-xl: 1.25rem;
|
|
44
|
+
--text-2xl: 1.5rem;
|
|
45
|
+
--text-3xl: 1.875rem;
|
|
46
|
+
--text-4xl: 2.25rem;
|
|
47
|
+
--text-5xl: 3rem;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.dark {
|
|
51
|
+
/* Dark mode semantic tokens */
|
|
52
|
+
--color-surface: oklch(0.13 0.008 262);
|
|
53
|
+
--color-surface-elevated: oklch(0.18 0.007 262);
|
|
54
|
+
--color-muted: oklch(0.22 0.007 262);
|
|
55
|
+
--color-muted-foreground: oklch(0.62 0.010 262);
|
|
56
|
+
--color-border: oklch(0.28 0.008 262);
|
|
57
|
+
}
|
|
58
|
+
}
|