create-cronus-app 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +173 -0
  3. package/dist/compose.d.ts +56 -0
  4. package/dist/compose.js +92 -0
  5. package/dist/index.d.ts +33 -0
  6. package/dist/index.js +255 -0
  7. package/dist/scaffold.d.ts +31 -0
  8. package/dist/scaffold.js +118 -0
  9. package/dist/utils.d.ts +121 -0
  10. package/dist/utils.js +285 -0
  11. package/dist/version.d.ts +3 -0
  12. package/dist/version.js +3 -0
  13. package/package.json +56 -0
  14. package/templates/dashboard/README.md +61 -0
  15. package/templates/dashboard/app/globals.css +16 -0
  16. package/templates/dashboard/app/layout.tsx +40 -0
  17. package/templates/dashboard/app/page.tsx +77 -0
  18. package/templates/dashboard/app/settings/page.tsx +21 -0
  19. package/templates/dashboard/components/dashboard-shell.tsx +120 -0
  20. package/templates/dashboard/components/orders-table.tsx +132 -0
  21. package/templates/dashboard/components/revenue-chart.tsx +50 -0
  22. package/templates/dashboard/components/settings-form.tsx +167 -0
  23. package/templates/dashboard/cronus-ui.json +17 -0
  24. package/templates/dashboard/gitignore +34 -0
  25. package/templates/dashboard/next.config.mjs +11 -0
  26. package/templates/dashboard/package.json +30 -0
  27. package/templates/dashboard/postcss.config.mjs +7 -0
  28. package/templates/dashboard/tsconfig.json +23 -0
  29. package/templates/default/README.md +46 -0
  30. package/templates/default/app/globals.css +16 -0
  31. package/templates/default/app/layout.tsx +39 -0
  32. package/templates/default/app/page.tsx +112 -0
  33. package/templates/default/cronus-ui.json +17 -0
  34. package/templates/default/gitignore +34 -0
  35. package/templates/default/next.config.mjs +11 -0
  36. package/templates/default/package.json +27 -0
  37. package/templates/default/postcss.config.mjs +7 -0
  38. package/templates/default/tsconfig.json +23 -0
  39. package/templates/marketing/README.md +65 -0
  40. package/templates/marketing/app/globals.css +16 -0
  41. package/templates/marketing/app/layout.tsx +39 -0
  42. package/templates/marketing/app/page.tsx +25 -0
  43. package/templates/marketing/components/faq.tsx +62 -0
  44. package/templates/marketing/components/feature-grid.tsx +67 -0
  45. package/templates/marketing/components/hero.tsx +61 -0
  46. package/templates/marketing/components/pricing.tsx +116 -0
  47. package/templates/marketing/components/site-footer.tsx +91 -0
  48. package/templates/marketing/components/site-header.tsx +47 -0
  49. package/templates/marketing/components/testimonials.tsx +91 -0
  50. package/templates/marketing/components/waitlist-cta.tsx +93 -0
  51. package/templates/marketing/cronus-ui.json +17 -0
  52. package/templates/marketing/gitignore +34 -0
  53. package/templates/marketing/next.config.mjs +11 -0
  54. package/templates/marketing/package.json +28 -0
  55. package/templates/marketing/postcss.config.mjs +7 -0
  56. package/templates/marketing/tsconfig.json +23 -0
