blacksmith-cli 0.1.3 → 0.1.5
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 +22 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/templates/backend/utils/__init__.py.hbs +0 -0
- package/src/templates/backend/utils/models.py.hbs +11 -0
- package/src/templates/frontend/src/pages/home/home.tsx.hbs +93 -11
- package/src/templates/resource/api-hooks/index.ts.hbs +2 -0
- package/src/templates/resource/{frontend/hooks → api-hooks}/use-{{kebabs}}-query.ts.hbs +10 -2
- package/src/templates/resource/{frontend/hooks → api-hooks}/use-{{kebab}}-mutations.ts.hbs +1 -1
- package/src/templates/resource/backend/models.py.hbs +2 -3
- package/src/templates/resource/frontend/components/{{kebab}}-card.tsx.hbs +1 -1
- package/src/templates/resource/frontend/components/{{kebab}}-list.tsx.hbs +1 -1
- package/src/templates/resource/frontend/index.ts.hbs +1 -2
- package/src/templates/resource/frontend/pages/{{kebabs}}-page.tsx.hbs +1 -1
- package/src/templates/resource/frontend/pages/{{kebab}}-detail-page.tsx.hbs +3 -11
- package/src/templates/resource/pages/components/{{kebab}}-card.tsx.hbs +1 -1
- package/src/templates/resource/pages/components/{{kebab}}-list.tsx.hbs +1 -1
- package/src/templates/resource/pages/hooks/index.ts.hbs +9 -0
- package/src/templates/resource/pages/index.ts.hbs +1 -2
- package/src/templates/resource/pages/{{kebabs}}-page.tsx.hbs +1 -1
- package/src/templates/resource/pages/{{kebab}}-detail-page.tsx.hbs +3 -11
- package/src/templates/frontend/src/pages/home/components/features-grid.tsx.hbs +0 -88
- package/src/templates/frontend/src/pages/home/components/getting-started.tsx.hbs +0 -88
- package/src/templates/frontend/src/pages/home/components/hero-section.tsx.hbs +0 -47
- package/src/templates/frontend/src/pages/home/components/resources-section.tsx.hbs +0 -34
- package/src/templates/resource/pages/hooks/use-{{kebabs}}-query.ts.hbs +0 -35
- package/src/templates/resource/pages/hooks/use-{{kebab}}-mutations.ts.hbs +0 -39
package/package.json
CHANGED
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from django.db import models
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class BaseModel(models.Model):
|
|
6
|
+
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
7
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
8
|
+
updated_at = models.DateTimeField(auto_now=True)
|
|
9
|
+
|
|
10
|
+
class Meta:
|
|
11
|
+
abstract = True
|
|
@@ -1,20 +1,102 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Stack,
|
|
3
|
+
Flex,
|
|
4
|
+
Typography,
|
|
5
|
+
Text,
|
|
6
|
+
Button,
|
|
7
|
+
Card,
|
|
8
|
+
CardHeader,
|
|
9
|
+
CardTitle,
|
|
10
|
+
CardDescription,
|
|
11
|
+
CardContent,
|
|
12
|
+
Grid,
|
|
13
|
+
} from '@blacksmith-ui/react'
|
|
2
14
|
import { useAuth } from '@/features/auth/hooks/use-auth'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
15
|
+
import { Link } from 'react-router-dom'
|
|
16
|
+
import { Path } from '@/router/paths'
|
|
17
|
+
import { BookOpen, Terminal, Layout } from 'lucide-react'
|
|
18
|
+
|
|
19
|
+
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'
|
|
7
20
|
|
|
8
21
|
export default function HomePage() {
|
|
9
22
|
const { isAuthenticated } = useAuth()
|
|
10
23
|
|
|
11
24
|
return (
|
|
12
|
-
<Stack gap={12
|
|
13
|
-
<
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
25
|
+
<Stack gap={10} className="pt-16 pb-12">
|
|
26
|
+
<Stack gap={4} align="center">
|
|
27
|
+
<Typography variant="h1" className="sm:text-5xl">
|
|
28
|
+
{{projectName}}
|
|
29
|
+
</Typography>
|
|
30
|
+
<Text size="lg" color="muted" align="center" className="max-w-md">
|
|
31
|
+
Your app is ready. Sign in to get started or explore the API.
|
|
32
|
+
</Text>
|
|
33
|
+
<Flex justify="center" gap="md" className="pt-2">
|
|
34
|
+
{isAuthenticated ? (
|
|
35
|
+
<Button size="lg" asChild>
|
|
36
|
+
<Link to={Path.Dashboard}>Dashboard</Link>
|
|
37
|
+
</Button>
|
|
38
|
+
) : (
|
|
39
|
+
<Button size="lg" asChild>
|
|
40
|
+
<Link to={Path.Login}>Sign In</Link>
|
|
41
|
+
</Button>
|
|
42
|
+
)}
|
|
43
|
+
<Button variant="outline" size="lg" asChild>
|
|
44
|
+
<a href={`${API_URL}/api/docs/`} target="_blank" rel="noopener noreferrer">
|
|
45
|
+
API Docs
|
|
46
|
+
</a>
|
|
47
|
+
</Button>
|
|
48
|
+
</Flex>
|
|
49
|
+
</Stack>
|
|
50
|
+
|
|
51
|
+
<Grid columns=\{{ base: 1, md: 3 }} gap={4} className="max-w-3xl mx-auto w-full">
|
|
52
|
+
<Card>
|
|
53
|
+
<CardHeader className="pb-2">
|
|
54
|
+
<Flex align="center" gap={2}>
|
|
55
|
+
<Terminal className="h-4 w-4 text-primary" />
|
|
56
|
+
<CardTitle className="text-sm">CLI</CardTitle>
|
|
57
|
+
</Flex>
|
|
58
|
+
</CardHeader>
|
|
59
|
+
<CardContent>
|
|
60
|
+
<CardDescription>
|
|
61
|
+
Run <Text as="code" size="sm" className="font-mono">blacksmith dev</Text> to start the full stack.
|
|
62
|
+
</CardDescription>
|
|
63
|
+
</CardContent>
|
|
64
|
+
</Card>
|
|
65
|
+
|
|
66
|
+
<Card>
|
|
67
|
+
<CardHeader className="pb-2">
|
|
68
|
+
<Flex align="center" gap={2}>
|
|
69
|
+
<BookOpen className="h-4 w-4 text-primary" />
|
|
70
|
+
<CardTitle className="text-sm">API</CardTitle>
|
|
71
|
+
</Flex>
|
|
72
|
+
</CardHeader>
|
|
73
|
+
<CardContent>
|
|
74
|
+
<CardDescription>
|
|
75
|
+
<a href={`${API_URL}/api/docs/`} target="_blank" rel="noopener noreferrer" className="underline underline-offset-4 hover:text-foreground">
|
|
76
|
+
Swagger UI
|
|
77
|
+
</a>
|
|
78
|
+
{' / '}
|
|
79
|
+
<a href={`${API_URL}/admin/`} target="_blank" rel="noopener noreferrer" className="underline underline-offset-4 hover:text-foreground">
|
|
80
|
+
Django Admin
|
|
81
|
+
</a>
|
|
82
|
+
</CardDescription>
|
|
83
|
+
</CardContent>
|
|
84
|
+
</Card>
|
|
85
|
+
|
|
86
|
+
<Card>
|
|
87
|
+
<CardHeader className="pb-2">
|
|
88
|
+
<Flex align="center" gap={2}>
|
|
89
|
+
<Layout className="h-4 w-4 text-primary" />
|
|
90
|
+
<CardTitle className="text-sm">Frontend</CardTitle>
|
|
91
|
+
</Flex>
|
|
92
|
+
</CardHeader>
|
|
93
|
+
<CardContent>
|
|
94
|
+
<CardDescription>
|
|
95
|
+
React, TanStack Query, and Tailwind CSS with typed API hooks.
|
|
96
|
+
</CardDescription>
|
|
97
|
+
</CardContent>
|
|
98
|
+
</Card>
|
|
99
|
+
</Grid>
|
|
18
100
|
</Stack>
|
|
19
101
|
)
|
|
20
102
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* {{Name}} Query Hooks
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* List and detail queries with smart retry and error parsing.
|
|
5
5
|
* Generated by Blacksmith. You own this file — customize as needed.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { useApiQuery } from '@/shared/hooks/use-api-query'
|
|
9
9
|
import {
|
|
10
10
|
{{snakes}}ListOptions,
|
|
11
|
+
{{snakes}}RetrieveOptions,
|
|
11
12
|
} from '@/api/generated/@tanstack/react-query.gen'
|
|
12
13
|
|
|
13
14
|
interface Use{{Names}}Params {
|
|
@@ -33,3 +34,10 @@ export function use{{Names}}(params: Use{{Names}}Params = {}) {
|
|
|
33
34
|
}),
|
|
34
35
|
})
|
|
35
36
|
}
|
|
37
|
+
|
|
38
|
+
export function useGet{{Name}}(id: string) {
|
|
39
|
+
return useApiQuery({
|
|
40
|
+
...{{snakes}}RetrieveOptions({ path: { id } }),
|
|
41
|
+
enabled: !!id,
|
|
42
|
+
})
|
|
43
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
from django.db import models
|
|
2
2
|
from django.conf import settings
|
|
3
|
+
from utils.models import BaseModel
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
class {{Name}}(
|
|
6
|
+
class {{Name}}(BaseModel):
|
|
6
7
|
"""{{Name}} model."""
|
|
7
8
|
|
|
8
9
|
title = models.CharField(max_length=255)
|
|
@@ -12,8 +13,6 @@ class {{Name}}(models.Model):
|
|
|
12
13
|
on_delete=models.CASCADE,
|
|
13
14
|
related_name='{{snakes}}',
|
|
14
15
|
)
|
|
15
|
-
created_at = models.DateTimeField(auto_now_add=True)
|
|
16
|
-
updated_at = models.DateTimeField(auto_now=True)
|
|
17
16
|
|
|
18
17
|
class Meta:
|
|
19
18
|
ordering = ['-created_at']
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { {{names}}Routes } from './routes'
|
|
2
|
-
export { use{{Names}} } from '
|
|
3
|
-
export { useCreate{{Name}}, useUpdate{{Name}}, useDelete{{Name}} } from './hooks/use-{{kebab}}-mutations'
|
|
2
|
+
export { use{{Names}}, useCreate{{Name}}, useUpdate{{Name}}, useDelete{{Name}} } from '@/api/hooks/{{kebabs}}'
|
|
4
3
|
export { {{Name}}Card } from './components/{{kebab}}-card'
|
|
5
4
|
export { {{Name}}List } from './components/{{kebab}}-list'
|
|
6
5
|
export { {{Name}}Form } from './components/{{kebab}}-form'
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Generated by Blacksmith. You own this file — customize as needed.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { use{{Names}} } from '
|
|
8
|
+
import { use{{Names}} } from '@/api/hooks/{{kebabs}}'
|
|
9
9
|
import { {{Name}}List } from '../components/{{kebab}}-list'
|
|
10
10
|
import { Alert, AlertDescription } from '@blacksmith-ui/react'
|
|
11
11
|
|
|
@@ -6,24 +6,16 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { useParams, useNavigate } from 'react-router-dom'
|
|
9
|
-
import {
|
|
10
|
-
import { useDelete{{Name}} } from '../hooks/use-{{kebab}}-mutations'
|
|
9
|
+
import { useGet{{Name}}, useDelete{{Name}} } from '@/api/hooks/{{kebabs}}'
|
|
11
10
|
import { Alert, AlertDescription } from '@blacksmith-ui/react'
|
|
12
11
|
import { Path } from '@/router/paths'
|
|
13
|
-
import {
|
|
14
|
-
{{snakes}}RetrieveOptions,
|
|
15
|
-
} from '@/api/generated/@tanstack/react-query.gen'
|
|
16
12
|
|
|
17
13
|
export default function {{Name}}DetailPage() {
|
|
18
14
|
const { id } = useParams<{ id: string }>()
|
|
19
15
|
const navigate = useNavigate()
|
|
20
16
|
const delete{{Name}} = useDelete{{Name}}()
|
|
21
17
|
|
|
22
|
-
const { data: {{name}}, isLoading, errorMessage } =
|
|
23
|
-
...{{snakes}}RetrieveOptions({
|
|
24
|
-
path: { id: Number(id) },
|
|
25
|
-
}),
|
|
26
|
-
})
|
|
18
|
+
const { data: {{name}}, isLoading, errorMessage } = useGet{{Name}}(id!)
|
|
27
19
|
|
|
28
20
|
if (isLoading) {
|
|
29
21
|
return (
|
|
@@ -49,7 +41,7 @@ export default function {{Name}}DetailPage() {
|
|
|
49
41
|
|
|
50
42
|
const handleDelete = async () => {
|
|
51
43
|
if (window.confirm('Are you sure you want to delete this {{name}}?')) {
|
|
52
|
-
await delete{{Name}}.mutateAsync({ path: { id:
|
|
44
|
+
await delete{{Name}}.mutateAsync({ path: { id: id! } })
|
|
53
45
|
navigate(Path.{{Names}})
|
|
54
46
|
}
|
|
55
47
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page-local hooks for {{Names}}
|
|
3
|
+
*
|
|
4
|
+
* This folder is for hooks that are specific to the {{Names}} page UI logic,
|
|
5
|
+
* such as form state, filtering, pagination, modals, and other page-level concerns.
|
|
6
|
+
*
|
|
7
|
+
* API hooks (queries and mutations) live in @/api/hooks/{{kebabs}}/ and should
|
|
8
|
+
* be imported from there.
|
|
9
|
+
*/
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export { {{names}}Routes } from './routes'
|
|
2
|
-
export { use{{Names}} } from '
|
|
3
|
-
export { useCreate{{Name}}, useUpdate{{Name}}, useDelete{{Name}} } from './hooks/use-{{kebab}}-mutations'
|
|
2
|
+
export { use{{Names}}, useCreate{{Name}}, useUpdate{{Name}}, useDelete{{Name}} } from '@/api/hooks/{{kebabs}}'
|
|
4
3
|
export { {{Name}}Card } from './components/{{kebab}}-card'
|
|
5
4
|
export { {{Name}}List } from './components/{{kebab}}-list'
|
|
6
5
|
export { {{Name}}Form } from './components/{{kebab}}-form'
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* Generated by Blacksmith. You own this file — customize as needed.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { use{{Names}} } from '
|
|
8
|
+
import { use{{Names}} } from '@/api/hooks/{{kebabs}}'
|
|
9
9
|
import { {{Name}}List } from './components/{{kebab}}-list'
|
|
10
10
|
import { Alert, AlertDescription } from '@blacksmith-ui/react'
|
|
11
11
|
|
|
@@ -6,24 +6,16 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { useParams, useNavigate } from 'react-router-dom'
|
|
9
|
-
import {
|
|
10
|
-
import { useDelete{{Name}} } from './hooks/use-{{kebab}}-mutations'
|
|
9
|
+
import { useGet{{Name}}, useDelete{{Name}} } from '@/api/hooks/{{kebabs}}'
|
|
11
10
|
import { Alert, AlertDescription } from '@blacksmith-ui/react'
|
|
12
11
|
import { Path } from '@/router/paths'
|
|
13
|
-
import {
|
|
14
|
-
{{snakes}}RetrieveOptions,
|
|
15
|
-
} from '@/api/generated/@tanstack/react-query.gen'
|
|
16
12
|
|
|
17
13
|
export default function {{Name}}DetailPage() {
|
|
18
14
|
const { id } = useParams<{ id: string }>()
|
|
19
15
|
const navigate = useNavigate()
|
|
20
16
|
const delete{{Name}} = useDelete{{Name}}()
|
|
21
17
|
|
|
22
|
-
const { data: {{name}}, isLoading, errorMessage } =
|
|
23
|
-
...{{snakes}}RetrieveOptions({
|
|
24
|
-
path: { id: Number(id) },
|
|
25
|
-
}),
|
|
26
|
-
})
|
|
18
|
+
const { data: {{name}}, isLoading, errorMessage } = useGet{{Name}}(id!)
|
|
27
19
|
|
|
28
20
|
if (isLoading) {
|
|
29
21
|
return (
|
|
@@ -49,7 +41,7 @@ export default function {{Name}}DetailPage() {
|
|
|
49
41
|
|
|
50
42
|
const handleDelete = async () => {
|
|
51
43
|
if (window.confirm('Are you sure you want to delete this {{name}}?')) {
|
|
52
|
-
await delete{{Name}}.mutateAsync({ path: { id:
|
|
44
|
+
await delete{{Name}}.mutateAsync({ path: { id: id! } })
|
|
53
45
|
navigate(Path.{{Names}})
|
|
54
46
|
}
|
|
55
47
|
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Card,
|
|
3
|
-
CardHeader,
|
|
4
|
-
CardTitle,
|
|
5
|
-
CardDescription,
|
|
6
|
-
CardContent,
|
|
7
|
-
Badge,
|
|
8
|
-
Stack,
|
|
9
|
-
Flex,
|
|
10
|
-
Grid,
|
|
11
|
-
Typography,
|
|
12
|
-
Text,
|
|
13
|
-
} from '@blacksmith-ui/react'
|
|
14
|
-
import { Database, Layout, Shield, Code2, Zap, Rocket } from 'lucide-react'
|
|
15
|
-
|
|
16
|
-
const features = [
|
|
17
|
-
{
|
|
18
|
-
icon: Database,
|
|
19
|
-
title: 'Django REST API',
|
|
20
|
-
description: 'Production-ready backend with JWT auth, serializers, and OpenAPI docs.',
|
|
21
|
-
badge: 'Backend',
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
icon: Layout,
|
|
25
|
-
title: 'React + TypeScript',
|
|
26
|
-
description: 'Vite-powered frontend with TanStack Query, React Router, and Tailwind CSS.',
|
|
27
|
-
badge: 'Frontend',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
icon: Shield,
|
|
31
|
-
title: 'Authentication',
|
|
32
|
-
description: 'Complete auth flow with login, register, password reset, and role guards.',
|
|
33
|
-
badge: 'Full Stack',
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
icon: Code2,
|
|
37
|
-
title: 'Type-Safe API',
|
|
38
|
-
description: 'Auto-generated TypeScript client from your OpenAPI schema via blacksmith sync.',
|
|
39
|
-
badge: 'DX',
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
icon: Zap,
|
|
43
|
-
title: 'Blacksmith UI',
|
|
44
|
-
description: '80+ accessible components, form library, hooks, and dark mode theming.',
|
|
45
|
-
badge: 'UI',
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
icon: Rocket,
|
|
49
|
-
title: 'Developer Experience',
|
|
50
|
-
description: 'Hot reload, AI-ready CLAUDE.md, and one command to run the full stack.',
|
|
51
|
-
badge: 'DX',
|
|
52
|
-
},
|
|
53
|
-
]
|
|
54
|
-
|
|
55
|
-
export function FeaturesGrid() {
|
|
56
|
-
return (
|
|
57
|
-
<Stack as="section" gap={6}>
|
|
58
|
-
<Stack gap={2} align="center">
|
|
59
|
-
<Typography variant="h2">What's Included</Typography>
|
|
60
|
-
<Text color="muted">
|
|
61
|
-
A batteries-included stack so you can focus on your product.
|
|
62
|
-
</Text>
|
|
63
|
-
</Stack>
|
|
64
|
-
<Grid columns=\{{ base: 1, sm: 2, lg: 3 }} gap={4}>
|
|
65
|
-
{features.map((feature) => (
|
|
66
|
-
<Card key={feature.title} className="group transition-colors hover:border-primary/50">
|
|
67
|
-
<CardHeader className="pb-3">
|
|
68
|
-
<Flex align="start" justify="between">
|
|
69
|
-
<Flex align="center" justify="center" className="h-10 w-10 rounded-lg bg-primary/10 transition-colors group-hover:bg-primary/20">
|
|
70
|
-
<feature.icon className="h-5 w-5 text-primary" />
|
|
71
|
-
</Flex>
|
|
72
|
-
<Badge variant="outline" className="text-xs">
|
|
73
|
-
{feature.badge}
|
|
74
|
-
</Badge>
|
|
75
|
-
</Flex>
|
|
76
|
-
</CardHeader>
|
|
77
|
-
<CardContent>
|
|
78
|
-
<Stack gap={1.5}>
|
|
79
|
-
<CardTitle className="text-base">{feature.title}</CardTitle>
|
|
80
|
-
<CardDescription>{feature.description}</CardDescription>
|
|
81
|
-
</Stack>
|
|
82
|
-
</CardContent>
|
|
83
|
-
</Card>
|
|
84
|
-
))}
|
|
85
|
-
</Grid>
|
|
86
|
-
</Stack>
|
|
87
|
-
)
|
|
88
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Card,
|
|
3
|
-
CardHeader,
|
|
4
|
-
CardTitle,
|
|
5
|
-
CardDescription,
|
|
6
|
-
CardContent,
|
|
7
|
-
Button,
|
|
8
|
-
Tooltip,
|
|
9
|
-
Stack,
|
|
10
|
-
Flex,
|
|
11
|
-
Grid,
|
|
12
|
-
Typography,
|
|
13
|
-
Text,
|
|
14
|
-
} from '@blacksmith-ui/react'
|
|
15
|
-
import { Terminal, Copy, Check } from 'lucide-react'
|
|
16
|
-
import { useCopyToClipboard } from '@blacksmith-ui/hooks'
|
|
17
|
-
|
|
18
|
-
const steps = [
|
|
19
|
-
{
|
|
20
|
-
command: 'blacksmith dev',
|
|
21
|
-
title: 'Start the dev server',
|
|
22
|
-
description: 'Runs Django, Vite, and OpenAPI watcher concurrently.',
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
command: 'blacksmith make:resource Post',
|
|
26
|
-
title: 'Scaffold a resource',
|
|
27
|
-
description: 'Generates model, serializer, viewset, hooks, and pages.',
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
command: 'blacksmith sync',
|
|
31
|
-
title: 'Sync API types',
|
|
32
|
-
description: 'Regenerates the TypeScript client from your Django schema.',
|
|
33
|
-
},
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
function CommandBlock({ command }: { command: string }) {
|
|
37
|
-
const { status, copy } = useCopyToClipboard(2000)
|
|
38
|
-
const copied = status === 'copied'
|
|
39
|
-
|
|
40
|
-
return (
|
|
41
|
-
<Flex align="center" gap={2} className="rounded-lg bg-muted px-3 py-2">
|
|
42
|
-
<Terminal className="h-3.5 w-3.5 text-muted-foreground shrink-0" />
|
|
43
|
-
<Text as="code" size="sm" className="flex-1 font-mono">{command}</Text>
|
|
44
|
-
<Tooltip content={copied ? 'Copied!' : 'Copy command'}>
|
|
45
|
-
<Button variant="ghost" size="icon" className="h-7 w-7" onClick={() => copy(command)}>
|
|
46
|
-
{copied ? (
|
|
47
|
-
<Check className="h-3.5 w-3.5 text-green-500" />
|
|
48
|
-
) : (
|
|
49
|
-
<Copy className="h-3.5 w-3.5" />
|
|
50
|
-
)}
|
|
51
|
-
</Button>
|
|
52
|
-
</Tooltip>
|
|
53
|
-
</Flex>
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function GettingStarted() {
|
|
58
|
-
return (
|
|
59
|
-
<Stack as="section" gap={6}>
|
|
60
|
-
<Stack gap={2} align="center">
|
|
61
|
-
<Typography variant="h2">Get Started in 3 Steps</Typography>
|
|
62
|
-
<Text color="muted">
|
|
63
|
-
Everything you need to go from scaffold to production.
|
|
64
|
-
</Text>
|
|
65
|
-
</Stack>
|
|
66
|
-
<Grid columns=\{{ base: 1, md: 3 }} gap={4}>
|
|
67
|
-
{steps.map((step, index) => (
|
|
68
|
-
<Card key={step.command}>
|
|
69
|
-
<CardHeader className="pb-3">
|
|
70
|
-
<Flex align="center" gap={3}>
|
|
71
|
-
<Flex align="center" justify="center" className="h-8 w-8 shrink-0 rounded-full bg-primary text-primary-foreground">
|
|
72
|
-
<Text size="sm" weight="semibold">{index + 1}</Text>
|
|
73
|
-
</Flex>
|
|
74
|
-
<CardTitle className="text-base">{step.title}</CardTitle>
|
|
75
|
-
</Flex>
|
|
76
|
-
</CardHeader>
|
|
77
|
-
<CardContent>
|
|
78
|
-
<Stack gap={3}>
|
|
79
|
-
<CardDescription>{step.description}</CardDescription>
|
|
80
|
-
<CommandBlock command={step.command} />
|
|
81
|
-
</Stack>
|
|
82
|
-
</CardContent>
|
|
83
|
-
</Card>
|
|
84
|
-
))}
|
|
85
|
-
</Grid>
|
|
86
|
-
</Stack>
|
|
87
|
-
)
|
|
88
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { Stack, Flex, Typography, Text, Button } from '@blacksmith-ui/react'
|
|
2
|
-
import { Rocket, BookOpen } from 'lucide-react'
|
|
3
|
-
import { Link } from 'react-router-dom'
|
|
4
|
-
import { Path } from '@/router/paths'
|
|
5
|
-
|
|
6
|
-
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'
|
|
7
|
-
|
|
8
|
-
interface HeroSectionProps {
|
|
9
|
-
isAuthenticated: boolean
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function HeroSection({ isAuthenticated }: HeroSectionProps) {
|
|
13
|
-
return (
|
|
14
|
-
<Stack as="section" gap={6} align="center" className="pt-10 pb-2">
|
|
15
|
-
<Typography variant="h1" className="sm:text-5xl">
|
|
16
|
-
Welcome to{' '}
|
|
17
|
-
<Text as="span" color="primary">{{projectName}}</Text>
|
|
18
|
-
</Typography>
|
|
19
|
-
<Text size="lg" color="muted" align="center" className="max-w-2xl">
|
|
20
|
-
Your fullstack Django + React application is ready to go.
|
|
21
|
-
</Text>
|
|
22
|
-
<Flex justify="center" gap={3} className="pt-2">
|
|
23
|
-
{isAuthenticated ? (
|
|
24
|
-
<Button size="lg" asChild>
|
|
25
|
-
<Link to={Path.Dashboard}>
|
|
26
|
-
<Rocket className="mr-2 h-4 w-4" />
|
|
27
|
-
Dashboard
|
|
28
|
-
</Link>
|
|
29
|
-
</Button>
|
|
30
|
-
) : (
|
|
31
|
-
<Button size="lg" asChild>
|
|
32
|
-
<Link to={Path.Login}>
|
|
33
|
-
<Rocket className="mr-2 h-4 w-4" />
|
|
34
|
-
Sign In
|
|
35
|
-
</Link>
|
|
36
|
-
</Button>
|
|
37
|
-
)}
|
|
38
|
-
<Button variant="outline" size="lg" asChild>
|
|
39
|
-
<a href={`${API_URL}/api/docs/`} target="_blank" rel="noopener noreferrer">
|
|
40
|
-
<BookOpen className="mr-2 h-4 w-4" />
|
|
41
|
-
API Docs
|
|
42
|
-
</a>
|
|
43
|
-
</Button>
|
|
44
|
-
</Flex>
|
|
45
|
-
</Stack>
|
|
46
|
-
)
|
|
47
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { Stack, Flex, Typography, Text, Button } from '@blacksmith-ui/react'
|
|
2
|
-
import { ExternalLink } from 'lucide-react'
|
|
3
|
-
|
|
4
|
-
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000'
|
|
5
|
-
|
|
6
|
-
const links = [
|
|
7
|
-
{ label: 'Swagger UI', href: `${API_URL}/api/docs/` },
|
|
8
|
-
{ label: 'ReDoc', href: `${API_URL}/api/redoc/` },
|
|
9
|
-
{ label: 'OpenAPI Schema', href: `${API_URL}/api/schema/` },
|
|
10
|
-
{ label: 'Django Admin', href: `${API_URL}/admin/` },
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
export function ResourcesSection() {
|
|
14
|
-
return (
|
|
15
|
-
<Stack as="section" gap={4}>
|
|
16
|
-
<Stack gap={2} align="center">
|
|
17
|
-
<Typography variant="h2">Resources</Typography>
|
|
18
|
-
<Text color="muted">
|
|
19
|
-
Documentation and tools to help you build.
|
|
20
|
-
</Text>
|
|
21
|
-
</Stack>
|
|
22
|
-
<Flex justify="center" wrap="wrap" gap={3}>
|
|
23
|
-
{links.map((link) => (
|
|
24
|
-
<Button key={link.label} variant="outline" size="sm" asChild>
|
|
25
|
-
<a href={link.href} target="_blank" rel="noopener noreferrer">
|
|
26
|
-
<ExternalLink className="mr-2 h-3.5 w-3.5" />
|
|
27
|
-
{link.label}
|
|
28
|
-
</a>
|
|
29
|
-
</Button>
|
|
30
|
-
))}
|
|
31
|
-
</Flex>
|
|
32
|
-
</Stack>
|
|
33
|
-
)
|
|
34
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* use{{Names}} Hook
|
|
3
|
-
*
|
|
4
|
-
* Wraps the generated list query with smart retry and error parsing.
|
|
5
|
-
* Generated by Blacksmith. You own this file — customize as needed.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { useApiQuery } from '@/shared/hooks/use-api-query'
|
|
9
|
-
import {
|
|
10
|
-
{{snakes}}ListOptions,
|
|
11
|
-
} from '@/api/generated/@tanstack/react-query.gen'
|
|
12
|
-
|
|
13
|
-
interface Use{{Names}}Params {
|
|
14
|
-
page?: number
|
|
15
|
-
search?: string
|
|
16
|
-
ordering?: string
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function use{{Names}}(params: Use{{Names}}Params = {}) {
|
|
20
|
-
return useApiQuery({
|
|
21
|
-
...{{snakes}}ListOptions({
|
|
22
|
-
query: {
|
|
23
|
-
page: params.page ?? 1,
|
|
24
|
-
search: params.search,
|
|
25
|
-
ordering: params.ordering ?? '-created_at',
|
|
26
|
-
},
|
|
27
|
-
}),
|
|
28
|
-
select: (data: any) => ({
|
|
29
|
-
{{names}}: data.results ?? [],
|
|
30
|
-
total: data.count ?? 0,
|
|
31
|
-
hasNext: !!data.next,
|
|
32
|
-
hasPrev: !!data.previous,
|
|
33
|
-
}),
|
|
34
|
-
})
|
|
35
|
-
}
|