blacksmith-cli 0.1.6 → 0.1.7

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 (34) hide show
  1. package/dist/index.js +1745 -689
  2. package/dist/index.js.map +1 -1
  3. package/package.json +2 -1
  4. package/src/templates/frontend/package.json.hbs +4 -4
  5. package/src/templates/frontend/src/__tests__/test-utils.tsx.hbs +5 -4
  6. package/src/templates/frontend/src/app.tsx.hbs +13 -9
  7. package/src/templates/frontend/src/features/auth/adapter.ts.hbs +7 -7
  8. package/src/templates/frontend/src/features/auth/components/auth-provider.tsx.hbs +91 -11
  9. package/src/templates/frontend/src/features/auth/hooks/use-auth.ts.hbs +3 -4
  10. package/src/templates/frontend/src/features/auth/pages/forgot-password-page.tsx.hbs +76 -12
  11. package/src/templates/frontend/src/features/auth/pages/login-page.tsx.hbs +84 -11
  12. package/src/templates/frontend/src/features/auth/pages/register-page.tsx.hbs +85 -14
  13. package/src/templates/frontend/src/features/auth/pages/reset-password-page.tsx.hbs +63 -12
  14. package/src/templates/frontend/src/features/auth/types.ts.hbs +32 -0
  15. package/src/templates/frontend/src/pages/dashboard/components/quick-start-card.tsx.hbs +19 -18
  16. package/src/templates/frontend/src/pages/dashboard/components/stack-cards.tsx.hbs +33 -31
  17. package/src/templates/frontend/src/pages/dashboard/components/welcome-header.tsx.hbs +5 -5
  18. package/src/templates/frontend/src/pages/dashboard/dashboard.tsx.hbs +5 -5
  19. package/src/templates/frontend/src/pages/home/home.tsx.hbs +48 -52
  20. package/src/templates/frontend/src/router/auth-guard.tsx.hbs +10 -7
  21. package/src/templates/frontend/src/router/error-boundary.tsx.hbs +16 -12
  22. package/src/templates/frontend/src/router/layouts/auth-layout.tsx.hbs +12 -12
  23. package/src/templates/frontend/src/router/layouts/main-layout.tsx.hbs +62 -55
  24. package/src/templates/frontend/src/shared/components/loading-spinner.tsx.hbs +6 -6
  25. package/src/templates/frontend/src/shared/components/not-found-page.tsx.hbs +1 -1
  26. package/src/templates/frontend/src/shared/hooks/use-debounce.ts.hbs +18 -2
  27. package/src/templates/frontend/src/styles/globals.css.hbs +3 -1
  28. package/src/templates/frontend/tailwind.config.js.hbs +1 -1
  29. package/src/templates/resource/frontend/components/{{kebab}}-form.tsx.hbs +3 -2
  30. package/src/templates/resource/frontend/pages/{{kebabs}}-page.tsx.hbs +3 -2
  31. package/src/templates/resource/frontend/pages/{{kebab}}-detail-page.tsx.hbs +5 -3
  32. package/src/templates/resource/pages/components/{{kebab}}-form.tsx.hbs +3 -2
  33. package/src/templates/resource/pages/{{kebabs}}-page.tsx.hbs +3 -2
  34. package/src/templates/resource/pages/{{kebab}}-detail-page.tsx.hbs +5 -3
@@ -2,20 +2,20 @@ import { Outlet, Link, useNavigate } from 'react-router-dom'
2
2
  import { Path } from '@/router/paths'
3
3
  import {
4
4
  Button,
5
- Separator,
5
+ IconButton,
6
6
  Tooltip,
7
- DropdownMenuPrimitives,
8
- DropdownMenuTrigger,
9
- DropdownMenuContent,
10
- DropdownMenuItem,
11
- DropdownMenuSeparator,
12
- DropdownMenuLabel,
7
+ Menu,
8
+ MenuButton,
9
+ MenuList,
10
+ MenuItem,
11
+ MenuDivider,
12
+ MenuGroup,
13
13
  Avatar,
14
- AvatarFallback,
15
- } from '@blacksmith-ui/react'
14
+ Divider,
15
+ useColorMode,
16
+ } from '@chakra-ui/react'
16
17
  import { LogOut, User, Settings, Sun, Moon, Anvil } from 'lucide-react'
