@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
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
2
|
|
|
3
3
|
import Link from 'next/link'
|
|
4
|
-
import {
|
|
5
|
-
IconDotsVertical,
|
|
6
|
-
IconLogout,
|
|
7
|
-
IconUserCircle,
|
|
8
|
-
} from "@tabler/icons-react"
|
|
4
|
+
import { IconDotsVertical, IconLogout, IconUserCircle } from '@tabler/icons-react'
|
|
9
5
|
|
|
10
|
-
import {
|
|
11
|
-
Avatar,
|
|
12
|
-
AvatarFallback,
|
|
13
|
-
AvatarImage,
|
|
14
|
-
} from "@/components/ui/avatar"
|
|
6
|
+
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
|
15
7
|
import {
|
|
16
8
|
DropdownMenu,
|
|
17
9
|
DropdownMenuContent,
|
|
@@ -20,13 +12,13 @@ import {
|
|
|
20
12
|
DropdownMenuLabel,
|
|
21
13
|
DropdownMenuSeparator,
|
|
22
14
|
DropdownMenuTrigger,
|
|
23
|
-
} from
|
|
15
|
+
} from '@/components/ui/dropdown-menu'
|
|
24
16
|
import {
|
|
25
17
|
SidebarMenu,
|
|
26
18
|
SidebarMenuButton,
|
|
27
19
|
SidebarMenuItem,
|
|
28
20
|
useSidebar,
|
|
29
|
-
} from
|
|
21
|
+
} from '@/components/ui/sidebar'
|
|
30
22
|
import { signOut } from '@/features/auth'
|
|
31
23
|
|
|
32
24
|
export function NavUser({
|
|
@@ -57,16 +49,14 @@ export function NavUser({
|
|
|
57
49
|
</Avatar>
|
|
58
50
|
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
59
51
|
<span className="truncate font-medium">{user.name}</span>
|
|
60
|
-
<span className="truncate text-xs text-muted-foreground">
|
|
61
|
-
{user.email}
|
|
62
|
-
</span>
|
|
52
|
+
<span className="truncate text-xs text-muted-foreground">{user.email}</span>
|
|
63
53
|
</div>
|
|
64
54
|
<IconDotsVertical className="ml-auto size-4" />
|
|
65
55
|
</SidebarMenuButton>
|
|
66
56
|
</DropdownMenuTrigger>
|
|
67
57
|
<DropdownMenuContent
|
|
68
58
|
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
|
|
69
|
-
side={isMobile ?
|
|
59
|
+
side={isMobile ? 'bottom' : 'right'}
|
|
70
60
|
align="end"
|
|
71
61
|
sideOffset={4}
|
|
72
62
|
>
|
|
@@ -78,16 +68,17 @@ export function NavUser({
|
|
|
78
68
|
</Avatar>
|
|
79
69
|
<div className="grid flex-1 text-left text-sm leading-tight">
|
|
80
70
|
<span className="truncate font-medium">{user.name}</span>
|
|
81
|
-
<span className="truncate text-xs text-muted-foreground">
|
|
82
|
-
{user.email}
|
|
83
|
-
</span>
|
|
71
|
+
<span className="truncate text-xs text-muted-foreground">{user.email}</span>
|
|
84
72
|
</div>
|
|
85
73
|
</div>
|
|
86
74
|
</DropdownMenuLabel>
|
|
87
75
|
<DropdownMenuSeparator />
|
|
88
76
|
<DropdownMenuGroup>
|
|
89
77
|
<DropdownMenuItem asChild>
|
|
90
|
-
<Link
|
|
78
|
+
<Link
|
|
79
|
+
href={`/${locale}/settings/profile`}
|
|
80
|
+
className="flex w-full items-center gap-2"
|
|
81
|
+
>
|
|
91
82
|
<IconUserCircle />
|
|
92
83
|
Account
|
|
93
84
|
</Link>
|
|
@@ -1,26 +1,23 @@
|
|
|
1
|
-
import { AppSidebar } from
|
|
2
|
-
import { ChartAreaInteractive } from
|
|
3
|
-
import { DataTable } from
|
|
4
|
-
import { SectionCards } from
|
|
5
|
-
import { SiteHeader } from
|
|
6
|
-
import {
|
|
7
|
-
SidebarInset,
|
|
8
|
-
SidebarProvider,
|
|
9
|
-
} from "@/components/ui/sidebar"
|
|
1
|
+
import { AppSidebar } from '@/components/dashboard/app-sidebar'
|
|
2
|
+
import { ChartAreaInteractive } from '@/components/dashboard/chart-area-interactive'
|
|
3
|
+
import { DataTable } from '@/components/dashboard/data-table'
|
|
4
|
+
import { SectionCards } from '@/components/dashboard/section-cards'
|
|
5
|
+
import { SiteHeader } from '@/components/dashboard/site-header'
|
|
6
|
+
import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar'
|
|
10
7
|
|
|
11
|
-
import data from
|
|
8
|
+
import data from './data.json'
|
|
12
9
|
|
|
13
10
|
export default function Page() {
|
|
14
11
|
return (
|
|
15
12
|
<SidebarProvider
|
|
16
13
|
style={
|
|
17
14
|
{
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
'--sidebar-width': 'calc(var(--spacing) * 72)',
|
|
16
|
+
'--header-height': 'calc(var(--spacing) * 12)',
|
|
20
17
|
} as React.CSSProperties
|
|
21
18
|
}
|
|
22
19
|
>
|
|
23
|
-
<AppSidebar variant="inset" user={{ name:
|
|
20
|
+
<AppSidebar variant="inset" user={{ name: 'User', email: '', avatar: '' }} locale="en" />
|
|
24
21
|
<SidebarInset>
|
|
25
22
|
<SiteHeader />
|
|
26
23
|
<div className="flex flex-1 flex-col">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { IconTrendingDown, IconTrendingUp } from
|
|
1
|
+
import { IconTrendingDown, IconTrendingUp } from '@tabler/icons-react'
|
|
2
2
|
|
|
3
|
-
import { Badge } from
|
|
3
|
+
import { Badge } from '@/components/ui/badge'
|
|
4
4
|
import {
|
|
5
5
|
Card,
|
|
6
6
|
CardAction,
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
CardFooter,
|
|
9
9
|
CardHeader,
|
|
10
10
|
CardTitle,
|
|
11
|
-
} from
|
|
11
|
+
} from '@/components/ui/card'
|
|
12
12
|
|
|
13
13
|
export function SectionCards() {
|
|
14
14
|
return (
|
|
@@ -30,9 +30,7 @@ export function SectionCards() {
|
|
|
30
30
|
<div className="line-clamp-1 flex gap-2 font-medium">
|
|
31
31
|
Trending up this month <IconTrendingUp className="size-4" />
|
|
32
32
|
</div>
|
|
33
|
-
<div className="text-muted-foreground">
|
|
34
|
-
Visitors for the last 6 months
|
|
35
|
-
</div>
|
|
33
|
+
<div className="text-muted-foreground">Visitors for the last 6 months</div>
|
|
36
34
|
</CardFooter>
|
|
37
35
|
</Card>
|
|
38
36
|
<Card className="@container/card">
|
|
@@ -52,9 +50,7 @@ export function SectionCards() {
|
|
|
52
50
|
<div className="line-clamp-1 flex gap-2 font-medium">
|
|
53
51
|
Down 20% this period <IconTrendingDown className="size-4" />
|
|
54
52
|
</div>
|
|
55
|
-
<div className="text-muted-foreground">
|
|
56
|
-
Acquisition needs attention
|
|
57
|
-
</div>
|
|
53
|
+
<div className="text-muted-foreground">Acquisition needs attention</div>
|
|
58
54
|
</CardFooter>
|
|
59
55
|
</Card>
|
|
60
56
|
<Card className="@container/card">
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { Button } from
|
|
2
|
-
import { Separator } from
|
|
3
|
-
import { SidebarTrigger } from
|
|
1
|
+
import { Button } from '@/components/ui/button'
|
|
2
|
+
import { Separator } from '@/components/ui/separator'
|
|
3
|
+
import { SidebarTrigger } from '@/components/ui/sidebar'
|
|
4
|
+
import { ThemeToggle } from '@/components/ui/theme-toggle'
|
|
4
5
|
|
|
5
6
|
export function SiteHeader() {
|
|
6
7
|
return (
|
|
7
8
|
<header className="flex h-(--header-height) shrink-0 items-center gap-2 border-b transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-(--header-height)">
|
|
8
9
|
<div className="flex w-full items-center gap-1 px-4 lg:gap-2 lg:px-6">
|
|
9
10
|
<SidebarTrigger className="-ml-1" />
|
|
10
|
-
<Separator
|
|
11
|
-
orientation="vertical"
|
|
12
|
-
className="mx-2 data-[orientation=vertical]:h-4"
|
|
13
|
-
/>
|
|
11
|
+
<Separator orientation="vertical" className="mx-2 data-[orientation=vertical]:h-4" />
|
|
14
12
|
<h1 className="text-base font-medium">Documents</h1>
|
|
15
13
|
<div className="ml-auto flex items-center gap-2">
|
|
14
|
+
<ThemeToggle />
|
|
16
15
|
<Button variant="ghost" asChild size="sm" className="hidden sm:flex">
|
|
17
16
|
<a
|
|
18
17
|
href="https://github.com/shadcn-ui/ui/tree/main/apps/v4/app/(examples)/dashboard"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useTranslations } from 'next-intl'
|
|
2
|
+
import { Shield, GitBranch, Bot, BookOpen } from 'lucide-react'
|
|
3
|
+
|
|
4
|
+
const icons = [Shield, GitBranch, Bot, BookOpen]
|
|
5
|
+
|
|
6
|
+
export function Features() {
|
|
7
|
+
const t = useTranslations('landing')
|
|
8
|
+
|
|
9
|
+
const features = [
|
|
10
|
+
{
|
|
11
|
+
key: 'auth',
|
|
12
|
+
icon: Shield,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
key: 'monorepo',
|
|
16
|
+
icon: GitBranch,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
key: 'aiCoding',
|
|
20
|
+
icon: Bot,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
key: 'docs',
|
|
24
|
+
icon: BookOpen,
|
|
25
|
+
},
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<section
|
|
30
|
+
id="features"
|
|
31
|
+
className="px-4 py-16 md:py-24"
|
|
32
|
+
style={{ padding: 'clamp(32px, 8vw, 80px) 0' }}
|
|
33
|
+
>
|
|
34
|
+
<div className="mx-auto max-w-7xl px-4 md:px-6">
|
|
35
|
+
<div className="mb-12 text-center">
|
|
36
|
+
<h2 className="text-3xl font-bold tracking-tight md:text-4xl">{t('features.title')}</h2>
|
|
37
|
+
<p className="mt-4 text-muted-foreground">{t('features.subtitle')}</p>
|
|
38
|
+
</div>
|
|
39
|
+
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
|
40
|
+
{features.map((feature) => {
|
|
41
|
+
const Icon = feature.icon
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
key={feature.key}
|
|
45
|
+
className="group rounded-xl border bg-card p-6 transition-colors hover:border-primary/20"
|
|
46
|
+
>
|
|
47
|
+
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
|
48
|
+
<Icon className="h-5 w-5" />
|
|
49
|
+
</div>
|
|
50
|
+
<h3 className="text-lg font-semibold">
|
|
51
|
+
{t(`features.items.${feature.key}.title`)}
|
|
52
|
+
</h3>
|
|
53
|
+
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">
|
|
54
|
+
{t(`features.items.${feature.key}.description`)}
|
|
55
|
+
</p>
|
|
56
|
+
</div>
|
|
57
|
+
)
|
|
58
|
+
})}
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</section>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import Link from 'next/link'
|
|
2
|
+
import { useTranslations } from 'next-intl'
|
|
3
|
+
import { IconBrandGithub } from '@tabler/icons-react'
|
|
4
|
+
|
|
5
|
+
interface FooterProps {
|
|
6
|
+
locale: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function Footer({ locale }: FooterProps) {
|
|
10
|
+
const t = useTranslations('landing')
|
|
11
|
+
const year = new Date().getFullYear()
|
|
12
|
+
|
|
13
|
+
const links = [
|
|
14
|
+
{ href: `/${locale}/help`, label: t('footer.help') },
|
|
15
|
+
{ href: `/${locale}/privacy`, label: t('footer.privacy') },
|
|
16
|
+
{ href: `/${locale}/about`, label: t('footer.about') },
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<footer className="border-t bg-background px-4 py-8 md:py-12">
|
|
21
|
+
<div className="mx-auto flex max-w-7xl flex-col items-center justify-between gap-6 md:flex-row md:px-6">
|
|
22
|
+
<div className="flex items-center gap-4">
|
|
23
|
+
{links.map((link) => (
|
|
24
|
+
<Link
|
|
25
|
+
key={link.href}
|
|
26
|
+
href={link.href}
|
|
27
|
+
className="text-sm text-muted-foreground transition-colors hover:text-foreground"
|
|
28
|
+
>
|
|
29
|
+
{link.label}
|
|
30
|
+
</Link>
|
|
31
|
+
))}
|
|
32
|
+
</div>
|
|
33
|
+
<div className="flex items-center gap-4">
|
|
34
|
+
<a
|
|
35
|
+
href="https://github.com"
|
|
36
|
+
target="_blank"
|
|
37
|
+
rel="noopener noreferrer"
|
|
38
|
+
aria-label="GitHub"
|
|
39
|
+
className="text-muted-foreground transition-colors hover:text-foreground"
|
|
40
|
+
>
|
|
41
|
+
<IconBrandGithub className="h-5 w-5" />
|
|
42
|
+
</a>
|
|
43
|
+
<span className="text-sm text-muted-foreground">{t('footer.copyright', { year })}</span>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
</footer>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import Link from 'next/link'
|
|
5
|
+
import { useTranslations } from 'next-intl'
|
|
6
|
+
import { Menu } from 'lucide-react'
|
|
7
|
+
import { Button } from '@/components/ui/button'
|
|
8
|
+
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
|
|
9
|
+
import { ThemeToggle } from '@/components/ui/theme-toggle'
|
|
10
|
+
|
|
11
|
+
interface HeaderProps {
|
|
12
|
+
locale: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function Header({ locale }: HeaderProps) {
|
|
16
|
+
const t = useTranslations('landing')
|
|
17
|
+
const [open, setOpen] = useState(false)
|
|
18
|
+
|
|
19
|
+
const navLinks = [
|
|
20
|
+
{ href: `/${locale}/#features`, label: t('nav.features') },
|
|
21
|
+
{ href: `/${locale}/#pricing`, label: t('nav.pricing') },
|
|
22
|
+
{ href: '/docs', label: t('nav.docs') },
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<header className="sticky top-0 z-50 w-full border-b bg-background/80 backdrop-blur-sm">
|
|
27
|
+
<div className="mx-auto flex h-(--header-height, 4rem) max-w-7xl items-center justify-between px-4 md:px-6">
|
|
28
|
+
{/* Logo */}
|
|
29
|
+
<Link href={`/${locale}`} className="flex items-center gap-2">
|
|
30
|
+
<span className="text-lg font-semibold tracking-tight">AgentDock</span>
|
|
31
|
+
</Link>
|
|
32
|
+
|
|
33
|
+
{/* Desktop Nav */}
|
|
34
|
+
<nav className="hidden items-center gap-6 md:flex">
|
|
35
|
+
{navLinks.map((link) => (
|
|
36
|
+
<Link
|
|
37
|
+
key={link.href}
|
|
38
|
+
href={link.href}
|
|
39
|
+
className="text-sm font-medium text-muted-foreground transition-colors hover:text-foreground"
|
|
40
|
+
>
|
|
41
|
+
{link.label}
|
|
42
|
+
</Link>
|
|
43
|
+
))}
|
|
44
|
+
</nav>
|
|
45
|
+
|
|
46
|
+
{/* Desktop Actions */}
|
|
47
|
+
<div className="hidden items-center gap-2 md:flex">
|
|
48
|
+
<ThemeToggle />
|
|
49
|
+
<Button variant="ghost" asChild size="sm" className="min-h-[44px]">
|
|
50
|
+
<Link href={`/${locale}/login`}>{t('nav.login')}</Link>
|
|
51
|
+
</Button>
|
|
52
|
+
<Button asChild size="sm" className="min-h-[44px]">
|
|
53
|
+
<Link href={`/${locale}/signup`}>{t('nav.getStarted')}</Link>
|
|
54
|
+
</Button>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
{/* Mobile Menu */}
|
|
58
|
+
<div className="flex items-center gap-2 md:hidden">
|
|
59
|
+
<ThemeToggle />
|
|
60
|
+
<Sheet open={open} onOpenChange={setOpen}>
|
|
61
|
+
<SheetTrigger asChild>
|
|
62
|
+
<Button variant="ghost" size="icon" className="min-h-[44px] min-w-[44px]">
|
|
63
|
+
<Menu className="h-5 w-5" />
|
|
64
|
+
<span className="sr-only">Open menu</span>
|
|
65
|
+
</Button>
|
|
66
|
+
</SheetTrigger>
|
|
67
|
+
<SheetContent side="right" className="w-[280px]">
|
|
68
|
+
<nav className="mt-8 flex flex-col gap-4">
|
|
69
|
+
{navLinks.map((link) => (
|
|
70
|
+
<Link
|
|
71
|
+
key={link.href}
|
|
72
|
+
href={link.href}
|
|
73
|
+
onClick={() => setOpen(false)}
|
|
74
|
+
className="text-base font-medium text-muted-foreground transition-colors hover:text-foreground"
|
|
75
|
+
>
|
|
76
|
+
{link.label}
|
|
77
|
+
</Link>
|
|
78
|
+
))}
|
|
79
|
+
<hr className="my-2" />
|
|
80
|
+
<Button asChild className="w-full min-h-[44px]">
|
|
81
|
+
<Link href={`/${locale}/signup`} onClick={() => setOpen(false)}>
|
|
82
|
+
{t('nav.getStarted')}
|
|
83
|
+
</Link>
|
|
84
|
+
</Button>
|
|
85
|
+
<Button variant="outline" asChild className="w-full min-h-[44px]">
|
|
86
|
+
<Link href={`/${locale}/login`} onClick={() => setOpen(false)}>
|
|
87
|
+
{t('nav.login')}
|
|
88
|
+
</Link>
|
|
89
|
+
</Button>
|
|
90
|
+
</nav>
|
|
91
|
+
</SheetContent>
|
|
92
|
+
</Sheet>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
</header>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import Link from 'next/link'
|
|
2
|
+
import { useTranslations } from 'next-intl'
|
|
3
|
+
import { Button } from '@/components/ui/button'
|
|
4
|
+
import { Badge } from '@/components/ui/badge'
|
|
5
|
+
|
|
6
|
+
interface HeroProps {
|
|
7
|
+
locale: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function Hero({ locale }: HeroProps) {
|
|
11
|
+
const t = useTranslations('landing')
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<section className="relative overflow-hidden px-4 py-16 md:py-24 lg:py-32">
|
|
15
|
+
<div className="mx-auto max-w-4xl text-center">
|
|
16
|
+
<Badge variant="secondary" className="mb-4">
|
|
17
|
+
{t('hero.badge')}
|
|
18
|
+
</Badge>
|
|
19
|
+
<h1
|
|
20
|
+
className="mx-auto max-w-3xl font-bold tracking-tight"
|
|
21
|
+
style={{ fontSize: 'clamp(2rem, 5vw + 1rem, 4rem)', lineHeight: 1.1 }}
|
|
22
|
+
>
|
|
23
|
+
{t('hero.title')}
|
|
24
|
+
</h1>
|
|
25
|
+
<p className="mx-auto mt-6 max-w-2xl text-lg leading-relaxed text-muted-foreground md:text-xl">
|
|
26
|
+
{t('hero.subtitle')}
|
|
27
|
+
</p>
|
|
28
|
+
<div className="mt-10 flex flex-col items-center justify-center gap-4 sm:flex-row">
|
|
29
|
+
<Button asChild size="lg" className="min-h-[44px] min-w-[44px] px-8">
|
|
30
|
+
<Link href={`/${locale}/signup`}>{t('hero.ctaPrimary')}</Link>
|
|
31
|
+
</Button>
|
|
32
|
+
<Button asChild variant="outline" size="lg" className="min-h-[44px] min-w-[44px] px-8">
|
|
33
|
+
<Link href="/docs">{t('hero.ctaSecondary')}</Link>
|
|
34
|
+
</Button>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
{/* Decorative geometric shapes - CSS only, no custom SVG */}
|
|
39
|
+
<div className="pointer-events-none absolute inset-0 -z-10 overflow-hidden opacity-30">
|
|
40
|
+
<div className="absolute -left-20 -top-20 h-64 w-64 rounded-full bg-gradient-to-br from-primary/10 to-transparent blur-3xl" />
|
|
41
|
+
<div className="absolute -bottom-20 -right-20 h-64 w-64 rounded-full bg-gradient-to-tl from-primary/10 to-transparent blur-3xl" />
|
|
42
|
+
</div>
|
|
43
|
+
</section>
|
|
44
|
+
)
|
|
45
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useTranslations } from 'next-intl'
|
|
2
|
+
|
|
3
|
+
export function HowItWorks() {
|
|
4
|
+
const t = useTranslations('landing')
|
|
5
|
+
|
|
6
|
+
const steps = [
|
|
7
|
+
{ key: 'step1', number: '01' },
|
|
8
|
+
{ key: 'step2', number: '02' },
|
|
9
|
+
{ key: 'step3', number: '03' },
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<section className="px-4 py-16 md:py-24" style={{ padding: 'clamp(32px, 8vw, 80px) 0' }}>
|
|
14
|
+
<div className="mx-auto max-w-7xl px-4 md:px-6">
|
|
15
|
+
<div className="mb-12 text-center">
|
|
16
|
+
<h2 className="text-3xl font-bold tracking-tight md:text-4xl">{t('howItWorks.title')}</h2>
|
|
17
|
+
<p className="mt-4 text-muted-foreground">{t('howItWorks.subtitle')}</p>
|
|
18
|
+
</div>
|
|
19
|
+
<div className="grid gap-8 md:grid-cols-3">
|
|
20
|
+
{steps.map((step) => (
|
|
21
|
+
<div key={step.key} className="relative text-center">
|
|
22
|
+
<div className="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-primary/10 text-2xl font-bold text-primary">
|
|
23
|
+
{step.number}
|
|
24
|
+
</div>
|
|
25
|
+
<h3 className="text-lg font-semibold">{t(`howItWorks.steps.${step.key}.title`)}</h3>
|
|
26
|
+
<p className="mt-2 text-sm leading-relaxed text-muted-foreground">
|
|
27
|
+
{t(`howItWorks.steps.${step.key}.description`)}
|
|
28
|
+
</p>
|
|
29
|
+
</div>
|
|
30
|
+
))}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</section>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useTranslations } from 'next-intl'
|
|
2
|
+
import { Button } from '@/components/ui/button'
|
|
3
|
+
|
|
4
|
+
export function PricingTeaser() {
|
|
5
|
+
const t = useTranslations('landing')
|
|
6
|
+
|
|
7
|
+
return (
|
|
8
|
+
<section
|
|
9
|
+
id="pricing"
|
|
10
|
+
className="px-4 py-16 md:py-24"
|
|
11
|
+
style={{ padding: 'clamp(32px, 8vw, 80px) 0' }}
|
|
12
|
+
>
|
|
13
|
+
<div className="mx-auto max-w-3xl px-4 text-center md:px-6">
|
|
14
|
+
<h2 className="text-3xl font-bold tracking-tight md:text-4xl">{t('pricing.title')}</h2>
|
|
15
|
+
<p className="mt-4 text-muted-foreground">{t('pricing.description')}</p>
|
|
16
|
+
<Button asChild className="mt-8 min-h-[44px] min-w-[44px]">
|
|
17
|
+
<a href="mailto:contact@agentdock.dev">{t('pricing.cta')}</a>
|
|
18
|
+
</Button>
|
|
19
|
+
{/* TODO: Integrate Stripe, see add-stripe-payments change */}
|
|
20
|
+
</div>
|
|
21
|
+
</section>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -17,7 +17,11 @@ interface ProfileFormProps {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export function ProfileForm({ defaultName, locale }: ProfileFormProps) {
|
|
20
|
-
const {
|
|
20
|
+
const {
|
|
21
|
+
register,
|
|
22
|
+
formState: { errors },
|
|
23
|
+
reset,
|
|
24
|
+
} = useForm<DisplayNameInput>({
|
|
21
25
|
resolver: zodResolver(displayNameSchema),
|
|
22
26
|
defaultValues: { name: defaultName },
|
|
23
27
|
})
|
|
@@ -48,9 +52,7 @@ export function ProfileForm({ defaultName, locale }: ProfileFormProps) {
|
|
|
48
52
|
{...register('name')}
|
|
49
53
|
/>
|
|
50
54
|
{errors.name && (
|
|
51
|
-
<FieldDescription className="text-destructive">
|
|
52
|
-
{errors.name.message}
|
|
53
|
-
</FieldDescription>
|
|
55
|
+
<FieldDescription className="text-destructive">{errors.name.message}</FieldDescription>
|
|
54
56
|
)}
|
|
55
57
|
</Field>
|
|
56
58
|
<Field>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { ThemeProvider as NextThemesProvider } from 'next-themes'
|
|
4
|
+
|
|
5
|
+
export function ThemeProvider({ children }: { children: React.ReactNode }) {
|
|
6
|
+
return (
|
|
7
|
+
<NextThemesProvider
|
|
8
|
+
attribute="class"
|
|
9
|
+
defaultTheme="system"
|
|
10
|
+
enableSystem
|
|
11
|
+
disableTransitionOnChange
|
|
12
|
+
>
|
|
13
|
+
{children}
|
|
14
|
+
</NextThemesProvider>
|
|
15
|
+
)
|
|
16
|
+
}
|