@@ -0,0 +1,40 @@
1
+ import { CronusThemeScript, CronusUIProvider } from "@cronus-ui/theme";
2
+ import type { Metadata } from "next";
3
+ import type { ReactNode } from "react";
4
+ import { DashboardShell } from "../components/dashboard-shell";
5
+ import "./globals.css";
6
+
7
+ export const metadata: Metadata = {
8
+ title: "__APP_NAME__",
9
+ description: "A dashboard built with Cronus UI.",
10
+ };
11
+
12
+ export default function RootLayout({ children }: { children: ReactNode }) {
13
+ return (
14
+ <html
15
+ lang="en"
16
+ // CronusThemeScript mutates <html> (theme/mode/dark class) before hydration
17
+ // to prevent a flash of the wrong theme, so React must not warn about the
18
+ // resulting attribute mismatch.
19
+ suppressHydrationWarning
20
+ >
21
+ <head>
22
+ <CronusThemeScript
23
+ storageKey="theme"
24
+ defaultThemeName="__THEME__"
25
+ defaultModeName="__MODE__"
26
+ />
27
+ </head>
28
+ <body>
29
+ <CronusUIProvider
30
+ asRoot
31
+ defaultThemeName="__THEME__"
32
+ defaultModeName="__MODE__"
33
+ storageKey="theme"
34
+ >
35
+ <DashboardShell>{children}</DashboardShell>
36
+ </CronusUIProvider>
37
+ </body>
38
+ </html>
39
+ );
40
+ }
@@ -0,0 +1,77 @@
1
+ import { Badge } from "@cronus-ui/ui/badge";
2
+ import { Card, CardContent } from "@cronus-ui/ui/card";
3
+ import { Metric, MetricDelta, MetricLabel, MetricValue } from "@cronus-ui/ui/metric";
4
+ import { Activity, DollarSign, TrendingDown, Users } from "lucide-react";
5
+ import { OrdersTable } from "../components/orders-table";
6
+ import { RevenueChart } from "../components/revenue-chart";
7
+
8
+ const KPIS = [
9
+ {
10
+ label: "Total revenue",
11
+ value: "$84,210",
12
+ delta: "+14.2%",
13
+ trend: "up",
14
+ icon: DollarSign,
15
+ },
16
+ {
17
+ label: "Active users",
18
+ value: "12,840",
19
+ delta: "+6.8%",
20
+ trend: "up",
21
+ icon: Users,
22
+ },
23
+ {
24
+ label: "Conversion",
25
+ value: "4.21%",
26
+ delta: "+0.9%",
27
+ trend: "up",
28
+ icon: Activity,
29
+ },
30
+ {
31
+ label: "Churn",
32
+ value: "1.64%",
33
+ delta: "-0.4%",
34
+ trend: "down",
35
+ icon: TrendingDown,
36
+ },
37
+ ] as const;
38
+
39
+ export default function OverviewPage() {
40
+ return (
41
+ <div className="flex flex-col gap-6 p-6">
42
+ <header className="flex flex-wrap items-end justify-between gap-3">
43
+ <div className="flex flex-col gap-1">
44
+ <h1 className="font-display text-2xl font-semibold tracking-tight text-fg">Overview</h1>
45
+ <p className="text-sm text-fg-secondary">
46
+ Performance across your workspace, last 7 months.
47
+ </p>
48
+ </div>
49
+ <Badge variant="success">Live</Badge>
50
+ </header>
51
+
52
+ {/* KPI grid */}
53
+ <div className="grid grid-cols-[repeat(auto-fit,minmax(min(100%,12rem),1fr))] gap-4">
54
+ {KPIS.map(({ label, value, delta, trend, icon: Icon }) => (
55
+ <Card key={label} className="gap-4 py-5">
56
+ <CardContent className="flex flex-col gap-3">
57
+ <div className="flex items-center justify-between">
58
+ <span className="inline-flex size-9 items-center justify-center rounded-lg bg-surface-overlay text-fg-secondary">
59
+ <Icon className="size-4" aria-hidden="true" />
60
+ </span>
61
+ <MetricDelta trend={trend}>{delta}</MetricDelta>
62
+ </div>
63
+ <Metric className="gap-1.5">
64
+ <MetricLabel>{label}</MetricLabel>
65
+ <MetricValue className="text-3xl">{value}</MetricValue>
66
+ </Metric>
67
+ </CardContent>
68
+ </Card>
69
+ ))}
70
+ </div>
71
+
72
+ {/* Chart + orders */}
73
+ <RevenueChart />
74
+ <OrdersTable />
75
+ </div>
76
+ );
77
+ }
@@ -0,0 +1,21 @@
1
+ import type { Metadata } from "next";
2
+ import { SettingsForm } from "../../components/settings-form";
3
+
4
+ export const metadata: Metadata = {
5
+ title: "Settings — __APP_NAME__",
6
+ };
7
+
8
+ export default function SettingsPage() {
9
+ return (
10
+ <div className="flex flex-col gap-6 p-6">
11
+ <header className="flex flex-col gap-1">
12
+ <h1 className="font-display text-2xl font-semibold tracking-tight text-fg">Settings</h1>
13
+ <p className="text-sm text-fg-secondary">
14
+ Manage your profile, workspace preferences, and notifications.
15
+ </p>
16
+ </header>
17
+
18
+ <SettingsForm />
19
+ </div>
20
+ );
21
+ }
@@ -0,0 +1,120 @@
1
+ "use client";
2
+
3
+ import { useTheme } from "@cronus-ui/theme";
4
+ import { AppShell } from "@cronus-ui/ui/app-shell";
5
+ import { Avatar, AvatarFallback } from "@cronus-ui/ui/avatar";
6
+ import { Button } from "@cronus-ui/ui/button";
7
+ import {
8
+ Sidebar,
9
+ SidebarContent,
10
+ SidebarFooter,
11
+ SidebarGroup,
12
+ SidebarGroupContent,
13
+ SidebarGroupLabel,
14
+ SidebarHeader,
15
+ SidebarMenu,
16
+ SidebarMenuButton,
17
+ SidebarMenuItem,
18
+ SidebarSeparator,
19
+ SidebarTrigger,
20
+ } from "@cronus-ui/ui/sidebar";
21
+ import { Bell, LayoutDashboard, Moon, Settings, Sparkles, Sun } from "lucide-react";
22
+ import Link from "next/link";
23
+ import { usePathname } from "next/navigation";
24
+ import type { ReactNode } from "react";
25
+
26
+ const NAV_ITEMS = [
27
+ { href: "/", label: "Overview", icon: LayoutDashboard },
28
+ { href: "/settings", label: "Settings", icon: Settings },
29
+ ] as const;
30
+
31
+ /**
32
+ * The persistent application chrome: an icon-collapsible sidebar with the
33
+ * route-aware nav, plus a sticky topbar with the sidebar trigger, a color-mode
34
+ * toggle, and the current user. Pages render inside as `children`.
35
+ */
36
+ export function DashboardShell({ children }: { children: ReactNode }) {
37
+ const pathname = usePathname();
38
+ const { mode, toggleMode } = useTheme();
39
+
40
+ const sidebar = (
41
+ <Sidebar collapsible="icon" aria-label="Primary">
42
+ <SidebarHeader>
43
+ <div className="flex items-center gap-2 px-1.5 py-1.5 group-data-[collapsible=icon]/sidebar:justify-center group-data-[collapsible=icon]/sidebar:px-0">
44
+ <span className="inline-flex size-8 shrink-0 items-center justify-center rounded-lg bg-gradient-primary text-primary-foreground shadow-glow">
45
+ <Sparkles className="size-4" aria-hidden="true" />
46
+ </span>
47
+ <span className="truncate text-sm font-semibold text-fg group-data-[collapsible=icon]/sidebar:hidden">
48
+ __APP_NAME__
49
+ </span>
50
+ </div>
51
+ </SidebarHeader>
52
+
53
+ <SidebarSeparator />
54
+
55
+ <SidebarContent>
56
+ <SidebarGroup>
57
+ <SidebarGroupLabel>Workspace</SidebarGroupLabel>
58
+ <SidebarGroupContent>
59
+ <SidebarMenu>
60
+ {NAV_ITEMS.map((item) => (
61
+ <SidebarMenuItem key={item.href}>
62
+ <SidebarMenuButton asChild isActive={pathname === item.href} tooltip={item.label}>
63
+ <Link href={item.href}>
64
+ <item.icon aria-hidden="true" />
65
+ <span>{item.label}</span>
66
+ </Link>
67
+ </SidebarMenuButton>
68
+ </SidebarMenuItem>
69
+ ))}
70
+ </SidebarMenu>
71
+ </SidebarGroupContent>
72
+ </SidebarGroup>
73
+ </SidebarContent>
74
+
75
+ <SidebarFooter>
76
+ <div className="flex items-center gap-2 rounded-lg px-2 py-1.5 group-data-[collapsible=icon]/sidebar:justify-center group-data-[collapsible=icon]/sidebar:px-0">
77
+ <Avatar className="size-8">
78
+ <AvatarFallback>AC</AvatarFallback>
79
+ </Avatar>
80
+ <div className="flex min-w-0 flex-col group-data-[collapsible=icon]/sidebar:hidden">
81
+ <span className="truncate text-sm font-medium text-fg">Alex Costa</span>
82
+ <span className="truncate text-xs text-fg-tertiary">alex@example.com</span>
83
+ </div>
84
+ </div>
85
+ </SidebarFooter>
86
+ </Sidebar>
87
+ );
88
+
89
+ const header = (
90
+ <div className="flex w-full items-center gap-3">
91
+ <SidebarTrigger />
92
+ <span className="text-sm font-semibold text-fg">
93
+ {NAV_ITEMS.find((item) => item.href === pathname)?.label ?? "__APP_NAME__"}
94
+ </span>
95
+ <div className="ml-auto flex items-center gap-1">
96
+ <Button
97
+ variant="ghost"
98
+ size="icon-sm"
99
+ aria-label={mode === "dark" ? "Switch to light mode" : "Switch to dark mode"}
100
+ onClick={toggleMode}
101
+ >
102
+ {mode === "dark" ? (
103
+ <Moon className="size-4" aria-hidden="true" />
104
+ ) : (
105
+ <Sun className="size-4" aria-hidden="true" />
106
+ )}
107
+ </Button>
108
+ <Button variant="ghost" size="icon-sm" aria-label="Notifications">
109
+ <Bell className="size-4" aria-hidden="true" />
110
+ </Button>
111
+ </div>
112
+ </div>
113
+ );
114
+
115
+ return (
116
+ <AppShell sidebar={sidebar} header={header}>
117
+ <main id="main-content">{children}</main>
118
+ </AppShell>
119
+ );
120
+ }
@@ -0,0 +1,132 @@
1
+ "use client";
2
+
3
+ import { Badge } from "@cronus-ui/ui/badge";
4
+ import { Card, CardHeader, CardTitle } from "@cronus-ui/ui/card";
5
+ import { DataTable, DataTableColumnHeader } from "@cronus-ui/ui/data-table";
6
+ import type { ColumnDef } from "@tanstack/react-table";
7
+
8
+ interface Order {
9
+ id: string;
10
+ customer: string;
11
+ email: string;
12
+ status: "Paid" | "Pending" | "Refunded";
13
+ amount: number;
14
+ }
15
+
16
+ const STATUS_VARIANT = {
17
+ Paid: "success",
18
+ Pending: "warning",
19
+ Refunded: "secondary",
20
+ } as const;
21
+
22
+ const ORDERS: Order[] = [
23
+ {
24
+ id: "INV-2048",
25
+ customer: "Mara Castillo",
26
+ email: "mara@northwind.io",
27
+ status: "Paid",
28
+ amount: 1290,
29
+ },
30
+ {
31
+ id: "INV-2047",
32
+ customer: "Devon Lane",
33
+ email: "devon@acme.dev",
34
+ status: "Pending",
35
+ amount: 640,
36
+ },
37
+ {
38
+ id: "INV-2046",
39
+ customer: "Priya Sharma",
40
+ email: "priya@lumon.co",
41
+ status: "Paid",
42
+ amount: 2180,
43
+ },
44
+ {
45
+ id: "INV-2045",
46
+ customer: "Tobias Funke",
47
+ email: "tobias@bluth.com",
48
+ status: "Refunded",
49
+ amount: 320,
50
+ },
51
+ { id: "INV-2044", customer: "Aiko Tanaka", email: "aiko@hooli.com", status: "Paid", amount: 990 },
52
+ {
53
+ id: "INV-2043",
54
+ customer: "Jonas Berg",
55
+ email: "jonas@fathom.dev",
56
+ status: "Paid",
57
+ amount: 1475,
58
+ },
59
+ {
60
+ id: "INV-2042",
61
+ customer: "Sofia Lindqvist",
62
+ email: "sofia@driftwood.se",
63
+ status: "Pending",
64
+ amount: 210,
65
+ },
66
+ {
67
+ id: "INV-2041",
68
+ customer: "David Chen",
69
+ email: "david@parallel.io",
70
+ status: "Paid",
71
+ amount: 3120,
72
+ },
73
+ ];
74
+
75
+ const currency = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
76
+
77
+ const COLUMNS: ColumnDef<Order>[] = [
78
+ {
79
+ accessorKey: "id",
80
+ header: ({ column }) => <DataTableColumnHeader column={column} title="Order" />,
81
+ cell: ({ row }) => <span className="font-medium text-fg">{row.original.id}</span>,
82
+ },
83
+ {
84
+ accessorKey: "customer",
85
+ header: ({ column }) => <DataTableColumnHeader column={column} title="Customer" />,
86
+ cell: ({ row }) => (
87
+ <div className="flex min-w-0 flex-col">
88
+ <span className="truncate text-sm font-medium text-fg">{row.original.customer}</span>
89
+ <span className="truncate text-xs text-fg-tertiary">{row.original.email}</span>
90
+ </div>
91
+ ),
92
+ },
93
+ {
94
+ accessorKey: "status",
95
+ header: ({ column }) => <DataTableColumnHeader column={column} title="Status" />,
96
+ cell: ({ row }) => (
97
+ <Badge variant={STATUS_VARIANT[row.original.status]}>{row.original.status}</Badge>
98
+ ),
99
+ },
100
+ {
101
+ accessorKey: "amount",
102
+ header: ({ column }) => (
103
+ <DataTableColumnHeader column={column} title="Amount" className="justify-end" />
104
+ ),
105
+ cell: ({ row }) => (
106
+ <span className="block text-right font-medium tabular-nums text-fg">
107
+ {currency.format(row.original.amount)}
108
+ </span>
109
+ ),
110
+ },
111
+ ];
112
+
113
+ /** Recent orders in a searchable, sortable, paginated `DataTable`. */
114
+ export function OrdersTable() {
115
+ return (
116
+ <Card className="gap-0 py-0">
117
+ <CardHeader className="px-6 py-5">
118
+ <CardTitle className="font-display text-base">Recent orders</CardTitle>
119
+ <p className="text-sm text-fg-secondary">Latest invoices and payments</p>
120
+ </CardHeader>
121
+ <DataTable
122
+ columns={COLUMNS}
123
+ data={ORDERS}
124
+ searchable
125
+ searchPlaceholder="Search orders…"
126
+ pagination
127
+ initialPageSize={5}
128
+ className="px-6 pb-6"
129
+ />
130
+ </Card>
131
+ );
132
+ }
@@ -0,0 +1,50 @@
1
+ "use client";
2
+
3
+ import { Card, CardContent, CardHeader, CardTitle } from "@cronus-ui/ui/card";
4
+ import {
5
+ type ChartConfig,
6
+ ChartContainer,
7
+ ChartTooltip,
8
+ ChartTooltipContent,
9
+ } from "@cronus-ui/ui/chart";
10
+ import { MetricDelta } from "@cronus-ui/ui/metric";
11
+ import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
12
+
13
+ const REVENUE_DATA = [
14
+ { month: "Jan", revenue: 42100 },
15
+ { month: "Feb", revenue: 48400 },
16
+ { month: "Mar", revenue: 45900 },
17
+ { month: "Apr", revenue: 56200 },
18
+ { month: "May", revenue: 61800 },
19
+ { month: "Jun", revenue: 72400 },
20
+ { month: "Jul", revenue: 84210 },
21
+ ];
22
+
23
+ const chartConfig = {
24
+ revenue: { label: "Revenue", color: "var(--color-chart-1)" },
25
+ } satisfies ChartConfig;
26
+
27
+ /** Monthly recurring revenue as a themed bar chart (recharts via `chart`). */
28
+ export function RevenueChart() {
29
+ return (
30
+ <Card>
31
+ <CardHeader className="flex-row items-center justify-between gap-3">
32
+ <div className="flex flex-col gap-1">
33
+ <CardTitle className="font-display text-base">Revenue trend</CardTitle>
34
+ <p className="text-sm text-fg-secondary">Monthly recurring revenue</p>
35
+ </div>
36
+ <MetricDelta trend="up">+99.8% YoY</MetricDelta>
37
+ </CardHeader>
38
+ <CardContent>
39
+ <ChartContainer config={chartConfig} className="aspect-[16/6] w-full">
40
+ <BarChart accessibilityLayer data={REVENUE_DATA} margin={{ left: 4, right: 4 }}>
41
+ <CartesianGrid vertical={false} />
42
+ <XAxis dataKey="month" tickLine={false} axisLine={false} tickMargin={8} fontSize={12} />
43
+ <ChartTooltip cursor={false} content={<ChartTooltipContent indicator="line" />} />
44
+ <Bar dataKey="revenue" fill="var(--color-chart-1)" radius={[6, 6, 0, 0]} />
45
+ </BarChart>
46
+ </ChartContainer>
47
+ </CardContent>
48
+ </Card>
49
+ );
50
+ }
@@ -0,0 +1,167 @@
1
+ "use client";
2
+
3
+ import { Button } from "@cronus-ui/ui/button";
4
+ import {
5
+ Card,
6
+ CardContent,
7
+ CardDescription,
8
+ CardFooter,
9
+ CardHeader,
10
+ CardTitle,
11
+ } from "@cronus-ui/ui/card";
12
+ import { Field, FieldDescription, FieldLabel } from "@cronus-ui/ui/field";
13
+ import { Input } from "@cronus-ui/ui/input";
14
+ import {
15
+ Select,
16
+ SelectContent,
17
+ SelectItem,
18
+ SelectTrigger,
19
+ SelectValue,
20
+ } from "@cronus-ui/ui/select";
21
+ import { Separator } from "@cronus-ui/ui/separator";
22
+ import { Switch } from "@cronus-ui/ui/switch";
23
+ import { Textarea } from "@cronus-ui/ui/textarea";
24
+ import { Check } from "lucide-react";
25
+ import { useState } from "react";
26
+
27
+ const NOTIFICATIONS = [
28
+ {
29
+ id: "notify-orders",
30
+ label: "New orders",
31
+ description: "Get notified when a new order comes in.",
32
+ defaultChecked: true,
33
+ },
34
+ {
35
+ id: "notify-digest",
36
+ label: "Weekly digest",
37
+ description: "A summary of your workspace, every Monday.",
38
+ defaultChecked: true,
39
+ },
40
+ {
41
+ id: "notify-mentions",
42
+ label: "Mentions",
43
+ description: "When a teammate mentions you in a comment.",
44
+ defaultChecked: false,
45
+ },
46
+ ] as const;
47
+
48
+ /**
49
+ * Profile + workspace preference forms. The submit is a demo — replace the
50
+ * `onSubmit` with a server action or API call.
51
+ */
52
+ export function SettingsForm() {
53
+ const [saved, setSaved] = useState(false);
54
+
55
+ return (
56
+ <form
57
+ className="flex max-w-3xl flex-col gap-6"
58
+ onSubmit={(event) => {
59
+ event.preventDefault();
60
+ setSaved(true);
61
+ }}
62
+ >
63
+ <Card>
64
+ <CardHeader>
65
+ <CardTitle className="font-display text-base">Profile</CardTitle>
66
+ <CardDescription>How you appear across the workspace.</CardDescription>
67
+ </CardHeader>
68
+ <CardContent className="grid gap-5 sm:grid-cols-2">
69
+ <Field>
70
+ <FieldLabel htmlFor="settings-name">Full name</FieldLabel>
71
+ <Input id="settings-name" name="name" defaultValue="Alex Costa" autoComplete="name" />
72
+ </Field>
73
+ <Field>
74
+ <FieldLabel htmlFor="settings-email">Email</FieldLabel>
75
+ <Input
76
+ id="settings-email"
77
+ name="email"
78
+ type="email"
79
+ defaultValue="alex@example.com"
80
+ autoComplete="email"
81
+ />
82
+ </Field>
83
+ <Field className="sm:col-span-2">
84
+ <FieldLabel htmlFor="settings-bio">Bio</FieldLabel>
85
+ <Textarea
86
+ id="settings-bio"
87
+ name="bio"
88
+ rows={3}
89
+ placeholder="A short description for your profile."
90
+ />
91
+ <FieldDescription>Shown on your public profile.</FieldDescription>
92
+ </Field>
93
+ </CardContent>
94
+ </Card>
95
+
96
+ <Card>
97
+ <CardHeader>
98
+ <CardTitle className="font-display text-base">Workspace</CardTitle>
99
+ <CardDescription>Defaults applied to everyone in this workspace.</CardDescription>
100
+ </CardHeader>
101
+ <CardContent className="grid gap-5 sm:grid-cols-2">
102
+ <Field>
103
+ <FieldLabel htmlFor="settings-language">Language</FieldLabel>
104
+ <Select defaultValue="en" name="language">
105
+ <SelectTrigger id="settings-language" className="w-full">
106
+ <SelectValue placeholder="Select a language" />
107
+ </SelectTrigger>
108
+ <SelectContent>
109
+ <SelectItem value="en">English</SelectItem>
110
+ <SelectItem value="pt">Português</SelectItem>
111
+ <SelectItem value="es">Español</SelectItem>
112
+ </SelectContent>
113
+ </Select>
114
+ </Field>
115
+ <Field>
116
+ <FieldLabel htmlFor="settings-timezone">Timezone</FieldLabel>
117
+ <Select defaultValue="utc" name="timezone">
118
+ <SelectTrigger id="settings-timezone" className="w-full">
119
+ <SelectValue placeholder="Select a timezone" />
120
+ </SelectTrigger>
121
+ <SelectContent>
122
+ <SelectItem value="utc">UTC</SelectItem>
123
+ <SelectItem value="sao-paulo">America/São Paulo</SelectItem>
124
+ <SelectItem value="new-york">America/New York</SelectItem>
125
+ <SelectItem value="london">Europe/London</SelectItem>
126
+ </SelectContent>
127
+ </Select>
128
+ </Field>
129
+ </CardContent>
130
+ </Card>
131
+
132
+ <Card>
133
+ <CardHeader>
134
+ <CardTitle className="font-display text-base">Notifications</CardTitle>
135
+ <CardDescription>Choose what you want to hear about.</CardDescription>
136
+ </CardHeader>
137
+ <CardContent className="flex flex-col">
138
+ {NOTIFICATIONS.map((item, index) => (
139
+ <div key={item.id}>
140
+ {index > 0 ? <Separator className="my-4" /> : null}
141
+ <div className="flex items-center justify-between gap-4">
142
+ <div className="flex min-w-0 flex-col gap-0.5">
143
+ <label htmlFor={item.id} className="text-sm font-medium text-fg">
144
+ {item.label}
145
+ </label>
146
+ <span className="text-sm text-fg-secondary">{item.description}</span>
147
+ </div>
148
+ <Switch id={item.id} name={item.id} defaultChecked={item.defaultChecked} />
149
+ </div>
150
+ </div>
151
+ ))}
152
+ </CardContent>
153
+ <CardFooter className="justify-end gap-3">
154
+ {saved ? (
155
+ <span role="status" className="inline-flex items-center gap-1.5 text-sm text-success">
156
+ <Check className="size-4" aria-hidden="true" />
157
+ Saved
158
+ </span>
159
+ ) : null}
160
+ <Button type="submit" variant="primary">
161
+ Save changes
162
+ </Button>
163
+ </CardFooter>
164
+ </Card>
165
+ </form>
166
+ );
167
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "aliases": {
3
+ "ui": "@/components/ui",
4
+ "lib": "@/lib",
5
+ "blocks": "@/components/blocks"
6
+ },
7
+ "paths": {
8
+ "ui": "components/ui",
9
+ "lib": "lib",
10
+ "blocks": "components/blocks"
11
+ },
12
+ "registry": "https://raw.githubusercontent.com/pedrogbraz/cronus-ui/v0.6.0/registry",
13
+ "theme": {
14
+ "name": "__THEME__",
15
+ "mode": "__MODE__"
16
+ }
17
+ }
@@ -0,0 +1,34 @@
1
+ # dependencies
2
+ /node_modules
3
+ /.pnp
4
+ .pnp.*
5
+
6
+ # next.js
7
+ /.next/
8
+ /out/
9
+
10
+ # production
11
+ /build
12
+
13
+ # misc
14
+ .DS_Store
15
+ *.pem
16
+
17
+ # debug
18
+ npm-debug.log*
19
+ yarn-debug.log*
20
+ yarn-error.log*
21
+ .pnpm-debug.log*
22
+
23
+ # env files
24
+ .env*.local
25
+
26
+ # typescript
27
+ *.tsbuildinfo
28
+ next-env.d.ts
29
+
30
+ # lockfiles (keep the one your package manager writes)
31
+ # package-lock.json
32
+ # pnpm-lock.yaml
33
+ # yarn.lock
34
+ # bun.lockb
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @cronus-ui/ui ships ESM with its `"use client"` boundaries already in place,
3
+ * so no `transpilePackages` is needed when it's installed (the normal case).
4
+ *
5
+ * @type {import('next').NextConfig}
6
+ */
7
+ const nextConfig = {
8
+ reactStrictMode: true,
9
+ };
10
+
11
+ export default nextConfig;