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.
- package/dist/index.js +1745 -689
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/src/templates/frontend/package.json.hbs +4 -4
- package/src/templates/frontend/src/__tests__/test-utils.tsx.hbs +5 -4
- package/src/templates/frontend/src/app.tsx.hbs +13 -9
- package/src/templates/frontend/src/features/auth/adapter.ts.hbs +7 -7
- package/src/templates/frontend/src/features/auth/components/auth-provider.tsx.hbs +91 -11
- package/src/templates/frontend/src/features/auth/hooks/use-auth.ts.hbs +3 -4
- package/src/templates/frontend/src/features/auth/pages/forgot-password-page.tsx.hbs +76 -12
- package/src/templates/frontend/src/features/auth/pages/login-page.tsx.hbs +84 -11
- package/src/templates/frontend/src/features/auth/pages/register-page.tsx.hbs +85 -14
- package/src/templates/frontend/src/features/auth/pages/reset-password-page.tsx.hbs +63 -12
- package/src/templates/frontend/src/features/auth/types.ts.hbs +32 -0
- package/src/templates/frontend/src/pages/dashboard/components/quick-start-card.tsx.hbs +19 -18
- package/src/templates/frontend/src/pages/dashboard/components/stack-cards.tsx.hbs +33 -31
- package/src/templates/frontend/src/pages/dashboard/components/welcome-header.tsx.hbs +5 -5
- package/src/templates/frontend/src/pages/dashboard/dashboard.tsx.hbs +5 -5
- package/src/templates/frontend/src/pages/home/home.tsx.hbs +48 -52
- package/src/templates/frontend/src/router/auth-guard.tsx.hbs +10 -7
- package/src/templates/frontend/src/router/error-boundary.tsx.hbs +16 -12
- package/src/templates/frontend/src/router/layouts/auth-layout.tsx.hbs +12 -12
- package/src/templates/frontend/src/router/layouts/main-layout.tsx.hbs +62 -55
- package/src/templates/frontend/src/shared/components/loading-spinner.tsx.hbs +6 -6
- package/src/templates/frontend/src/shared/components/not-found-page.tsx.hbs +1 -1
- package/src/templates/frontend/src/shared/hooks/use-debounce.ts.hbs +18 -2
- package/src/templates/frontend/src/styles/globals.css.hbs +3 -1
- package/src/templates/frontend/tailwind.config.js.hbs +1 -1
- package/src/templates/resource/frontend/components/{{kebab}}-form.tsx.hbs +3 -2
- package/src/templates/resource/frontend/pages/{{kebabs}}-page.tsx.hbs +3 -2
- package/src/templates/resource/frontend/pages/{{kebab}}-detail-page.tsx.hbs +5 -3
- package/src/templates/resource/pages/components/{{kebab}}-form.tsx.hbs +3 -2
- package/src/templates/resource/pages/{{kebabs}}-page.tsx.hbs +3 -2
- 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
|
-
|
|
5
|
+
IconButton,
|
|
6
6
|
Tooltip,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
Menu,
|
|
8
|
+
MenuButton,
|
|
9
|
+
MenuList,
|
|
10
|
+
MenuItem,
|
|
11
|
+
MenuDivider,
|
|
12
|
+
MenuGroup,
|
|
13
13
|
Avatar,
|
|
14
|
-
|
|
15
|
-
|
|
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 {
|
|
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
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
<
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
</
|
|
80
|
-
<
|
|
81
|
-
<
|
|
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
|
-
</
|
|
85
|
-
<
|
|
86
|
-
<Settings className="mr-2 h-4 w-4" />
|
|
93
|
+
</MenuItem>
|
|
94
|
+
<MenuItem icon={<Settings className="h-4 w-4" />}>
|
|
87
95
|
Settings
|
|
88
|
-
</
|
|
89
|
-
<
|
|
90
|
-
<
|
|
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
|
-
</
|
|
94
|
-
</
|
|
95
|
-
</
|
|
100
|
+
</MenuItem>
|
|
101
|
+
</MenuList>
|
|
102
|
+
</Menu>
|
|
96
103
|
) : (
|
|
97
104
|
<>
|
|
98
|
-
<Button variant="ghost" size="sm"
|
|
99
|
-
|
|
105
|
+
<Button variant="ghost" size="sm" as={Link} to={Path.Login}>
|
|
106
|
+
Sign In
|
|
100
107
|
</Button>
|
|
101
|
-
<Button size="sm"
|
|
102
|
-
|
|
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>© {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"
|
|
122
|
-
|
|
128
|
+
<Button variant="link" size="sm" className="text-muted-foreground h-auto p-0" as={Link} to="/terms">
|
|
129
|
+
Terms
|
|
123
130
|
</Button>
|
|
124
|
-
<
|
|
125
|
-
<Button variant="link" size="sm" className="text-muted-foreground h-auto p-0"
|
|
126
|
-
|
|
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
|
-
<
|
|
129
|
-
<Button variant="link" size="sm" className="text-muted-foreground h-auto p-0"
|
|
130
|
-
|
|
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 '@
|
|
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: '
|
|
10
|
-
md: '
|
|
11
|
-
lg: '
|
|
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
|
|
17
|
+
<Spinner size={sizeMap[size]} />
|
|
18
18
|
</div>
|
|
19
19
|
)
|
|
20
20
|
}
|
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* useDebounce Hook
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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
|
-
|
|
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
|
+
}
|
|
@@ -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 '@
|
|
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
|
|
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 '@
|
|
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
|
|
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 '@
|
|
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
|
|
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
|
|
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 '@
|
|
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
|
|
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 '@
|
|
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
|
|
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 '@
|
|
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
|
|
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
|
|
53
|
+
<Alert status="error" borderRadius="md" className="mb-4">
|
|
54
|
+
<AlertIcon />
|
|
53
55
|
<AlertDescription>{delete{{Name}}.errorMessage}</AlertDescription>
|
|
54
56
|
</Alert>
|
|
55
57
|
)}
|