17
18
  import { useAuth } from '@/features/auth/hooks/use-auth'
18
- import { useDarkMode } from '@blacksmith-ui/react'
19
19
 
20
20
  function getInitials(user: any): string {
21
21
  if (user?.displayName) {
@@ -29,9 +29,11 @@ function getInitials(user: any): string {
29
29
 
30
30
  export function MainLayout() {
31
31
  const { user, isAuthenticated, logout } = useAuth()
32
- const { isDark, toggle } = useDarkMode()
32
+ const { colorMode, toggleColorMode } = useColorMode()
33
33
  const navigate = useNavigate()
34
34
 
35
+ const isDark = colorMode === 'dark'
36
+
35
37
  const handleLogout = () => {
36
38
  logout()
37
39
  navigate(Path.Login)
@@ -49,57 +51,62 @@ export function MainLayout() {
49
51
  <div className="flex-1" />
50
52
 
51
53
  <div className="flex items-center gap-2">
52
- <Tooltip content={isDark ? 'Light mode' : 'Dark mode'}>
53
- <Button variant="ghost" size="icon" onClick={toggle}>
54
- {isDark ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
55
- </Button>
54
+ <Tooltip label={isDark ? 'Light mode' : 'Dark mode'}>
55
+ <IconButton
56
+ aria-label="Toggle color mode"
57
+ variant="ghost"
58
+ size="sm"
59
+ onClick={toggleColorMode}
60
+ icon={isDark ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
61
+ />
56
62
  </Tooltip>
57
63
 
58
64
  {isAuthenticated ? (
59
- <DropdownMenuPrimitives.Root>
60
- <DropdownMenuTrigger asChild>
61
- <Button variant="ghost" className="relative h-8 w-8 rounded-full">
62
- <Avatar className="h-8 w-8">
63
- <AvatarFallback className="text-xs">
64
- {getInitials(user)}
65
- </AvatarFallback>
66
- </Avatar>
67
- </Button>
68
- </DropdownMenuTrigger>
69
- <DropdownMenuContent align="end" className="w-56">
70
- <DropdownMenuLabel className="font-normal">
71
- <div className="flex flex-col space-y-1">
65
+ <Menu>
66
+ <MenuButton
67
+ as={Button}
68
+ variant="ghost"
69
+ className="relative h-8 w-8 rounded-full"
70
+ p={0}
71
+ minW="auto"
72
+ >
73
+ <Avatar
74
+ size="sm"
75
+ name={user?.displayName || user?.email || 'U'}
76
+ getInitials={() => getInitials(user)}
77
+ />
78
+ </MenuButton>
79
+ <MenuList>
80
+ <MenuGroup>
81
+ <div className="px-3 py-2">
72
82
  <p className="text-sm font-medium leading-none">
73
83
  {user?.displayName || 'User'}
74
84
  </p>
75
- <p className="text-xs leading-none text-muted-foreground">
85
+ <p className="text-xs leading-none text-muted-foreground mt-1">
76
86
  {user?.email}
77
87
  </p>
78
88
  </div>
79
- </DropdownMenuLabel>
80
- <DropdownMenuSeparator />
81
- <DropdownMenuItem>
82
- <User className="mr-2 h-4 w-4" />
89
+ </MenuGroup>
90
+ <MenuDivider />
91
+ <MenuItem icon={<User className="h-4 w-4" />}>
83
92
  Profile
84
- </DropdownMenuItem>
85
- <DropdownMenuItem>
86
- <Settings className="mr-2 h-4 w-4" />
93
+ </MenuItem>
94
+ <MenuItem icon={<Settings className="h-4 w-4" />}>
87
95
  Settings
88
- </DropdownMenuItem>
89
- <DropdownMenuSeparator />
90
- <DropdownMenuItem onClick={handleLogout}>
91
- <LogOut className="mr-2 h-4 w-4" />
96
+ </MenuItem>
97
+ <MenuDivider />
98
+ <MenuItem icon={<LogOut className="h-4 w-4" />} onClick={handleLogout}>
92
99
  Sign Out
93
- </DropdownMenuItem>
94
- </DropdownMenuContent>
95
- </DropdownMenuPrimitives.Root>
100
+ </MenuItem>
101
+ </MenuList>
102
+ </Menu>
96
103
  ) : (
97
104
  <>
98
- <Button variant="ghost" size="sm" asChild>
99
- <Link to={Path.Login}>Sign In</Link>
105
+ <Button variant="ghost" size="sm" as={Link} to={Path.Login}>
106
+ Sign In
100
107
  </Button>
101
- <Button size="sm" asChild>
102
- <Link to={Path.Register}>Sign Up</Link>
108
+ <Button size="sm" colorScheme="blue" as={Link} to={Path.Register}>
109
+ Sign Up
103
110
  </Button>
104
111
  </>
105
112
  )}
@@ -118,16 +125,16 @@ export function MainLayout() {
118
125
  <span>&copy; {new Date().getFullYear()} {{projectName}}. All rights reserved.</span>
119
126
  </div>
120
127
  <div className="flex items-center gap-4">
121
- <Button variant="link" size="sm" className="text-muted-foreground h-auto p-0" asChild>
122
- <Link to="/terms">Terms</Link>
128
+ <Button variant="link" size="sm" className="text-muted-foreground h-auto p-0" as={Link} to="/terms">
129
+ Terms
123
130
  </Button>
124
- <Separator orientation="vertical" className="h-4" />
125
- <Button variant="link" size="sm" className="text-muted-foreground h-auto p-0" asChild>
126
- <Link to="/privacy">Privacy</Link>
131
+ <Divider orientation="vertical" h={4} />
132
+ <Button variant="link" size="sm" className="text-muted-foreground h-auto p-0" as={Link} to="/privacy">
133
+ Privacy
127
134
  </Button>
128
- <Separator orientation="vertical" className="h-4" />
129
- <Button variant="link" size="sm" className="text-muted-foreground h-auto p-0" asChild>
130
- <Link to="/support">Support</Link>
135
+ <Divider orientation="vertical" h={4} />
136
+ <Button variant="link" size="sm" className="text-muted-foreground h-auto p-0" as={Link} to="/support">
137
+ Support
131
138
  </Button>
132
139
  </div>
133
140
  </div>
@@ -1,4 +1,4 @@
1
- import { Spinner } from '@blacksmith-ui/react'
1
+ import { Spinner } from '@chakra-ui/react'
2
2
 
3
3
  interface LoadingSpinnerProps {
4
4
  size?: 'sm' | 'md' | 'lg'
@@ -6,15 +6,15 @@ interface LoadingSpinnerProps {
6
6
  }
7
7
 
8
8
  const sizeMap = {
9
- sm: 'h-4 w-4',
10
- md: 'h-8 w-8',
11
- lg: 'h-12 w-12',
12
- }
9
+ sm: 'sm',
10
+ md: 'md',
11
+ lg: 'lg',
12
+ } as const
13
13
 
14
14
  export function LoadingSpinner({ size = 'md', className = '' }: LoadingSpinnerProps) {
15
15
  return (
16
16
  <div className={`flex items-center justify-center ${className}`}>
17
- <Spinner className={sizeMap[size]} />
17
+ <Spinner size={sizeMap[size]} />
18
18
  </div>
19
19
  )
20
20
  }
@@ -1,5 +1,5 @@
1
1
  import { useNavigate } from 'react-router-dom'
2
- import { Button } from '@blacksmith-ui/react'
2
+ import { Button } from '@chakra-ui/react'
3
3
  import { ArrowLeft, FileQuestion } from 'lucide-react'
4
4
  import { Path } from '@/router/paths'
5
5
 
@@ -1,10 +1,26 @@
1
1
  /**
2
2
  * useDebounce Hook
3
3
  *
4
- * Re-exports useDebounce from @blacksmith-ui/hooks.
4
+ * Debounces a value by the specified delay.
5
5
  * Import from here so your app has a single import path.
6
6
  *
7
7
  * Generated by Blacksmith. You own this file — customize as needed.
8
8
  */
9
9
 
10
- export { useDebounce } from '@blacksmith-ui/hooks'
10
+ import { useState, useEffect } from 'react'
11
+
12
+ export function useDebounce<T>(value: T, delay: number = 300): T {
13
+ const [debouncedValue, setDebouncedValue] = useState<T>(value)
14
+
15
+ useEffect(() => {
16
+ const timer = setTimeout(() => {
17
+ setDebouncedValue(value)
18
+ }, delay)
19
+
20
+ return () => {
21
+ clearTimeout(timer)
22
+ }
23
+ }, [value, delay])
24
+
25
+ return debouncedValue
26
+ }
@@ -1,4 +1,6 @@
1
- @import '@blacksmith-ui/react/styles.css';
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
2
4
 
3
5
  {{#if (ne themePreset "default")}}
4
6
  @layer base {
@@ -4,7 +4,7 @@ export default {
4
4
  content: [
5
5
  './index.html',
6
6
  './src/**/*.{js,ts,jsx,tsx}',
7
- './node_modules/@blacksmith-ui/**/*.{js,ts,jsx,tsx}',
7
+ './node_modules/@chakra-ui/**/*.{js,ts,jsx,tsx}',
8
8
  ],
9
9
  theme: {
10
10
  extend: {
@@ -9,7 +9,7 @@
9
9
  import { useForm } from 'react-hook-form'
10
10
  import { z } from 'zod'
11
11
  import { zodResolver } from '@hookform/resolvers/zod'
12
- import { Alert, AlertDescription } from '@blacksmith-ui/react'
12
+ import { Alert, AlertIcon, AlertDescription } from '@chakra-ui/react'
13
13
 
14
14
  const {{name}}Schema = z.object({
15
15
  title: z.string().min(1, 'Title is required').max(255),
@@ -55,7 +55,8 @@ export function {{Name}}Form({
55
55
  return (
56
56
  <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
57
57
  {errorMessage && (
58
- <Alert variant="destructive">
58
+ <Alert status="error" borderRadius="md">
59
+ <AlertIcon />
59
60
  <AlertDescription>{errorMessage}</AlertDescription>
60
61
  </Alert>
61
62
  )}
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { use{{Names}} } from '@/api/hooks/{{kebabs}}'
9
9
  import { {{Name}}List } from '../components/{{kebab}}-list'
10
- import { Alert, AlertDescription } from '@blacksmith-ui/react'
10
+ import { Alert, AlertIcon, AlertDescription } from '@chakra-ui/react'
11
11
 
12
12
  export default function {{Names}}Page() {
13
13
  const { data, isLoading, errorMessage } = use{{Names}}()
@@ -19,7 +19,8 @@ export default function {{Names}}Page() {
19
19
  </div>
20
20
 
21
21
  {errorMessage && (
22
- <Alert variant="destructive" className="mb-4">
22
+ <Alert status="error" borderRadius="md" className="mb-4">
23
+ <AlertIcon />
23
24
  <AlertDescription>{errorMessage}</AlertDescription>
24
25
  </Alert>
25
26
  )}
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { useParams, useNavigate } from 'react-router-dom'
9
9
  import { useGet{{Name}}, useDelete{{Name}} } from '@/api/hooks/{{kebabs}}'
10
- import { Alert, AlertDescription } from '@blacksmith-ui/react'
10
+ import { Alert, AlertIcon, AlertDescription } from '@chakra-ui/react'
11
11
  import { Path } from '@/router/paths'
12
12
 
13
13
  export default function {{Name}}DetailPage() {
@@ -29,7 +29,8 @@ export default function {{Name}}DetailPage() {
29
29
 
30
30
  if (errorMessage) {
31
31
  return (
32
- <Alert variant="destructive">
32
+ <Alert status="error" borderRadius="md">
33
+ <AlertIcon />
33
34
  <AlertDescription>{errorMessage}</AlertDescription>
34
35
  </Alert>
35
36
  )
@@ -49,7 +50,8 @@ export default function {{Name}}DetailPage() {
49
50
  return (
50
51
  <div>
51
52
  {delete{{Name}}.errorMessage && (
52
- <Alert variant="destructive" className="mb-4">
53
+ <Alert status="error" borderRadius="md" className="mb-4">
54
+ <AlertIcon />
53
55
  <AlertDescription>{delete{{Name}}.errorMessage}</AlertDescription>
54
56
  </Alert>
55
57
  )}
@@ -9,7 +9,7 @@
9
9
  import { useForm } from 'react-hook-form'
10
10
  import { z } from 'zod'
11
11
  import { zodResolver } from '@hookform/resolvers/zod'
12
- import { Alert, AlertDescription } from '@blacksmith-ui/react'
12
+ import { Alert, AlertIcon, AlertDescription } from '@chakra-ui/react'
13
13
 
14
14
  const {{name}}Schema = z.object({
15
15
  title: z.string().min(1, 'Title is required').max(255),
@@ -55,7 +55,8 @@ export function {{Name}}Form({
55
55
  return (
56
56
  <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
57
57
  {errorMessage && (
58
- <Alert variant="destructive">
58
+ <Alert status="error" borderRadius="md">
59
+ <AlertIcon />
59
60
  <AlertDescription>{errorMessage}</AlertDescription>
60
61
  </Alert>
61
62
  )}
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { use{{Names}} } from '@/api/hooks/{{kebabs}}'
9
9
  import { {{Name}}List } from './components/{{kebab}}-list'
10
- import { Alert, AlertDescription } from '@blacksmith-ui/react'
10
+ import { Alert, AlertIcon, AlertDescription } from '@chakra-ui/react'
11
11
 
12
12
  export default function {{Names}}Page() {
13
13
  const { data, isLoading, errorMessage } = use{{Names}}()
@@ -19,7 +19,8 @@ export default function {{Names}}Page() {
19
19
  </div>
20
20
 
21
21
  {errorMessage && (
22
- <Alert variant="destructive" className="mb-4">
22
+ <Alert status="error" borderRadius="md" className="mb-4">
23
+ <AlertIcon />
23
24
  <AlertDescription>{errorMessage}</AlertDescription>
24
25
  </Alert>
25
26
  )}
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { useParams, useNavigate } from 'react-router-dom'
9
9
  import { useGet{{Name}}, useDelete{{Name}} } from '@/api/hooks/{{kebabs}}'
10
- import { Alert, AlertDescription } from '@blacksmith-ui/react'
10
+ import { Alert, AlertIcon, AlertDescription } from '@chakra-ui/react'
11
11
  import { Path } from '@/router/paths'
12
12
 
13
13
  export default function {{Name}}DetailPage() {
@@ -29,7 +29,8 @@ export default function {{Name}}DetailPage() {
29
29
 
30
30
  if (errorMessage) {
31
31
  return (
32
- <Alert variant="destructive">
32
+ <Alert status="error" borderRadius="md">
33
+ <AlertIcon />
33
34
  <AlertDescription>{errorMessage}</AlertDescription>
34
35
  </Alert>
35
36
  )
@@ -49,7 +50,8 @@ export default function {{Name}}DetailPage() {
49
50
  return (
50
51
  <div>
51
52
  {delete{{Name}}.errorMessage && (
52
- <Alert variant="destructive" className="mb-4">
53
+ <Alert status="error" borderRadius="md" className="mb-4">
54
+ <AlertIcon />
53
55
  <AlertDescription>{delete{{Name}}.errorMessage}</AlertDescription>
54
56
  </Alert>
55
57
  )}