@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.
Files changed (105) hide show
  1. package/README.md +29 -22
  2. package/dist/index.js +9 -15
  3. package/dist/templates/web-nextjs/.github/copilot-instructions.md +5 -6
  4. package/dist/templates/web-nextjs/README.md +25 -24
  5. package/dist/templates/web-nextjs/apps/docs/.source/browser.ts +18 -8
  6. package/dist/templates/web-nextjs/apps/docs/.source/dynamic.ts +11 -5
  7. package/dist/templates/web-nextjs/apps/docs/.source/server.ts +37 -17
  8. package/dist/templates/web-nextjs/apps/docs/app/docs/[[...slug]]/page.tsx +1 -6
  9. package/dist/templates/web-nextjs/apps/docs/app/docs/layout.tsx +1 -4
  10. package/dist/templates/web-nextjs/apps/docs/app/llms-full.txt/route.ts +3 -1
  11. package/dist/templates/web-nextjs/apps/docs/next-env.d.ts +1 -1
  12. package/dist/templates/web-nextjs/apps/web/.github/copilot-instructions.md +53 -6
  13. package/dist/templates/web-nextjs/apps/web/.github/skills/impeccable/SKILL.md +55 -0
  14. package/dist/templates/web-nextjs/apps/web/DESIGN.md +65 -0
  15. package/dist/templates/web-nextjs/apps/web/messages/en.json +127 -1
  16. package/dist/templates/web-nextjs/apps/web/messages/zh.json +127 -1
  17. package/dist/templates/web-nextjs/apps/web/next-env.d.ts +1 -1
  18. package/dist/templates/web-nextjs/apps/web/next.config.ts +3 -3
  19. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/forgot-password/page.tsx +265 -0
  20. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/login/page.tsx +20 -4
  21. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/reset-password/page.tsx +115 -0
  22. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(auth)/signup/page.tsx +18 -17
  23. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/dashboard/page.tsx +1 -5
  24. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/settings/layout.tsx +5 -0
  25. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/settings/page.tsx +9 -0
  26. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/(protected)/settings/profile/page.tsx +91 -0
  27. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/about/page.tsx +22 -0
  28. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/globals.css +17 -5
  29. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/help/page.tsx +21 -0
  30. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/layout.tsx +10 -8
  31. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/page.tsx +22 -6
  32. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/privacy/page.tsx +14 -15
  33. package/dist/templates/web-nextjs/apps/web/src/app/[locale]/terms/page.tsx +1 -5
  34. package/dist/templates/web-nextjs/apps/web/src/app/auth/callback/route.ts +7 -2
  35. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/app-sidebar.tsx +37 -137
  36. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/chart-area-interactive.tsx +122 -146
  37. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/data-table.tsx +84 -149
  38. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/nav-documents.tsx +7 -16
  39. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/nav-main.tsx +4 -4
  40. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/nav-secondary.tsx +4 -4
  41. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/nav-user.tsx +17 -33
  42. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/page.tsx +10 -13
  43. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/section-cards.tsx +5 -9
  44. package/dist/templates/web-nextjs/apps/web/src/components/dashboard/site-header.tsx +6 -7
  45. package/dist/templates/web-nextjs/apps/web/src/components/landing/features.tsx +63 -0
  46. package/dist/templates/web-nextjs/apps/web/src/components/landing/footer.tsx +48 -0
  47. package/dist/templates/web-nextjs/apps/web/src/components/landing/header.tsx +97 -0
  48. package/dist/templates/web-nextjs/apps/web/src/components/landing/hero.tsx +45 -0
  49. package/dist/templates/web-nextjs/apps/web/src/components/landing/how-it-works.tsx +35 -0
  50. package/dist/templates/web-nextjs/apps/web/src/components/landing/pricing-teaser.tsx +23 -0
  51. package/dist/templates/web-nextjs/apps/web/src/components/profile/profile-form.tsx +66 -0
  52. package/dist/templates/web-nextjs/apps/web/src/components/providers/theme-provider.tsx +16 -0
  53. package/dist/templates/web-nextjs/apps/web/src/components/ui/alert-dialog.tsx +32 -49
  54. package/dist/templates/web-nextjs/apps/web/src/components/ui/alert.tsx +16 -23
  55. package/dist/templates/web-nextjs/apps/web/src/components/ui/avatar.tsx +25 -38
  56. package/dist/templates/web-nextjs/apps/web/src/components/ui/badge.tsx +16 -18
  57. package/dist/templates/web-nextjs/apps/web/src/components/ui/breadcrumb.tsx +19 -26
  58. package/dist/templates/web-nextjs/apps/web/src/components/ui/button.tsx +23 -24
  59. package/dist/templates/web-nextjs/apps/web/src/components/ui/card.tsx +19 -36
  60. package/dist/templates/web-nextjs/apps/web/src/components/ui/chart.tsx +60 -94
  61. package/dist/templates/web-nextjs/apps/web/src/components/ui/checkbox.tsx +8 -11
  62. package/dist/templates/web-nextjs/apps/web/src/components/ui/collapsible.tsx +5 -17
  63. package/dist/templates/web-nextjs/apps/web/src/components/ui/command.tsx +25 -48
  64. package/dist/templates/web-nextjs/apps/web/src/components/ui/dialog.tsx +21 -35
  65. package/dist/templates/web-nextjs/apps/web/src/components/ui/drawer.tsx +24 -35
  66. package/dist/templates/web-nextjs/apps/web/src/components/ui/dropdown-menu.tsx +26 -55
  67. package/dist/templates/web-nextjs/apps/web/src/components/ui/field.tsx +62 -76
  68. package/dist/templates/web-nextjs/apps/web/src/components/ui/form.tsx +19 -34
  69. package/dist/templates/web-nextjs/apps/web/src/components/ui/input-otp.tsx +13 -20
  70. package/dist/templates/web-nextjs/apps/web/src/components/ui/input.tsx +6 -6
  71. package/dist/templates/web-nextjs/apps/web/src/components/ui/label.tsx +6 -6
  72. package/dist/templates/web-nextjs/apps/web/src/components/ui/pagination.tsx +21 -42
  73. package/dist/templates/web-nextjs/apps/web/src/components/ui/popover.tsx +16 -31
  74. package/dist/templates/web-nextjs/apps/web/src/components/ui/progress.tsx +5 -8
  75. package/dist/templates/web-nextjs/apps/web/src/components/ui/radio-group.tsx +8 -8
  76. package/dist/templates/web-nextjs/apps/web/src/components/ui/scroll-area.tsx +10 -12
  77. package/dist/templates/web-nextjs/apps/web/src/components/ui/select.tsx +26 -41
  78. package/dist/templates/web-nextjs/apps/web/src/components/ui/separator.tsx +7 -7
  79. package/dist/templates/web-nextjs/apps/web/src/components/ui/sheet.tsx +29 -38
  80. package/dist/templates/web-nextjs/apps/web/src/components/ui/sidebar.tsx +157 -189
  81. package/dist/templates/web-nextjs/apps/web/src/components/ui/skeleton.tsx +3 -3
  82. package/dist/templates/web-nextjs/apps/web/src/components/ui/slider.tsx +10 -15
  83. package/dist/templates/web-nextjs/apps/web/src/components/ui/sonner.tsx +13 -7
  84. package/dist/templates/web-nextjs/apps/web/src/components/ui/switch.tsx +9 -9
  85. package/dist/templates/web-nextjs/apps/web/src/components/ui/table.tsx +24 -48
  86. package/dist/templates/web-nextjs/apps/web/src/components/ui/tabs.tsx +21 -31
  87. package/dist/templates/web-nextjs/apps/web/src/components/ui/textarea.tsx +5 -5
  88. package/dist/templates/web-nextjs/apps/web/src/components/ui/theme-toggle.tsx +23 -0
  89. package/dist/templates/web-nextjs/apps/web/src/components/ui/toggle-group.tsx +15 -16
  90. package/dist/templates/web-nextjs/apps/web/src/components/ui/toggle.tsx +14 -15
  91. package/dist/templates/web-nextjs/apps/web/src/components/ui/tooltip.tsx +8 -12
  92. package/dist/templates/web-nextjs/apps/web/src/core/repositories/IAuthRepository.ts +5 -0
  93. package/dist/templates/web-nextjs/apps/web/src/core/types/auth.ts +1 -3
  94. package/dist/templates/web-nextjs/apps/web/src/features/auth/__contract__.ts +6 -0
  95. package/dist/templates/web-nextjs/apps/web/src/features/auth/actions.ts +126 -1
  96. package/dist/templates/web-nextjs/apps/web/src/features/auth/index.ts +12 -1
  97. package/dist/templates/web-nextjs/apps/web/src/features/auth/server.ts +3 -0
  98. package/dist/templates/web-nextjs/apps/web/src/hooks/use-mobile.ts +4 -4
  99. package/dist/templates/web-nextjs/apps/web/src/i18n/config.ts +1 -1
  100. package/dist/templates/web-nextjs/apps/web/src/infra/db/SupabaseAuthRepository.ts +73 -4
  101. package/dist/templates/web-nextjs/apps/web/src/infra/db/client.ts +1 -4
  102. package/dist/templates/web-nextjs/apps/web/src/lib/utils.ts +2 -2
  103. package/dist/templates/web-nextjs/apps/web/src/lib/validations/auth.ts +34 -0
  104. package/dist/templates/web-nextjs/apps/web/src/styles/tokens.css +58 -0
  105. package/package.json +1 -1
