mycontext-cli 4.2.7 → 4.2.8

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 (95) hide show
  1. package/dist/commands/generate.d.ts.map +1 -1
  2. package/dist/commands/generate.js +19 -60
  3. package/dist/commands/generate.js.map +1 -1
  4. package/dist/commands/init-interactive.d.ts +20 -0
  5. package/dist/commands/init-interactive.d.ts.map +1 -1
  6. package/dist/commands/init-interactive.js +168 -5
  7. package/dist/commands/init-interactive.js.map +1 -1
  8. package/dist/config/shadcn-catalog.json +93 -0
  9. package/dist/core/brain/BrainClient.d.ts +1 -1
  10. package/dist/core/brain/BrainClient.d.ts.map +1 -1
  11. package/dist/core/brain/BrainClient.js +5 -5
  12. package/dist/core/brain/BrainClient.js.map +1 -1
  13. package/dist/doctor/DoctorEngine.d.ts.map +1 -1
  14. package/dist/doctor/DoctorEngine.js +21 -11
  15. package/dist/doctor/DoctorEngine.js.map +1 -1
  16. package/dist/doctor/rules/dead-code-rules.d.ts.map +1 -1
  17. package/dist/doctor/rules/dead-code-rules.js +33 -0
  18. package/dist/doctor/rules/dead-code-rules.js.map +1 -1
  19. package/dist/doctor/rules/instantdb-rules.d.ts.map +1 -1
  20. package/dist/doctor/rules/instantdb-rules.js +278 -69
  21. package/dist/doctor/rules/instantdb-rules.js.map +1 -1
  22. package/dist/doctor/rules/nextjs-rules.d.ts.map +1 -1
  23. package/dist/doctor/rules/nextjs-rules.js +53 -3
  24. package/dist/doctor/rules/nextjs-rules.js.map +1 -1
  25. package/dist/package.json +4 -2
  26. package/dist/services/ComponentInferenceEngine.d.ts +66 -0
  27. package/dist/services/ComponentInferenceEngine.d.ts.map +1 -0
  28. package/dist/services/ComponentInferenceEngine.js +302 -0
  29. package/dist/services/ComponentInferenceEngine.js.map +1 -0
  30. package/dist/services/ComponentRegistry.d.ts +61 -0
  31. package/dist/services/ComponentRegistry.d.ts.map +1 -0
  32. package/dist/services/ComponentRegistry.js +128 -0
  33. package/dist/services/ComponentRegistry.js.map +1 -0
  34. package/dist/services/InferenceEngine.js +1 -1
  35. package/dist/services/ProjectScanner.d.ts.map +1 -1
  36. package/dist/services/ProjectScanner.js +15 -1
  37. package/dist/services/ProjectScanner.js.map +1 -1
  38. package/dist/services/ScaffoldEngine.d.ts +87 -0
  39. package/dist/services/ScaffoldEngine.d.ts.map +1 -0
  40. package/dist/services/ScaffoldEngine.js +409 -0
  41. package/dist/services/ScaffoldEngine.js.map +1 -0
  42. package/dist/services/ScaffoldPreview.d.ts +62 -0
  43. package/dist/services/ScaffoldPreview.d.ts.map +1 -0
  44. package/dist/services/ScaffoldPreview.js +292 -0
  45. package/dist/services/ScaffoldPreview.js.map +1 -0
  46. package/dist/services/TemplateEngine.d.ts +136 -0
  47. package/dist/services/TemplateEngine.d.ts.map +1 -0
  48. package/dist/services/TemplateEngine.js +483 -0
  49. package/dist/services/TemplateEngine.js.map +1 -0
  50. package/dist/services/TemplateHelpers.d.ts +9 -0
  51. package/dist/services/TemplateHelpers.d.ts.map +1 -0
  52. package/dist/services/TemplateHelpers.js +212 -0
  53. package/dist/services/TemplateHelpers.js.map +1 -0
  54. package/dist/templates/actions/auth-actions.ts.hbs +140 -0
  55. package/dist/templates/actions/crud-actions.ts.hbs +113 -0
  56. package/dist/templates/components/auth/login-form.tsx.hbs +67 -0
  57. package/dist/templates/components/auth/login-skeleton.tsx.hbs +24 -0
  58. package/dist/templates/components/auth/register-form.tsx.hbs +116 -0
  59. package/dist/templates/components/crud/entity-card.tsx.hbs +71 -0
  60. package/dist/templates/components/crud/entity-form.tsx.hbs +158 -0
  61. package/dist/templates/components/crud/entity-skeleton.tsx.hbs +90 -0
  62. package/dist/templates/components/crud/entity-table.tsx.hbs +129 -0
  63. package/dist/templates/components/ui/button.tsx.hbs +53 -0
  64. package/dist/templates/components/ui/card.tsx.hbs +68 -0
  65. package/dist/templates/components/ui/input.tsx.hbs +33 -0
  66. package/dist/templates/components/ui/label.tsx.hbs +20 -0
  67. package/dist/templates/components/ui/skeleton.tsx.hbs +15 -0
  68. package/dist/templates/components/ui/theme-provider.tsx.hbs +66 -0
  69. package/dist/templates/components/ui/theme-toggle.tsx.hbs +30 -0
  70. package/dist/templates/config/app.css.hbs +150 -0
  71. package/dist/templates/layouts/dashboard-layout.tsx.hbs +69 -0
  72. package/dist/templates/layouts/error.tsx.hbs +51 -0
  73. package/dist/templates/layouts/loading.tsx.hbs +22 -0
  74. package/dist/templates/layouts/not-found.tsx.hbs +24 -0
  75. package/dist/templates/layouts/root-layout.tsx.hbs +40 -0
  76. package/dist/templates/lib/instant.ts.hbs +19 -0
  77. package/dist/templates/lib/utils.ts.hbs +24 -0
  78. package/dist/templates/pages/auth/login-page.tsx.hbs +30 -0
  79. package/dist/templates/pages/auth/register-page.tsx.hbs +38 -0
  80. package/dist/templates/pages/crud/create-page.tsx.hbs +42 -0
  81. package/dist/templates/pages/crud/detail-page.tsx.hbs +90 -0
  82. package/dist/templates/pages/crud/list-page.tsx.hbs +60 -0
  83. package/dist/templates/pages/crud/loading.tsx.hbs +13 -0
  84. package/dist/templates/pages/landing-page.tsx.hbs +111 -0
  85. package/dist/types/asl.d.ts +1 -1
  86. package/dist/types/asl.d.ts.map +1 -1
  87. package/dist/types/living-context.d.ts +1 -1
  88. package/dist/types/living-context.d.ts.map +1 -1
  89. package/dist/utils/FileGenerator.js +3 -3
  90. package/dist/utils/FileGenerator.js.map +1 -1
  91. package/dist/utils/generateTypesFromSchema.d.ts +47 -0
  92. package/dist/utils/generateTypesFromSchema.d.ts.map +1 -0
  93. package/dist/utils/generateTypesFromSchema.js +298 -0
  94. package/dist/utils/generateTypesFromSchema.js.map +1 -0
  95. package/package.json +4 -2
