create-react-forge 1.6.0 → 1.6.1
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/README.md +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +3 -2
- package/dist/cli/parser.js.map +1 -1
- package/dist/config/defaults.d.ts +1 -1
- package/dist/config/defaults.js +2 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/schema.d.ts +11 -9
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +4 -3
- package/dist/config/schema.js.map +1 -1
- package/dist/templates/registry.d.ts +6 -2
- package/dist/templates/registry.d.ts.map +1 -1
- package/dist/templates/registry.js +31 -16
- package/dist/templates/registry.js.map +1 -1
- package/dist/templates/utils.js +4 -4
- package/dist/templates/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/templates/overlays/base/manifest.json +4 -3
- package/src/templates/overlays/base/src/components/ui/Button.tsx +103 -31
- package/src/templates/overlays/base/src/components/ui/Input.tsx +55 -29
- package/src/templates/overlays/base/src/lib/utils.ts +0 -10
- package/src/templates/overlays/runtime/nextjs/src/app/error.tsx +39 -10
- package/src/templates/overlays/runtime/nextjs/src/app/loading.tsx +25 -7
- package/src/templates/overlays/runtime/nextjs/src/app/not-found.tsx +54 -13
- package/src/templates/overlays/runtime/nextjs/src/app/page.tsx +55 -13
- package/src/templates/overlays/runtime/nextjs/src/styles/globals.css +1 -1
- package/src/templates/overlays/runtime/vite/src/components/errors/ErrorFallback.tsx +49 -15
- package/src/templates/overlays/runtime/vite/src/components/ui/LoadingSpinner.tsx +33 -13
- package/src/templates/overlays/runtime/vite/src/features/misc/routes/Landing.tsx +78 -21
- package/src/templates/overlays/runtime/vite/src/features/misc/routes/NotFound.tsx +77 -19
- package/src/templates/overlays/runtime/vite/src/main.tsx +0 -2
- package/src/templates/overlays/styling/css/_nextjs/src/app/error.css +49 -0
- package/src/templates/overlays/styling/css/_nextjs/src/app/error.tsx +34 -0
- package/src/templates/overlays/styling/css/_nextjs/src/app/loading.css +28 -0
- package/src/templates/overlays/styling/css/_nextjs/src/app/loading.tsx +13 -0
- package/src/templates/overlays/styling/css/_nextjs/src/app/not-found.css +70 -0
- package/src/templates/overlays/styling/css/_nextjs/src/app/not-found.tsx +25 -0
- package/src/templates/overlays/styling/css/_nextjs/src/app/page.css +73 -0
- package/src/templates/overlays/styling/css/_nextjs/src/app/page.tsx +32 -0
- package/src/templates/overlays/styling/css/_vite/src/components/errors/ErrorFallback.css +49 -0
- package/src/templates/overlays/styling/css/_vite/src/components/errors/ErrorFallback.tsx +22 -0
- package/src/templates/overlays/styling/css/_vite/src/components/ui/LoadingSpinner.css +40 -0
- package/src/templates/overlays/styling/css/_vite/src/components/ui/LoadingSpinner.tsx +21 -0
- package/src/templates/overlays/styling/css/_vite/src/features/misc/routes/Landing.css +73 -0
- package/src/templates/overlays/styling/css/_vite/src/features/misc/routes/Landing.tsx +32 -0
- package/src/templates/overlays/styling/css/_vite/src/features/misc/routes/NotFound.css +70 -0
- package/src/templates/overlays/styling/css/_vite/src/features/misc/routes/NotFound.tsx +25 -0
- package/src/templates/overlays/styling/css/manifest.json +17 -0
- package/src/templates/overlays/styling/css/src/styles/globals.css +107 -0
- package/src/templates/overlays/styling/css-modules/_nextjs/src/app/loading.tsx +13 -0
- package/src/templates/overlays/styling/css-modules/_nextjs/src/app/page.module.css +73 -0
- package/src/templates/overlays/styling/css-modules/_nextjs/src/app/page.tsx +32 -0
- package/src/templates/overlays/styling/css-modules/_vite/src/components/errors/ErrorFallback.module.css +45 -0
- package/src/templates/overlays/styling/css-modules/_vite/src/components/errors/ErrorFallback.tsx +22 -0
- package/src/templates/overlays/styling/css-modules/_vite/src/components/ui/LoadingSpinner.module.css +40 -0
- package/src/templates/overlays/styling/css-modules/_vite/src/components/ui/LoadingSpinner.tsx +23 -0
- package/src/templates/overlays/styling/css-modules/_vite/src/features/misc/routes/Landing.module.css +73 -0
- package/src/templates/overlays/styling/css-modules/_vite/src/features/misc/routes/Landing.tsx +32 -0
- package/src/templates/overlays/styling/css-modules/_vite/src/features/misc/routes/NotFound.module.css +67 -0
- package/src/templates/overlays/styling/css-modules/_vite/src/features/misc/routes/NotFound.tsx +25 -0
- package/src/templates/overlays/styling/css-modules/manifest.json +6 -3
- package/src/templates/overlays/styling/css-modules/src/css.d.ts +13 -0
- package/src/templates/overlays/styling/styled-components/_nextjs/src/app/error.tsx +72 -0
- package/src/templates/overlays/styling/styled-components/_nextjs/src/app/layout.tsx +25 -0
- package/src/templates/overlays/styling/styled-components/_nextjs/src/app/loading.tsx +40 -0
- package/src/templates/overlays/styling/styled-components/_nextjs/src/app/not-found.tsx +91 -0
- package/src/templates/overlays/styling/styled-components/_nextjs/src/app/page.tsx +102 -0
- package/src/templates/overlays/styling/styled-components/_nextjs/src/app/providers.tsx +22 -0
- package/src/templates/overlays/styling/styled-components/{src → _nextjs/src}/lib/StyledComponentsRegistry.tsx +10 -2
- package/src/templates/overlays/styling/styled-components/_vite/src/app/provider.tsx +27 -0
- package/src/templates/overlays/styling/styled-components/_vite/src/components/errors/ErrorFallback.tsx +59 -0
- package/src/templates/overlays/styling/styled-components/_vite/src/components/ui/LoadingSpinner.tsx +47 -0
- package/src/templates/overlays/styling/styled-components/_vite/src/features/misc/routes/Landing.tsx +100 -0
- package/src/templates/overlays/styling/styled-components/_vite/src/features/misc/routes/NotFound.tsx +89 -0
- package/src/templates/overlays/styling/styled-components/_vite/src/main.tsx +13 -0
- package/src/templates/overlays/styling/styled-components/manifest.json +5 -1
- package/src/templates/overlays/styling/tailwind/_nextjs/src/app/error.tsx +33 -0
- package/src/templates/overlays/styling/tailwind/_nextjs/src/app/loading.tsx +12 -0
- package/src/templates/overlays/styling/tailwind/_nextjs/src/app/not-found.tsx +26 -0
- package/src/templates/overlays/styling/tailwind/_nextjs/src/app/page.tsx +33 -0
- package/src/templates/overlays/styling/tailwind/manifest.json +5 -3
- package/src/templates/overlays/runtime/vite/src/styles/globals.css +0 -55
- package/src/templates/overlays/styling/css-modules/src/components/ui/Button.module.css +0 -87
- package/src/templates/overlays/styling/css-modules/src/styles/globals.css +0 -91
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
|
|
6
|
+
const Container = styled.div`
|
|
7
|
+
display: flex;
|
|
8
|
+
min-height: 100vh;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
align-items: center;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const Content = styled.div`
|
|
15
|
+
text-align: center;
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const Title = styled.h1`
|
|
19
|
+
font-size: 1.5rem;
|
|
20
|
+
font-weight: 700;
|
|
21
|
+
color: #dc2626;
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
const Message = styled.p`
|
|
25
|
+
margin-top: 1rem;
|
|
26
|
+
color: #4b5563;
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const RetryButton = styled.button`
|
|
30
|
+
margin-top: 1.5rem;
|
|
31
|
+
border-radius: 0.375rem;
|
|
32
|
+
background-color: #4f46e5;
|
|
33
|
+
padding: 0.625rem 0.875rem;
|
|
34
|
+
font-size: 0.875rem;
|
|
35
|
+
font-weight: 600;
|
|
36
|
+
color: white;
|
|
37
|
+
border: none;
|
|
38
|
+
cursor: pointer;
|
|
39
|
+
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
40
|
+
transition: background-color 0.2s;
|
|
41
|
+
|
|
42
|
+
&:hover {
|
|
43
|
+
background-color: #6366f1;
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
export default function Error({
|
|
48
|
+
error,
|
|
49
|
+
reset,
|
|
50
|
+
}: {
|
|
51
|
+
error: Error & { digest?: string };
|
|
52
|
+
reset: () => void;
|
|
53
|
+
}) {
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
// Log the error to an error reporting service
|
|
56
|
+
console.error(error);
|
|
57
|
+
}, [error]);
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<Container role="alert">
|
|
61
|
+
<Content>
|
|
62
|
+
<Title>Something went wrong</Title>
|
|
63
|
+
<Message>{error.message || 'An unexpected error occurred'}</Message>
|
|
64
|
+
<RetryButton onClick={reset}>Try again</RetryButton>
|
|
65
|
+
</Content>
|
|
66
|
+
</Container>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Metadata } from 'next';
|
|
2
|
+
import { Providers } from './providers';
|
|
3
|
+
|
|
4
|
+
export const metadata: Metadata = {
|
|
5
|
+
title: '{{PROJECT_NAME}}',
|
|
6
|
+
description: 'A production-ready Next.js application',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export default function RootLayout({
|
|
10
|
+
children,
|
|
11
|
+
}: {
|
|
12
|
+
children: React.ReactNode;
|
|
13
|
+
}) {
|
|
14
|
+
return (
|
|
15
|
+
<html lang="en">
|
|
16
|
+
<body>
|
|
17
|
+
<Providers>{children}</Providers>
|
|
18
|
+
</body>
|
|
19
|
+
</html>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import styled, { keyframes } from 'styled-components';
|
|
4
|
+
|
|
5
|
+
const spin = keyframes`
|
|
6
|
+
from {
|
|
7
|
+
transform: rotate(0deg);
|
|
8
|
+
}
|
|
9
|
+
to {
|
|
10
|
+
transform: rotate(360deg);
|
|
11
|
+
}
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const Container = styled.div`
|
|
15
|
+
display: flex;
|
|
16
|
+
min-height: 100vh;
|
|
17
|
+
align-items: center;
|
|
18
|
+
justify-content: center;
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
const Spinner = styled.div`
|
|
22
|
+
width: 2rem;
|
|
23
|
+
height: 2rem;
|
|
24
|
+
border-radius: 50%;
|
|
25
|
+
border: 2px solid #d1d5db;
|
|
26
|
+
border-top-color: #4f46e5;
|
|
27
|
+
animation: ${spin} 1s linear infinite;
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
export default function Loading() {
|
|
31
|
+
return (
|
|
32
|
+
<Container>
|
|
33
|
+
<Spinner role="status" aria-label="Loading" />
|
|
34
|
+
</Container>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
|
|
6
|
+
const Container = styled.div`
|
|
7
|
+
display: flex;
|
|
8
|
+
min-height: 100vh;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
align-items: center;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const Content = styled.div`
|
|
15
|
+
text-align: center;
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const ErrorCode = styled.p`
|
|
19
|
+
font-size: 1rem;
|
|
20
|
+
font-weight: 600;
|
|
21
|
+
color: #4f46e5;
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
const Title = styled.h1`
|
|
25
|
+
margin-top: 1rem;
|
|
26
|
+
font-size: 1.875rem;
|
|
27
|
+
font-weight: 700;
|
|
28
|
+
letter-spacing: -0.025em;
|
|
29
|
+
color: #111827;
|
|
30
|
+
|
|
31
|
+
@media (min-width: 640px) {
|
|
32
|
+
font-size: 3rem;
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const Description = styled.p`
|
|
37
|
+
margin-top: 1.5rem;
|
|
38
|
+
font-size: 1rem;
|
|
39
|
+
line-height: 1.75rem;
|
|
40
|
+
color: #4b5563;
|
|
41
|
+
`;
|
|
42
|
+
|
|
43
|
+
const ButtonGroup = styled.div`
|
|
44
|
+
margin-top: 2.5rem;
|
|
45
|
+
display: flex;
|
|
46
|
+
align-items: center;
|
|
47
|
+
justify-content: center;
|
|
48
|
+
gap: 1.5rem;
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
const PrimaryButton = styled(Link)`
|
|
52
|
+
border-radius: 0.375rem;
|
|
53
|
+
background-color: #4f46e5;
|
|
54
|
+
padding: 0.625rem 0.875rem;
|
|
55
|
+
font-size: 0.875rem;
|
|
56
|
+
font-weight: 600;
|
|
57
|
+
color: white;
|
|
58
|
+
text-decoration: none;
|
|
59
|
+
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
60
|
+
transition: background-color 0.2s;
|
|
61
|
+
|
|
62
|
+
&:hover {
|
|
63
|
+
background-color: #6366f1;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
&:focus-visible {
|
|
67
|
+
outline: 2px solid #4f46e5;
|
|
68
|
+
outline-offset: 2px;
|
|
69
|
+
}
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
export default function NotFound() {
|
|
73
|
+
return (
|
|
74
|
+
<Container>
|
|
75
|
+
<Content>
|
|
76
|
+
<ErrorCode>404</ErrorCode>
|
|
77
|
+
<Title>Page not found</Title>
|
|
78
|
+
<Description>
|
|
79
|
+
Sorry, we couldn't find the page you're looking for.
|
|
80
|
+
</Description>
|
|
81
|
+
<ButtonGroup>
|
|
82
|
+
<PrimaryButton href="/">Go back home</PrimaryButton>
|
|
83
|
+
</ButtonGroup>
|
|
84
|
+
</Content>
|
|
85
|
+
</Container>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
|
|
6
|
+
const Container = styled.div`
|
|
7
|
+
display: flex;
|
|
8
|
+
min-height: 100vh;
|
|
9
|
+
flex-direction: column;
|
|
10
|
+
align-items: center;
|
|
11
|
+
justify-content: center;
|
|
12
|
+
`;
|
|
13
|
+
|
|
14
|
+
const Content = styled.div`
|
|
15
|
+
text-align: center;
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const Title = styled.h1`
|
|
19
|
+
font-size: 2.25rem;
|
|
20
|
+
font-weight: 700;
|
|
21
|
+
letter-spacing: -0.025em;
|
|
22
|
+
color: #111827;
|
|
23
|
+
|
|
24
|
+
@media (min-width: 640px) {
|
|
25
|
+
font-size: 3.75rem;
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const Description = styled.p`
|
|
30
|
+
margin-top: 1.5rem;
|
|
31
|
+
font-size: 1.125rem;
|
|
32
|
+
line-height: 2rem;
|
|
33
|
+
color: #4b5563;
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const ButtonGroup = styled.div`
|
|
37
|
+
margin-top: 2.5rem;
|
|
38
|
+
display: flex;
|
|
39
|
+
align-items: center;
|
|
40
|
+
justify-content: center;
|
|
41
|
+
gap: 1.5rem;
|
|
42
|
+
`;
|
|
43
|
+
|
|
44
|
+
const PrimaryButton = styled(Link)`
|
|
45
|
+
border-radius: 0.375rem;
|
|
46
|
+
background-color: #4f46e5;
|
|
47
|
+
padding: 0.625rem 0.875rem;
|
|
48
|
+
font-size: 0.875rem;
|
|
49
|
+
font-weight: 600;
|
|
50
|
+
color: white;
|
|
51
|
+
text-decoration: none;
|
|
52
|
+
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
53
|
+
transition: background-color 0.2s;
|
|
54
|
+
|
|
55
|
+
&:hover {
|
|
56
|
+
background-color: #6366f1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
&:focus-visible {
|
|
60
|
+
outline: 2px solid #4f46e5;
|
|
61
|
+
outline-offset: 2px;
|
|
62
|
+
}
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
const SecondaryLink = styled.a`
|
|
66
|
+
font-size: 0.875rem;
|
|
67
|
+
font-weight: 600;
|
|
68
|
+
line-height: 1.5rem;
|
|
69
|
+
color: #111827;
|
|
70
|
+
text-decoration: none;
|
|
71
|
+
|
|
72
|
+
&:hover {
|
|
73
|
+
color: #4f46e5;
|
|
74
|
+
}
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
export default function HomePage() {
|
|
78
|
+
return (
|
|
79
|
+
<Container>
|
|
80
|
+
<Content>
|
|
81
|
+
<Title>Welcome to Your App</Title>
|
|
82
|
+
<Description>
|
|
83
|
+
A production-ready Next.js application scaffolded with create-react-forge.
|
|
84
|
+
</Description>
|
|
85
|
+
<ButtonGroup>
|
|
86
|
+
<PrimaryButton href="/dashboard">Get started</PrimaryButton>
|
|
87
|
+
<SecondaryLink
|
|
88
|
+
href="https://github.com/alan2207/bulletproof-react"
|
|
89
|
+
target="_blank"
|
|
90
|
+
rel="noopener noreferrer"
|
|
91
|
+
>
|
|
92
|
+
Learn more <span aria-hidden="true">→</span>
|
|
93
|
+
</SecondaryLink>
|
|
94
|
+
</ButtonGroup>
|
|
95
|
+
</Content>
|
|
96
|
+
</Container>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import StyledComponentsRegistry from '@/lib/StyledComponentsRegistry';
|
|
4
|
+
import { GlobalStyles } from '@/styles/globals';
|
|
5
|
+
import { ReactNode } from 'react';
|
|
6
|
+
|
|
7
|
+
type ProvidersProps = {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function Providers({ children }: ProvidersProps) {
|
|
12
|
+
return (
|
|
13
|
+
<StyledComponentsRegistry>
|
|
14
|
+
<GlobalStyles />
|
|
15
|
+
{children}
|
|
16
|
+
</StyledComponentsRegistry>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { useState } from 'react';
|
|
4
3
|
import { useServerInsertedHTML } from 'next/navigation';
|
|
4
|
+
import React, { useState } from 'react';
|
|
5
5
|
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
|
|
6
6
|
|
|
7
7
|
export default function StyledComponentsRegistry({
|
|
@@ -10,6 +10,7 @@ export default function StyledComponentsRegistry({
|
|
|
10
10
|
children: React.ReactNode;
|
|
11
11
|
}) {
|
|
12
12
|
// Only create stylesheet once with lazy initial state
|
|
13
|
+
// useState with initializer ensures this only runs once
|
|
13
14
|
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
|
|
14
15
|
|
|
15
16
|
useServerInsertedHTML(() => {
|
|
@@ -18,7 +19,11 @@ export default function StyledComponentsRegistry({
|
|
|
18
19
|
return <>{styles}</>;
|
|
19
20
|
});
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
// On the client side, styled-components automatically injects styles
|
|
23
|
+
// We only need StyleSheetManager on the server for SSR collection
|
|
24
|
+
if (typeof window !== 'undefined') {
|
|
25
|
+
return <>{children}</>;
|
|
26
|
+
}
|
|
22
27
|
|
|
23
28
|
return (
|
|
24
29
|
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
|
|
@@ -27,3 +32,6 @@ export default function StyledComponentsRegistry({
|
|
|
27
32
|
);
|
|
28
33
|
}
|
|
29
34
|
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ErrorFallback } from '@/components/errors/ErrorFallback';
|
|
2
|
+
import { LoadingSpinner } from '@/components/ui/LoadingSpinner';
|
|
3
|
+
import { GlobalStyles } from '@/styles/globals';
|
|
4
|
+
import { Suspense } from 'react';
|
|
5
|
+
import { ErrorBoundary } from 'react-error-boundary';
|
|
6
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
7
|
+
|
|
8
|
+
type AppProviderProps = {
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function AppProvider({ children }: AppProviderProps) {
|
|
13
|
+
return (
|
|
14
|
+
<Suspense fallback={<LoadingSpinner />}>
|
|
15
|
+
<ErrorBoundary FallbackComponent={ErrorFallback}>
|
|
16
|
+
<BrowserRouter>
|
|
17
|
+
<GlobalStyles />
|
|
18
|
+
{children}
|
|
19
|
+
</BrowserRouter>
|
|
20
|
+
</ErrorBoundary>
|
|
21
|
+
</Suspense>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { FallbackProps } from 'react-error-boundary';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
const Container = styled.div`
|
|
5
|
+
display: flex;
|
|
6
|
+
min-height: 100vh;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const Content = styled.div`
|
|
13
|
+
text-align: center;
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const Title = styled.h1`
|
|
17
|
+
font-size: 1.5rem;
|
|
18
|
+
font-weight: 700;
|
|
19
|
+
color: #dc2626;
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
const Message = styled.p`
|
|
23
|
+
margin-top: 1rem;
|
|
24
|
+
color: #4b5563;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const RetryButton = styled.button`
|
|
28
|
+
margin-top: 1.5rem;
|
|
29
|
+
border-radius: 0.375rem;
|
|
30
|
+
background-color: #4f46e5;
|
|
31
|
+
padding: 0.625rem 0.875rem;
|
|
32
|
+
font-size: 0.875rem;
|
|
33
|
+
font-weight: 600;
|
|
34
|
+
color: white;
|
|
35
|
+
border: none;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
38
|
+
transition: background-color 0.2s;
|
|
39
|
+
|
|
40
|
+
&:hover {
|
|
41
|
+
background-color: #6366f1;
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
export function ErrorFallback({ error, resetErrorBoundary }: FallbackProps) {
|
|
46
|
+
return (
|
|
47
|
+
<Container role="alert">
|
|
48
|
+
<Content>
|
|
49
|
+
<Title>Something went wrong</Title>
|
|
50
|
+
<Message>{error.message || 'An unexpected error occurred'}</Message>
|
|
51
|
+
<RetryButton onClick={resetErrorBoundary}>Try again</RetryButton>
|
|
52
|
+
</Content>
|
|
53
|
+
</Container>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
package/src/templates/overlays/styling/styled-components/_vite/src/components/ui/LoadingSpinner.tsx
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import styled, { keyframes } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
type LoadingSpinnerProps = {
|
|
4
|
+
size?: 'sm' | 'md' | 'lg';
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const spin = keyframes`
|
|
8
|
+
from {
|
|
9
|
+
transform: rotate(0deg);
|
|
10
|
+
}
|
|
11
|
+
to {
|
|
12
|
+
transform: rotate(360deg);
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const sizes = {
|
|
17
|
+
sm: '1rem',
|
|
18
|
+
md: '2rem',
|
|
19
|
+
lg: '4rem',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const Container = styled.div`
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
const Spinner = styled.div<{ $size: 'sm' | 'md' | 'lg' }>`
|
|
29
|
+
width: ${(props) => sizes[props.$size]};
|
|
30
|
+
height: ${(props) => sizes[props.$size]};
|
|
31
|
+
border-radius: 50%;
|
|
32
|
+
border: 2px solid #d1d5db;
|
|
33
|
+
border-top-color: #4f46e5;
|
|
34
|
+
animation: ${spin} 1s linear infinite;
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
export function LoadingSpinner({ size = 'md' }: LoadingSpinnerProps) {
|
|
38
|
+
return (
|
|
39
|
+
<Container>
|
|
40
|
+
<Spinner $size={size} role="status" aria-label="Loading" />
|
|
41
|
+
</Container>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
package/src/templates/overlays/styling/styled-components/_vite/src/features/misc/routes/Landing.tsx
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Link } from 'react-router-dom';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
const Container = styled.div`
|
|
5
|
+
display: flex;
|
|
6
|
+
min-height: 100vh;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
align-items: center;
|
|
9
|
+
justify-content: center;
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const Content = styled.div`
|
|
13
|
+
text-align: center;
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
const Title = styled.h1`
|
|
17
|
+
font-size: 2.25rem;
|
|
18
|
+
font-weight: 700;
|
|
19
|
+
letter-spacing: -0.025em;
|
|
20
|
+
color: #111827;
|
|
21
|
+
|
|
22
|
+
@media (min-width: 640px) {
|
|
23
|
+
font-size: 3.75rem;
|
|
24
|
+
}
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const Description = styled.p`
|
|
28
|
+
margin-top: 1.5rem;
|
|
29
|
+
font-size: 1.125rem;
|
|
30
|
+
line-height: 2rem;
|
|
31
|
+
color: #4b5563;
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
const ButtonGroup = styled.div`
|
|
35
|
+
margin-top: 2.5rem;
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
justify-content: center;
|
|
39
|
+
gap: 1.5rem;
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
const PrimaryButton = styled(Link)`
|
|
43
|
+
border-radius: 0.375rem;
|
|
44
|
+
background-color: #4f46e5;
|
|
45
|
+
padding: 0.625rem 0.875rem;
|
|
46
|
+
font-size: 0.875rem;
|
|
47
|
+
font-weight: 600;
|
|
48
|
+
color: white;
|
|
49
|
+
text-decoration: none;
|
|
50
|
+
box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
|
|
51
|
+
transition: background-color 0.2s;
|
|
52
|
+
|
|
53
|
+
&:hover {
|
|
54
|
+
background-color: #6366f1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
&:focus-visible {
|
|
58
|
+
outline: 2px solid #4f46e5;
|
|
59
|
+
outline-offset: 2px;
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
const SecondaryLink = styled.a`
|
|
64
|
+
font-size: 0.875rem;
|
|
65
|
+
font-weight: 600;
|
|
66
|
+
line-height: 1.5rem;
|
|
67
|
+
color: #111827;
|
|
68
|
+
text-decoration: none;
|
|
69
|
+
|
|
70
|
+
&:hover {
|
|
71
|
+
color: #4f46e5;
|
|
72
|
+
}
|
|
73
|
+
`;
|
|
74
|
+
|
|
75
|
+
export function Landing() {
|
|
76
|
+
return (
|
|
77
|
+
<Container>
|
|
78
|
+
<Content>
|
|
79
|
+
<Title>Welcome to Your App</Title>
|
|
80
|
+
<Description>
|
|
81
|
+
A production-ready React application scaffolded with create-react-forge.
|
|
82
|
+
</Description>
|
|
83
|
+
<ButtonGroup>
|
|
84
|
+
<PrimaryButton to="/dashboard">Get started</PrimaryButton>
|
|
85
|
+
<SecondaryLink
|
|
86
|
+
href="https://github.com/alan2207/bulletproof-react"
|
|
87
|
+
target="_blank"
|
|
88
|
+
rel="noopener noreferrer"
|
|
89
|
+
>
|
|
90
|
+
Learn more <span aria-hidden="true">→</span>
|
|
91
|
+
</SecondaryLink>
|
|
92
|
+
</ButtonGroup>
|
|
93
|
+
</Content>
|
|
94
|
+
</Container>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|