@@ -0,0 +1,115 @@
1
+ 'use client'
2
+
3
+ import { useActionState, useEffect } from 'react'
4
+ import { useParams, useSearchParams } 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 { resetPassword } from '@/features/auth'
16
+ import { resetPasswordSchema, type ResetPasswordInput } from '@/lib/validations/auth'
17
+ import type { ActionResult } from '@/core/types/auth'
18
+
19
+ export default function ResetPasswordPage() {
20
+ const t = useTranslations('auth')
21
+ const routeParams = useParams<{ locale: string }>()
22
+ const searchParams = useSearchParams()
23
+ const locale = routeParams.locale ?? 'en'
24
+ const error = searchParams.get('error')
25
+
26
+ const {
27
+ register,
28
+ formState: { errors },
29
+ } = useForm<ResetPasswordInput>({
30
+ resolver: zodResolver(resetPasswordSchema),
31
+ defaultValues: { password: '', confirmPassword: '' },
32
+ })
33
+
34
+ const [state, formAction, isPending] = useActionState<ActionResult | null, FormData>(
35
+ resetPassword,
36
+ null,
37
+ )
38
+
39
+ useEffect(() => {
40
+ if (state?.error) toast.error(state.error)
41
+ }, [state])
42
+
43
+ useEffect(() => {
44
+ if (error === 'link_expired') {
45
+ toast.error(t('resetLinkExpired'))
46
+ }
47
+ }, [error, t])
48
+
49
+ return (
50
+ <div className="flex min-h-svh flex-col items-center justify-center gap-6 bg-muted p-6 md:p-10">
51
+ <div className="flex w-full max-w-sm flex-col gap-6">
52
+ <Link href={`/${locale}`} className="flex items-center gap-2 self-center font-medium">
53
+ <div className="flex size-6 items-center justify-center rounded-md bg-primary text-primary-foreground">
54
+ <GalleryVerticalEnd className="size-4" />
55
+ </div>
56
+ AgentDock
57
+ </Link>
58
+
59
+ <Card>
60
+ <CardHeader className="text-center">
61
+ <CardTitle className="text-xl">{t('resetPasswordTitle')}</CardTitle>
62
+ <CardDescription>{t('resetPasswordSubtitle')}</CardDescription>
63
+ </CardHeader>
64
+ <CardContent>
65
+ <form action={formAction}>
66
+ <input type="hidden" name="locale" value={locale} />
67
+ <FieldGroup>
68
+ <Field>
69
+ <FieldLabel htmlFor="password">{t('newPasswordLabel')}</FieldLabel>
70
+ <Input
71
+ id="password"
72
+ type="password"
73
+ placeholder={t('passwordPlaceholder')}
74
+ autoComplete="new-password"
75
+ {...register('password')}
76
+ />
77
+ {errors.password && (
78
+ <FieldDescription className="text-destructive">
79
+ {errors.password.message}
80
+ </FieldDescription>
81
+ )}
82
+ </Field>
83
+ <Field>
84
+ <FieldLabel htmlFor="confirmPassword">{t('confirmPasswordLabel')}</FieldLabel>
85
+ <Input
86
+ id="confirmPassword"
87
+ type="password"
88
+ placeholder={t('confirmPasswordPlaceholder')}
89
+ autoComplete="new-password"
90
+ {...register('confirmPassword')}
91
+ />
92
+ {errors.confirmPassword && (
93
+ <FieldDescription className="text-destructive">
94
+ {errors.confirmPassword.message}
95
+ </FieldDescription>
96
+ )}
97
+ </Field>
98
+ <Field>
99
+ <Button type="submit" className="w-full" disabled={isPending}>
100
+ {isPending ? '\u2026' : t('resetPasswordButton')}
101
+ </Button>
102
+ <FieldDescription className="text-center">
103
+ <Link href={`/${locale}/login`} className="underline underline-offset-4">
104
+ {t('backToLogin')}
105
+ </Link>
106
+ </FieldDescription>
107
+ </Field>
108
+ </FieldGroup>
109
+ </form>
110
+ </CardContent>
111
+ </Card>
112
+ </div>
113
+ </div>
114
+ )
115
+ }
@@ -11,12 +11,7 @@ import { toast } from 'sonner'
11
11
  import { Button } from '@/components/ui/button'