@@ -0,0 +1,66 @@
1
+ 'use client'
2
+
3
+ import { createContext, useContext, useEffect, useState } from 'react'
4
+
5
+ type Theme = 'dark' | 'light' | 'system'
6
+
7
+ interface ThemeContextType {
8
+ theme: Theme
9
+ setTheme: (theme: Theme) => void
10
+ resolvedTheme: 'dark' | 'light'
11
+ }
12
+
13
+ const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
14
+
15
+ export function ThemeProvider({
16
+ children,
17
+ defaultTheme = 'system',
18
+ storageKey = 'theme',
19
+ }: {
20
+ children: React.ReactNode
21
+ defaultTheme?: Theme
22
+ storageKey?: string
23
+ }) {
24
+ const [theme, setTheme] = useState<Theme>(defaultTheme)
25
+ const [resolvedTheme, setResolvedTheme] = useState<'dark' | 'light'>('light')
26
+
27
+ useEffect(() => {
28
+ const stored = localStorage.getItem(storageKey) as Theme | null
29
+ if (stored) setTheme(stored)
30
+ }, [storageKey])
31
+
32
+ useEffect(() => {
33
+ const root = document.documentElement
34
+ root.classList.remove('light', 'dark')
35
+
36
+ const resolved = theme === 'system'
37
+ ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
38
+ : theme
39
+
40
+ root.classList.add(resolved)
41
+ setResolvedTheme(resolved)
42
+
43
+ // Update meta theme-color for mobile browsers
44
+ const metaThemeColor = document.querySelector('meta[name="theme-color"]')
45
+ if (metaThemeColor) {
46
+ metaThemeColor.setAttribute('content', resolved === 'dark' ? '#09090b' : '#ffffff')
47
+ }
48
+ }, [theme])
49
+
50
+ return (
51
+ <ThemeContext.Provider value={ { theme, setTheme: (newTheme)=> {
52
+ localStorage.setItem(storageKey, newTheme)
53
+ setTheme(newTheme)
54
+ },
55
+ resolvedTheme,
56
+ } }>
57
+ {children}
58
+ </ThemeContext.Provider>
59
+ )
60
+ }
61
+
62
+ export const useTheme = () => {
63
+ const context = useContext(ThemeContext)
64
+ if (!context) throw new Error('useTheme must be used within ThemeProvider')
65
+ return context
66
+ }
@@ -0,0 +1,30 @@
1
+ 'use client'
2
+
3
+ import { Moon, Sun } from 'lucide-react'
4
+ import { useTheme } from './theme-provider'
5
+ import { Button } from './button'
6
+
7
+ export function ThemeToggle() {
8
+ const { theme, setTheme, resolvedTheme } = useTheme()
9
+
10
+ const toggleTheme = () => {
11
+ if (theme === 'system') {
12
+ setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')
13
+ } else {
14
+ setTheme(theme === 'dark' ? 'light' : 'dark')
15
+ }
16
+ }
17
+
18
+ return (
19
+ <Button
20
+ variant="ghost"
21
+ size="icon"
22
+ onClick={toggleTheme}
23
+ aria-label="Toggle theme"
24
+ >
25
+ <Sun className="size-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
26
+ <Moon className="absolute size-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
27
+ <span className="sr-only">Toggle theme</span>
28
+ </Button>
29
+ )
30
+ }
@@ -0,0 +1,150 @@
1
+ @import "tailwindcss";
2
+
3
+ /* Tailwind v4 CSS-first configuration with design tokens */
4
+ @theme {
5
+ /* Semantic color tokens using OKLCH for better color perception */
6
+ --color-background: oklch(100% 0 0);
7
+ --color-foreground: oklch(14.5% 0.025 264);
8
+
9
+ --color-primary: oklch(14.5% 0.025 264);
10
+ --color-primary-foreground: oklch(98% 0.01 264);
11
+
12
+ --color-secondary: oklch(96% 0.01 264);
13
+ --color-secondary-foreground: oklch(14.5% 0.025 264);
14
+
15
+ --color-muted: oklch(96% 0.01 264);
16
+ --color-muted-foreground: oklch(46% 0.02 264);
17
+
18
+ --color-accent: oklch(96% 0.01 264);
19
+ --color-accent-foreground: oklch(14.5% 0.025 264);
20
+
21
+ --color-destructive: oklch(53% 0.22 27);
22
+ --color-destructive-foreground: oklch(98% 0.01 264);
23
+
24
+ --color-border: oklch(91% 0.01 264);
25
+ --color-ring: oklch(14.5% 0.025 264);
26
+
27
+ --color-card: oklch(100% 0 0);
28
+ --color-card-foreground: oklch(14.5% 0.025 264);
29
+
30
+ --color-ring-offset: oklch(100% 0 0);
31
+
32
+ /* Radius tokens */
33
+ --radius-sm: 0.25rem;
34
+ --radius-md: 0.375rem;
35
+ --radius-lg: 0.5rem;
36
+ --radius-xl: 0.75rem;
37
+
38
+ /* Animation tokens */
39
+ --animate-fade-in: fade-in 0.2s ease-out;
40
+ --animate-fade-out: fade-out 0.2s ease-in;
41
+ --animate-slide-in: slide-in 0.3s ease-out;
42
+ --animate-slide-out: slide-out 0.3s ease-in;
43
+ --animate-dialog-in: dialog-fade-in 0.2s ease-out;
44
+ --animate-dialog-out: dialog-fade-out 0.15s ease-in;
45
+
46
+ @keyframes fade-in {
47
+ from {
48
+ opacity: 0;
49
+ }
50
+ to {
51
+ opacity: 1;
52
+ }
53
+ }
54
+
55
+ @keyframes fade-out {
56
+ from {
57
+ opacity: 1;
58
+ }
59
+ to {
60
+ opacity: 0;
61
+ }
62
+ }
63
+
64
+ @keyframes slide-in {
65
+ from {
66
+ transform: translateY(-0.5rem);
67
+ opacity: 0;
68
+ }
69
+ to {
70
+ transform: translateY(0);
71
+ opacity: 1;
72
+ }
73
+ }
74
+
75
+ @keyframes slide-out {
76
+ from {
77
+ transform: translateY(0);
78
+ opacity: 1;
79
+ }
80
+ to {
81
+ transform: translateY(-0.5rem);
82
+ opacity: 0;
83
+ }
84
+ }
85
+
86
+ @keyframes dialog-fade-in {
87
+ from {
88
+ opacity: 0;
89
+ transform: scale(0.95) translateY(-0.5rem);
90
+ }
91
+ to {
92
+ opacity: 1;
93
+ transform: scale(1) translateY(0);
94
+ }
95
+ }
96
+
97
+ @keyframes dialog-fade-out {
98
+ from {
99
+ opacity: 1;
100
+ transform: scale(1) translateY(0);
101
+ }
102
+ to {
103
+ opacity: 0;
104
+ transform: scale(0.95) translateY(-0.5rem);
105
+ }
106
+ }
107
+ }
108
+
109
+ /* Dark mode variant */
110
+ @custom-variant dark (&:where(.dark, .dark *));
111
+
112
+ /* Dark mode theme overrides */
113
+ .dark {
114
+ --color-background: oklch(14.5% 0.025 264);
115
+ --color-foreground: oklch(98% 0.01 264);
116
+
117
+ --color-primary: oklch(98% 0.01 264);
118
+ --color-primary-foreground: oklch(14.5% 0.025 264);
119
+
120
+ --color-secondary: oklch(22% 0.02 264);
121
+ --color-secondary-foreground: oklch(98% 0.01 264);
122
+
123
+ --color-muted: oklch(22% 0.02 264);
124
+ --color-muted-foreground: oklch(65% 0.02 264);
125
+
126
+ --color-accent: oklch(22% 0.02 264);
127
+ --color-accent-foreground: oklch(98% 0.01 264);
128
+
129
+ --color-destructive: oklch(42% 0.15 27);
130
+ --color-destructive-foreground: oklch(98% 0.01 264);
131
+
132
+ --color-border: oklch(22% 0.02 264);
133
+ --color-ring: oklch(83% 0.02 264);
134
+
135
+ --color-card: oklch(14.5% 0.025 264);
136
+ --color-card-foreground: oklch(98% 0.01 264);
137
+
138
+ --color-ring-offset: oklch(14.5% 0.025 264);
139
+ }
140
+
141
+ /* Base styles */
142
+ @layer base {
143
+ * {
144
+ @apply border-border;
145
+ }
146
+
147
+ body {
148
+ @apply bg-background text-foreground antialiased;
149
+ }
150
+ }
@@ -0,0 +1,69 @@
1
+ import { redirect } from 'next/navigation'
2
+ import Link from 'next/link'
3
+ import { Button } from '@/components/ui/button'
4
+ import { ThemeToggle } from '@/components/ui/theme-toggle'
5
+ import { getCurrentUser, logout } from '@/app/actions/auth'
6
+
7
+ export default async function DashboardLayout({
8
+ children,
9
+ }: {
10
+ children: React.ReactNode
11
+ }) {
12
+ const { user } = await getCurrentUser()
13
+
14
+ if (!user) {
15
+ redirect('/login')
16
+ }
17
+
18
+ return (
19
+ <div className="flex min-h-screen flex-col">
20
+ {/* Header */}
21
+ <header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
22
+ <div className="container flex h-14 items-center">
23
+ <div className="mr-4 flex">
24
+ <Link href="/dashboard" className="mr-6 flex items-center space-x-2">
25
+ <span className="font-bold">{{projectName}}</span>
26
+ </Link>
27
+ <nav className="flex items-center space-x-6 text-sm font-medium">
28
+ <Link
29
+ href="/dashboard"
30
+ className="transition-colors hover:text-foreground/80"
31
+ >
32
+ Dashboard
33
+ </Link>
34
+ {{#each entities}}
35
+ <Link
36
+ href="/{{lowercase name}}s"
37
+ className="transition-colors hover:text-foreground/80"
38
+ >
39
+ {{capitalize name}}s
40
+ </Link>
41
+ {{/each}}
42
+ </nav>
43
+ </div>
44
+ <div className="ml-auto flex items-center space-x-4">
45
+ <ThemeToggle />
46
+ <div className="flex items-center gap-2">
47
+ <span className="text-sm text-muted-foreground">{user.name || user.email}</span>
48
+ <form action={logout}>
49
+ <Button type="submit" variant="ghost" size="sm">
50
+ Log out
51
+ </Button>
52
+ </form>
53
+ </div>
54
+ </div>
55
+ </div>
56
+ </header>
57
+
58
+ {/* Main Content */}
59
+ <main className="flex-1">{children}</main>
60
+
61
+ {/* Footer */}
62
+ <footer className="border-t py-6">
63
+ <div className="container text-center text-sm text-muted-foreground">
64
+ <p>&copy; {new Date().getFullYear()} {{projectName}}. All rights reserved.</p>
65
+ </div>
66
+ </footer>
67
+ </div>
68
+ )
69
+ }
@@ -0,0 +1,51 @@
1
+ 'use client'
2
+
3
+ import { useEffect } from 'react'
4
+ import { Button } from '@/components/ui/button'
5
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
6
+
7
+ export default function Error({
8
+ error,
9
+ reset,
10
+ }: {
11
+ error: Error & { digest?: string }
12
+ reset: () => void
13
+ }) {
14
+ useEffect(() => {
15
+ // Log the error to an error reporting service
16
+ console.error(error)
17
+ }, [error])
18
+
19
+ return (
20
+ <div className="container mx-auto flex min-h-screen items-center justify-center px-4">
21
+ <Card className="w-full max-w-md">
22
+ <CardHeader>
23
+ <CardTitle>Something went wrong</CardTitle>
24
+ <CardDescription>
25
+ An error occurred while loading this page
26
+ </CardDescription>
27
+ </CardHeader>
28
+ <CardContent className="space-y-4">
29
+ {error.message && (
30
+ <div className="rounded-md bg-destructive/10 p-3">
31
+ <p className="text-sm text-destructive">{error.message}</p>
32
+ </div>
33
+ )}
34
+ {error.digest && (
35
+ <p className="text-xs text-muted-foreground">
36
+ Error ID: {error.digest}
37
+ </p>
38
+ )}
39
+ <div className="flex gap-2">
40
+ <Button onClick={reset} className="flex-1">
41
+ Try again
42
+ </Button>
43
+ <Button variant="outline" asChild className="flex-1">
44
+ <a href="/">Go home</a>
45
+ </Button>
46
+ </div>
47
+ </CardContent>
48
+ </Card>
49
+ </div>
50
+ )
51
+ }
@@ -0,0 +1,22 @@
1
+ import { Skeleton } from '@/components/ui/skeleton'
2
+
3
+ export default function Loading() {
4
+ return (
5
+ <div className="container mx-auto py-10">
6
+ <div className="space-y-8">
7
+ {/* Header skeleton */}
8
+ <div className="space-y-2">
9
+ <Skeleton className="h-10 w-64" />
10
+ <Skeleton className="h-4 w-96" />
11
+ </div>
12
+
13
+ {/* Content skeleton */}
14
+ <div className="space-y-4">
15
+ <Skeleton className="h-32 w-full" />
16
+ <Skeleton className="h-32 w-full" />
17
+ <Skeleton className="h-32 w-full" />
18
+ </div>
19
+ </div>
20
+ </div>
21
+ )
22
+ }
@@ -0,0 +1,24 @@
1
+ import Link from 'next/link'
2
+ import { Button } from '@/components/ui/button'
3
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
4
+
5
+ export default function NotFound() {
6
+ return (
7
+ <div className="container mx-auto flex min-h-screen items-center justify-center px-4">
8
+ <Card className="w-full max-w-md text-center">
9
+ <CardHeader>
10
+ <div className="mb-4 text-6xl font-bold text-muted-foreground">404</div>
11
+ <CardTitle>Page not found</CardTitle>
12
+ <CardDescription>
13
+ Sorry, we couldn't find the page you're looking for
14
+ </CardDescription>
15
+ </CardHeader>
16
+ <CardContent>
17
+ <Button asChild>
18
+ <Link href="/">Go back home</Link>
19
+ </Button>
20
+ </CardContent>
21
+ </Card>
22
+ </div>
23
+ )
24
+ }
@@ -0,0 +1,40 @@
1
+ import type { Metadata } from 'next'
2
+ import { Inter } from 'next/font/google'
3
+ import { ThemeProvider } from '@/components/ui/theme-provider'
4
+ import '@/app.css'
5
+
6
+ const inter = Inter({
7
+ subsets: ['latin'],
8
+ display: 'swap',
9
+ variable: '--font-inter',
10
+ })
11
+
12
+ export const metadata: Metadata = {
13
+ title: {
14
+ default: '{{projectName}}',
15
+ template: '%s | {{projectName}}',
16
+ },
17
+ description: '{{projectDescription}}',
18
+ metadataBase: new URL(process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'),
19
+ }
20
+
21
+ export default function RootLayout({
22
+ children,
23
+ }: {
24
+ children: React.ReactNode
25
+ }) {
26
+ return (
27
+ <html lang="en" suppressHydrationWarning>
28
+ <body className={inter.variable}>
29
+ <ThemeProvider
30
+ attribute="class"
31
+ defaultTheme="system"
32
+ enableSystem
33
+ disableTransitionOnChange
34
+ >
35
+ {children}
36
+ </ThemeProvider>
37
+ </body>
38
+ </html>
39
+ )
40
+ }
@@ -0,0 +1,19 @@
1
+ import { init } from "@instantdb/react"
2
+ import schema from "@/instant.schema"
3
+
4
+ const APP_ID = process.env.NEXT_PUBLIC_INSTANT_APP_ID!
5
+
6
+ if (!APP_ID) {
7
+ throw new Error(
8
+ "Missing NEXT_PUBLIC_INSTANT_APP_ID environment variable. " +
9
+ "Get your app ID from https://instantdb.com/dash"
10
+ )
11
+ }
12
+
13
+ /**
14
+ * InstantDB client instance
15
+ *
16
+ * Usage:
17
+ * const { data, isLoading, error } = db.useQuery({ {{entityName}}s: {} })
18
+ */
19
+ export const db = init({ appId: APP_ID, schema })
@@ -0,0 +1,24 @@
1
+ import { type ClassValue, clsx } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ /**
5
+ * Merge Tailwind CSS classes with proper precedence
6
+ * @param inputs - Class values to merge
7
+ * @returns Merged class string
8
+ */
9
+ export function cn(...inputs: ClassValue[]) {
10
+ return twMerge(clsx(inputs))
11
+ }
12
+
13
+ /**
14
+ * Focus ring utility for consistent focus styles
15
+ */
16
+ export const focusRing = cn(
17
+ "focus-visible:outline-none focus-visible:ring-2",
18
+ "focus-visible:ring-ring focus-visible:ring-offset-2"
19
+ )
20
+
21
+ /**
22
+ * Disabled utility for consistent disabled states
23
+ */
24
+ export const disabled = "disabled:pointer-events-none disabled:opacity-50"
@@ -0,0 +1,30 @@
1
+ import { Metadata } from 'next'
2
+ import { Suspense } from 'react'
3
+ import { LoginForm } from '@/components/auth/LoginForm'
4
+ import { LoginSkeleton } from '@/components/auth/LoginSkeleton'
5
+
6
+ export const metadata: Metadata = {
7
+ title: 'Login',
8
+ description: 'Sign in to your account',
9
+ }
10
+
11
+ export default function LoginPage() {
12
+ return (
13
+ <div className="container mx-auto flex min-h-screen items-center justify-center px-4">
14
+ <div className="w-full max-w-md space-y-8">
15
+ <div className="text-center">
16
+ <h1 className="text-3xl font-bold tracking-tight">
17
+ Welcome back
18
+ </h1>
19
+ <p className="mt-2 text-sm text-muted-foreground">
20
+ Sign in to your account to continue
21
+ </p>
22
+ </div>
23
+
24
+ <Suspense fallback={<LoginSkeleton />}>
25
+ <LoginForm />
26
+ </Suspense>
27
+ </div>
28
+ </div>
29
+ )
30
+ }
@@ -0,0 +1,38 @@
1
+ import { Metadata } from 'next'
2
+ import { Suspense } from 'react'
3
+ import Link from 'next/link'
4
+ import { RegisterForm } from '@/components/auth/RegisterForm'
5
+ import { LoginSkeleton } from '@/components/auth/LoginSkeleton'
6
+
7
+ export const metadata: Metadata = {
8
+ title: 'Register',
9
+ description: 'Create a new account',
10
+ }
11
+
12
+ export default function RegisterPage() {
13
+ return (
14
+ <div className="container mx-auto flex min-h-screen items-center justify-center px-4">
15
+ <div className="w-full max-w-md space-y-8">
16
+ <div className="text-center">
17
+ <h1 className="text-3xl font-bold tracking-tight">
18
+ Create an account
19
+ </h1>
20
+ <p className="mt-2 text-sm text-muted-foreground">
21
+ Sign up to get started
22
+ </p>
23
+ </div>
24
+
25
+ <Suspense fallback={<LoginSkeleton />}>
26
+ <RegisterForm />
27
+ </Suspense>
28
+
29
+ <p className="text-center text-sm text-muted-foreground">
30
+ Already have an account?{' '}
31
+ <Link href="/login" className="font-medium text-primary hover:underline">
32
+ Sign in
33
+ </Link>
34
+ </p>
35
+ </div>
36
+ </div>
37
+ )
38
+ }
@@ -0,0 +1,42 @@
1
+ import { Metadata } from 'next'
2
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
3
+ import { EntityForm } from '@/components/crud/entity-form'
4
+ import { create{{entityName}} } from '@/app/actions/crud'
5
+
6
+ export const metadata: Metadata = {
7
+ title: 'Create {{entityName}}',
8
+ description: 'Create a new {{entityLower}}',
9
+ }
10
+
11
+ export default function Create{{entityName}}Page() {
12
+ const fields = [
13
+ {{#each fields}}
14
+ {
15
+ name: '{{name}}',
16
+ type: '{{type}}',
17
+ label: '{{capitalize name}}',
18
+ required: {{#if required}}true{{else}}false{{/if}},
19
+ },
20
+ {{/each}}
21
+ ]
22
+
23
+ return (
24
+ <div className="container mx-auto py-10">
25
+ <div className="mb-8">
26
+ <h1 className="text-3xl font-bold tracking-tight">Create {{entityName}}</h1>
27
+ <p className="text-muted-foreground">Add a new {{entityLower}} to your collection</p>
28
+ </div>
29
+
30
+ <Card className="mx-auto max-w-2xl">
31
+ <CardHeader>
32
+ <CardTitle>{{entityName}} Information</CardTitle>
33
+ <CardDescription>Enter the details for your new {{entityLower}}</CardDescription>
34
+ </CardHeader>
35
+ <CardContent>
36
+ <EntityForm entityName="{{entityName}}" entityLower="{{entityLower}}" fields={fields} action={create{{entityName}}
37
+ } submitLabel="Create {{entityName}}" />
38
+ </CardContent>
39
+ </Card>
40
+ </div>
41
+ )
42
+ }