12
12
  import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
13
13
  import { Input } from '@/components/ui/input'
14
- import {
15
- Field,
16
- FieldDescription,
17
- FieldGroup,
18
- FieldLabel,
19
- } from '@/components/ui/field'
14
+ import { Field, FieldDescription, FieldGroup, FieldLabel } from '@/components/ui/field'
20
15
  import { signUp } from '@/features/auth'
21
16
  import { signUpSchema, type SignUpInput } from '@/lib/validations/auth'
22
17
  import type { ActionResult } from '@/core/types/auth'
@@ -28,15 +23,18 @@ export default function SignupPage() {
28
23
  const locale = routeParams.locale ?? 'en'
29
24
  const [verifyEmail, setVerifyEmail] = useState<string | null>(null)
30
25
 
31
- const { register, formState: { errors } } = useForm<SignUpInput>({
26
+ const {
27
+ register,
28
+ formState: { errors },
29
+ } = useForm<SignUpInput>({
32
30
  resolver: zodResolver(signUpSchema),
33
31
  defaultValues: { email: '', password: '', confirmPassword: '' },
34
32
  })
35
33
 
36
- const [state, formAction, isPending] = useActionState<ActionResult<SignUpSuccessData> | null, FormData>(
37
- signUp,
38
- null,
39
- )
34
+ const [state, formAction, isPending] = useActionState<
35
+ ActionResult<SignUpSuccessData> | null,
36
+ FormData
37
+ >(signUp, null)
40
38
 
41
39
  useEffect(() => {
42
40
  if (state?.error) toast.error(state.error)
@@ -56,9 +54,7 @@ export default function SignupPage() {
56
54
  <Card>
57
55
  <CardHeader className="text-center">
58
56
  <CardTitle className="text-xl">{t('verifyEmailTitle')}</CardTitle>
59
- <CardDescription>
60
- {t('verifyEmailMessage', { email: verifyEmail })}
61
- </CardDescription>
57
+ <CardDescription>{t('verifyEmailMessage', { email: verifyEmail })}</CardDescription>
62
58
  </CardHeader>
63
59
  <CardContent>
64
60
  <div className="text-center">
@@ -158,9 +154,14 @@ export default function SignupPage() {
158
154
  </Card>
159
155
  <FieldDescription className="px-6 text-center">
160
156
  {t('termsText')}{' '}
161
- <Link href={`/${locale}/terms`} className="underline underline-offset-4">{t('termsLink')}</Link>
162
- {' '}{t('andText')}{' '}
163
- <Link href={`/${locale}/privacy`} className="underline underline-offset-4">{t('privacyLink')}</Link>.
157
+ <Link href={`/${locale}/terms`} className="underline underline-offset-4">
158
+ {t('termsLink')}
159
+ </Link>{' '}
160
+ {t('andText')}{' '}
161
+ <Link href={`/${locale}/privacy`} className="underline underline-offset-4">
162
+ {t('privacyLink')}
163
+ </Link>
164
+ .
164
165
  </FieldDescription>
165
166
  </div>
166
167
  </div>
@@ -8,11 +8,7 @@ import { getCurrentUser } from '@/features/auth/server'
8
8
 
9
9
  import data from '@/components/dashboard/data.json'
10
10
 
11
- export default async function DashboardPage({
12
- params,
13
- }: {
14
- params: Promise<{ locale: string }>
15
- }) {
11
+ export default async function DashboardPage({ params }: { params: Promise<{ locale: string }> }) {
16
12
  const { locale } = await params
17
13
  const user = await getCurrentUser()
18
14
 
@@ -0,0 +1,5 @@
1
+ import { ReactNode } from 'react'
2
+
3
+ export default function SettingsLayout({ children }: { children: ReactNode }) {
4
+ return <div className="container mx-auto max-w-4xl px-4 py-10">{children}</div>
5
+ }
@@ -0,0 +1,9 @@
1
+ import { redirect } from 'next/navigation'
2
+ import { defaultLocale } from '@/i18n/config'
3
+
4
+ export default function SettingsPage({ params }: { params: Promise<{ locale: string }> }) {
5
+ return (async () => {
6
+ const { locale } = await params
7
+ redirect(`/${locale}/settings/profile`)
8
+ })()
9
+ }
@@ -0,0 +1,91 @@
1
+ import { redirect } from 'next/navigation'
2
+ import Link from 'next/link'
3
+ import { getCurrentUser } from '@/features/auth/server'
4
+ import { ProfileForm } from '@/components/profile/profile-form'
5
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
6
+ import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
7
+ import { Button } from '@/components/ui/button'
8
+ import { Separator } from '@/components/ui/separator'
9
+
10
+ export default async function ProfilePage({ params }: { params: Promise<{ locale: string }> }) {
11
+ const { locale } = await params
12
+ const user = await getCurrentUser()
13
+
14
+ if (!user) {
15
+ redirect(`/${locale}/login`)
16
+ }
17
+
18
+ const displayName = user.user_metadata?.display_name ?? ''
19
+ const email = user.email ?? ''
20
+ const avatarUrl = user.user_metadata?.avatar_url ?? ''
21
+ const initials = email.charAt(0).toUpperCase()
22
+
23
+ return (
24
+ <div className="space-y-6">
25
+ <div>
26
+ <h1 className="text-2xl font-semibold">Profile Settings</h1>
27
+ <p className="text-muted-foreground">Manage your account settings and preferences.</p>
28
+ </div>
29
+ <Separator />
30
+
31
+ {/* Avatar & Basic Info */}
32
+ <Card>
33
+ <CardHeader>
34
+ <CardTitle>Profile</CardTitle>
35
+ <CardDescription>Your public profile information.</CardDescription>
36
+ </CardHeader>
37
+ <CardContent className="space-y-4">
38
+ <div className="flex items-center gap-4">
39
+ <Avatar className="h-16 w-16">
40
+ <AvatarImage src={avatarUrl} alt={displayName || email} />
41
+ <AvatarFallback className="text-lg">{initials}</AvatarFallback>
42
+ </Avatar>
43
+ <div>
44
+ <p className="font-medium">{displayName || 'No display name set'}</p>
45
+ <p className="text-sm text-muted-foreground">{email}</p>
46
+ </div>
47
+ </div>
48
+ <p className="text-sm text-muted-foreground">Avatar upload feature coming soon.</p>
49
+ </CardContent>
50
+ </Card>
51
+
52
+ {/* Display Name */}
53
+ <Card>
54
+ <CardHeader>
55
+ <CardTitle>Display Name</CardTitle>
56
+ <CardDescription>Update your display name shown across the app.</CardDescription>
57
+ </CardHeader>
58
+ <CardContent>
59
+ <ProfileForm defaultName={displayName} locale={locale} />
60
+ </CardContent>
61
+ </Card>
62
+
63
+ {/* Email */}
64
+ <Card>
65
+ <CardHeader>
66
+ <CardTitle>Email Address</CardTitle>
67
+ <CardDescription>Your primary email address.</CardDescription>
68
+ </CardHeader>
69
+ <CardContent>
70
+ <p className="text-sm">{email}</p>
71
+ <p className="mt-2 text-sm text-muted-foreground">
72
+ To change your email, please contact support.
73
+ </p>
74
+ </CardContent>
75
+ </Card>
76
+
77
+ {/* Password */}
78
+ <Card>
79
+ <CardHeader>
80
+ <CardTitle>Password</CardTitle>
81
+ <CardDescription>Change your password.</CardDescription>
82
+ </CardHeader>
83
+ <CardContent>
84
+ <Button asChild variant="outline">
85
+ <Link href={`/${locale}/forgot-password`}>Change Password</Link>
86
+ </Button>
87
+ </CardContent>
88
+ </Card>
89
+ </div>
90
+ )
91
+ }
@@ -0,0 +1,22 @@
1
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
2
+
3
+ export default async function AboutPage({ params }: { params: Promise<{ locale: string }> }) {
4
+ const { locale } = await params
5
+
6
+ return (
7
+ <main className="mx-auto max-w-3xl px-4 py-10">
8
+ <Card>
9
+ <CardHeader>
10
+ <CardTitle>About Us</CardTitle>
11
+ <CardDescription>Learn more about AgentDock.</CardDescription>
12
+ </CardHeader>
13
+ <CardContent>
14
+ <p className="text-muted-foreground">
15
+ This page is a placeholder. Replace it with your actual about information before
16
+ shipping.
17
+ </p>
18
+ </CardContent>
19
+ </Card>
20
+ </main>
21
+ )
22
+ }
@@ -1,12 +1,24 @@
1
1
  @import "../../styles/tw-animate.css";
2
2
  @import "../../styles/shadcn-tailwind.css";
3
3
  @import "tailwindcss";
4
+ @import "../../styles/tokens.css";
4
5
 
5
6
  @custom-variant dark (&:is(.dark *));
6
7
 
7
8
  @theme inline {
8
- --font-heading: var(--font-sans);
9
+ --font-heading: var(--font-geist, var(--font-sans));
9
10
  --font-sans: var(--font-sans);
11
+
12
+ /* Semantic tokens registered as Tailwind utilities */
13
+ --color-surface: var(--color-surface);
14
+ --color-surface-elevated: var(--color-surface-elevated);
15
+ --color-muted-custom: var(--color-muted);
16
+ --color-muted-foreground-custom: var(--color-muted-foreground);
17
+ --color-border-custom: var(--color-border);
18
+ --color-primary-custom: var(--color-primary);
19
+ --color-primary-foreground-custom: var(--color-primary-foreground);
20
+
21
+ /* shadcn/ui tokens */
10
22
  --color-sidebar-ring: var(--sidebar-ring);
11
23
  --color-sidebar-border: var(--sidebar-border);
12
24
  --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
@@ -119,11 +131,11 @@
119
131
  @layer base {
120
132
  * {
121
133
  @apply border-border outline-ring/50;
122
- }
134
+ }
123
135
  body {
124
136
  @apply bg-background text-foreground;
125
- }
137
+ }
126
138
  html {
127
139
  @apply font-sans;
128
- }
129
- }
140
+ }
141
+ }
@@ -0,0 +1,21 @@
1
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
2
+
3
+ export default async function HelpPage({ params }: { params: Promise<{ locale: string }> }) {
4
+ const { locale } = await params
5
+
6
+ return (
7
+ <main className="mx-auto max-w-3xl px-4 py-10">
8
+ <Card>
9
+ <CardHeader>
10
+ <CardTitle>Help Center</CardTitle>
11
+ <CardDescription>Find answers to common questions.</CardDescription>
12
+ </CardHeader>
13
+ <CardContent>
14
+ <p className="text-muted-foreground">
15
+ We are currently compiling our help documentation. Please check back later.
16
+ </p>
17
+ </CardContent>
18
+ </Card>
19
+ </main>
20
+ )
21
+ }
@@ -1,15 +1,17 @@
1
1
  import type { Metadata } from 'next'
2
- import { Inter } from 'next/font/google'
2
+ import { Geist } from 'next/font/google'
3
3
  import { NextIntlClientProvider } from 'next-intl'
4
4
  import { getMessages } from 'next-intl/server'
5
5
  import { notFound } from 'next/navigation'
6
6
  import { Toaster } from '@/components/ui/sonner'
7
+ import { ThemeProvider } from '@/components/providers/theme-provider'
7
8
  import { isLocale } from '@/i18n/config'
8
9
  import './globals.css'
9
10
 
10
- const inter = Inter({
11
+ const geist = Geist({
11
12
  subsets: ['latin'],
12
- variable: '--font-sans',
13
+ variable: '--font-geist',
14
+ fallback: ['system-ui', 'sans-serif'],
13
15
  })
14
16
 
15
17
  export const metadata: Metadata = {
@@ -33,11 +35,11 @@ export default async function LocaleLayout({
33
35
  const messages = await getMessages()
34
36
 
35
37
  return (
36
- <html lang={locale} suppressHydrationWarning>
37
- <body className={`${inter.variable} min-h-screen bg-background font-sans antialiased`}>
38
- <NextIntlClientProvider messages={messages}>
39
- {children}
40
- </NextIntlClientProvider>
38
+ <html lang={locale} suppressHydrationWarning className={geist.variable}>
39
+ <body className={`${geist.variable} min-h-screen bg-background font-sans antialiased`}>
40
+ <ThemeProvider>
41
+ <NextIntlClientProvider messages={messages}>{children}</NextIntlClientProvider>
42
+ </ThemeProvider>
41
43
  <Toaster />
42
44
  </body>
43
45
  </html>
@@ -1,12 +1,28 @@
1
1
  import { useTranslations } from 'next-intl'
2
+ import { Header } from '@/components/landing/header'
3
+ import { Hero } from '@/components/landing/hero'
4
+ import { Features } from '@/components/landing/features'
5
+ import { HowItWorks } from '@/components/landing/how-it-works'
6
+ import { PricingTeaser } from '@/components/landing/pricing-teaser'
7
+ import { Footer } from '@/components/landing/footer'
2
8
 
3
- export default function HomePage() {
4
- const t = useTranslations('home')
9
+ export default function HomePage({ params }: { params: Promise<{ locale: string }> }) {
10
+ return <HomePageContent params={params} />
11
+ }
12
+
13
+ async function HomePageContent({ params }: { params: Promise<{ locale: string }> }) {
14
+ const { locale } = await params
5
15
 
6
16
  return (
7
- <main className="flex min-h-screen flex-col items-center justify-center p-24">
8
- <h1 className="text-4xl font-bold">{t('title')}</h1>
9
- <p className="mt-4 text-gray-600">{t('description')}</p>
10
- </main>
17
+ <div className="flex min-h-screen flex-col">
18
+ <Header locale={locale} />
19
+ <main className="flex-1">
20
+ <Hero locale={locale} />
21
+ <Features />
22
+ <HowItWorks />
23
+ <PricingTeaser />
24
+ </main>
25
+ <Footer locale={locale} />
26
+ </div>
11
27
  )
12
28
  }
@@ -1,23 +1,22 @@
1
- import Link from 'next/link'
1
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
2
2
 
3
- export default async function PrivacyPage({
4
- params,
5
- }: {
6
- params: Promise<{ locale: string }>
7
- }) {
3
+ export default async function PrivacyPage({ params }: { params: Promise<{ locale: string }> }) {
8
4
  const { locale } = await params
9
5
 
10
6
  return (
11
7
  <main className="mx-auto max-w-3xl px-4 py-10">
12
- <h1 className="text-2xl font-semibold">Privacy Policy</h1>
13
- <p className="mt-4 text-muted-foreground">
14
- This template page is a placeholder. Replace it with your actual privacy policy before shipping.
15
- </p>
16
- <p className="mt-6">
17
- <Link href={`/${locale}/signup`} className="underline underline-offset-4">
18
- Back to sign up
19
- </Link>
20
- </p>
8
+ <Card>
9
+ <CardHeader>
10
+ <CardTitle>Privacy Policy</CardTitle>
11
+ <CardDescription>How we handle your data.</CardDescription>
12
+ </CardHeader>
13
+ <CardContent>
14
+ <p className="text-muted-foreground">
15
+ This template page is a placeholder. Replace it with your actual privacy policy before
16
+ shipping.
17
+ </p>
18
+ </CardContent>
19
+ </Card>
21
20
  </main>
22
21
  )
23
22
  }
@@ -1,10 +1,6 @@
1
1
  import Link from 'next/link'
2
2
 
3
- export default async function TermsPage({
4
- params,
5
- }: {
6
- params: Promise<{ locale: string }>
7
- }) {
3
+ export default async function TermsPage({ params }: { params: Promise<{ locale: string }> }) {
8
4
  const { locale } = await params
9
5
 
10
6
  return (
@@ -2,18 +2,23 @@ import { NextRequest, NextResponse } from 'next/server'
2
2
  import { getServerClient } from '@/infra/db/client'
3
3
  import { defaultLocale } from '@/i18n/config'
4
4
 
5
+ const ALLOWED_NEXT = ['/reset-password']
6
+
5
7
  export async function GET(request: NextRequest) {
6
8
  const { searchParams, origin } = new URL(request.url)
7
9
  const code = searchParams.get('code')
8
10
  const nextParam = searchParams.get('next')
9
- const next = nextParam && nextParam.startsWith('/') ? nextParam : `/${defaultLocale}/dashboard`
11
+
12
+ // Whitelist check to prevent open redirect attacks
13
+ const safePath =
14
+ nextParam && ALLOWED_NEXT.includes(nextParam) ? nextParam : `/${defaultLocale}/dashboard`
10
15
 
11
16
  if (code) {
12
17
  const supabase = await getServerClient()
13
18
  const { error } = await supabase.auth.exchangeCodeForSession(code)
14
19
 
15
20
  if (!error) {
16
- return NextResponse.redirect(`${origin}${next}`)
21
+ return NextResponse.redirect(`${origin}${safePath}`)
17
22
  }
18
23
  }
19
24