@aditokmo/create-react-project 0.2.0
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 +150 -0
- package/dist/index.js +129 -0
- package/dist/installers.js +30 -0
- package/dist/mapper.js +28 -0
- package/dist/packages.js +62 -0
- package/dist/questions.js +123 -0
- package/dist/types.js +1 -0
- package/dist/utils.js +101 -0
- package/package.json +65 -0
- package/templates/base/README.md +73 -0
- package/templates/base/eslint.config.js +23 -0
- package/templates/base/index.html +15 -0
- package/templates/base/package.json +14 -0
- package/templates/base/public/vite.svg +1 -0
- package/templates/base/src/App.tsx +9 -0
- package/templates/base/src/api/api.ts +29 -0
- package/templates/base/src/api/http.ts +27 -0
- package/templates/base/src/api/index.ts +2 -0
- package/templates/base/src/assets/react.svg +1 -0
- package/templates/base/src/layout/AuthLayout.tsx +0 -0
- package/templates/base/src/layout/MainLayout.tsx +0 -0
- package/templates/base/src/layout/index.ts +2 -0
- package/templates/base/src/main.tsx +9 -0
- package/templates/base/src/modules/auth/components/AuthForm.tsx +5 -0
- package/templates/base/src/modules/auth/hooks/useAuth.ts +55 -0
- package/templates/base/src/modules/auth/pages/ForgotPassword.tsx +5 -0
- package/templates/base/src/modules/auth/pages/Login.tsx +5 -0
- package/templates/base/src/modules/auth/pages/Register.tsx +5 -0
- package/templates/base/src/modules/auth/pages/index.ts +3 -0
- package/templates/base/src/modules/auth/services/auth.service.ts +17 -0
- package/templates/base/src/modules/auth/services/endpoint.ts +10 -0
- package/templates/base/src/modules/auth/services/index.ts +3 -0
- package/templates/base/src/modules/auth/services/oauth.service.ts +1 -0
- package/templates/base/src/modules/auth/services/password.service.ts +1 -0
- package/templates/base/src/modules/auth/types/auth.types.ts +3 -0
- package/templates/base/src/modules/auth/types/index.ts +3 -0
- package/templates/base/src/modules/auth/types/oauth.types.ts +1 -0
- package/templates/base/src/modules/auth/types/password.types.ts +1 -0
- package/templates/base/src/modules/common/pages/NotFound.tsx +14 -0
- package/templates/base/src/modules/common/pages/index.ts +1 -0
- package/templates/base/src/utils/api-error-handler.ts +25 -0
- package/templates/base/tsconfig.app.json +32 -0
- package/templates/base/tsconfig.json +13 -0
- package/templates/base/tsconfig.node.json +26 -0
- package/templates/base/vite-env.d.ts +1 -0
- package/templates/base/vite.config.ts +18 -0
- package/templates/hooks/index.ts +0 -0
- package/templates/hooks/useDebounce.ts +1 -0
- package/templates/hooks/useTheme.ts +1 -0
- package/templates/hooks/useThrottle.ts +1 -0
- package/templates/hooks/useWebStorage.ts +1 -0
- package/templates/router/react-router/src/components/ProtectedRoute.tsx +20 -0
- package/templates/router/react-router/src/components/PublicRoute.tsx +20 -0
- package/templates/router/react-router/src/modules/auth/routes/index.tsx +21 -0
- package/templates/router/react-router/src/routes/AppRoutes.tsx +18 -0
- package/templates/router/react-router/src/routes/index.ts +3 -0
- package/templates/router/tanstack-router/src/providers/TanstackRouterProvider.tsx +14 -0
- package/templates/router/tanstack-router/src/routes/__root.tsx +13 -0
- package/templates/router/tanstack-router/src/routes/_protected/index.tsx +10 -0
- package/templates/router/tanstack-router/src/routes/_protected.tsx +18 -0
- package/templates/router/tanstack-router/src/routes/_public/forgot-password.tsx +6 -0
- package/templates/router/tanstack-router/src/routes/_public/login.tsx +6 -0
- package/templates/router/tanstack-router/src/routes/_public/register.tsx +6 -0
- package/templates/router/tanstack-router/src/routes/_public.tsx +13 -0
- package/templates/state/react-query/src/hook/useAuth.ts +43 -0
- package/templates/state/react-query/src/provider/ReactQueryProvider.tsx +22 -0
- package/templates/state/react-query/src/provider/index.ts +1 -0
- package/templates/state/zustand/src/auth/useAuthStore.ts +29 -0
- package/templates/state/zustand/src/index.ts +2 -0
- package/templates/state/zustand/src/theme/useThemeStore.ts +27 -0
- package/templates/styles/css/src/404.css +101 -0
- package/templates/styles/css/src/main.css +70 -0
- package/templates/styles/css/src/variables.css +15 -0
- package/templates/styles/scss/src/404.scss +103 -0
- package/templates/styles/scss/src/_index.scss +2 -0
- package/templates/styles/scss/src/_mixins.scss +27 -0
- package/templates/styles/scss/src/_variables.scss +28 -0
- package/templates/styles/scss/src/main.scss +20 -0
- package/templates/styles/tailwind/config/_vite.config.ts +14 -0
- package/templates/styles/tailwind/src/404.css +98 -0
- package/templates/styles/tailwind/src/main.css +123 -0
- package/templates/ui/shadcn/src/components/ui/button.tsx +62 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Login, Register, ForgotPassword } from '../pages';
|
|
2
|
+
|
|
3
|
+
export const authRoutes = [
|
|
4
|
+
{
|
|
5
|
+
children: [
|
|
6
|
+
{
|
|
7
|
+
path: 'login',
|
|
8
|
+
element: <Login />
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
path: 'register',
|
|
12
|
+
element: <Register />
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
path: 'forgot-password',
|
|
16
|
+
element: <ForgotPassword />
|
|
17
|
+
}
|
|
18
|
+
// Edit or Add routes as needed
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { authRoutes } from '@/modules/auth/routes';
|
|
2
|
+
import { NotFound } from '@/modules/common/pages';
|
|
3
|
+
import { useRoutes } from 'react-router-dom';
|
|
4
|
+
|
|
5
|
+
// Exaxmple: Import feature routes here
|
|
6
|
+
// import { dashboardRoutes } from '@/features/dashboard/routes';
|
|
7
|
+
|
|
8
|
+
export function AppRoutes() {
|
|
9
|
+
return useRoutes([
|
|
10
|
+
// Spread feature routes here
|
|
11
|
+
...authRoutes,
|
|
12
|
+
{
|
|
13
|
+
path: '*',
|
|
14
|
+
element: <NotFound />
|
|
15
|
+
}
|
|
16
|
+
// ...dashboardRoutes,
|
|
17
|
+
]);
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { RouterProvider, createRouter } from '@tanstack/react-router'
|
|
2
|
+
import { routeTree } from '../routeTree.gen'
|
|
3
|
+
|
|
4
|
+
const router = createRouter({ routeTree })
|
|
5
|
+
|
|
6
|
+
declare module '@tanstack/react-router' {
|
|
7
|
+
interface Register {
|
|
8
|
+
router: typeof router
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function TanStackRouterProvider() {
|
|
13
|
+
return <RouterProvider router={router} />
|
|
14
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NotFound } from '@/modules/common/pages';
|
|
2
|
+
import { createRootRoute, Outlet } from '@tanstack/react-router';
|
|
3
|
+
import { TanStackRouterDevtools } from '@tanstack/react-router-devtools';
|
|
4
|
+
|
|
5
|
+
export const Route = createRootRoute({
|
|
6
|
+
component: () => (
|
|
7
|
+
<>
|
|
8
|
+
<Outlet />
|
|
9
|
+
<TanStackRouterDevtools />
|
|
10
|
+
</>
|
|
11
|
+
),
|
|
12
|
+
notFoundComponent: () => <NotFound />,
|
|
13
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
|
|
3
|
+
export const Route = createFileRoute('/_protected/')({
|
|
4
|
+
// Check public files and do the same - this is only template
|
|
5
|
+
component: RouteComponent,
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
function RouteComponent() {
|
|
9
|
+
return <div>Page for authenticated users</div>
|
|
10
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createFileRoute, redirect, Outlet } from '@tanstack/react-router';
|
|
2
|
+
import { useAuthStore } from '@/store';
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute('/_protected')({
|
|
5
|
+
beforeLoad: ({ location }) => {
|
|
6
|
+
const isAuthenticated = useAuthStore.getState().isAuthenticated;
|
|
7
|
+
|
|
8
|
+
if (!isAuthenticated) {
|
|
9
|
+
throw redirect({
|
|
10
|
+
to: '/login',
|
|
11
|
+
search: {
|
|
12
|
+
redirect: location.href,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
component: () => <Outlet />,
|
|
18
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createFileRoute, redirect, Outlet } from '@tanstack/react-router';
|
|
2
|
+
import { useAuthStore } from '@/store';
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute('/_public')({
|
|
5
|
+
beforeLoad: () => {
|
|
6
|
+
const isAuthenticated = useAuthStore.getState().isAuthenticated;
|
|
7
|
+
|
|
8
|
+
if (isAuthenticated) {
|
|
9
|
+
throw redirect({ to: '/' });
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
component: () => <Outlet />,
|
|
13
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getAPIErrorMessage } from "@/utils/api-error-handler";
|
|
2
|
+
import { AuthService } from "../services"
|
|
3
|
+
import { useMutation } from '@tanstack/react-query';
|
|
4
|
+
|
|
5
|
+
export const useAuth = () => {
|
|
6
|
+
const login = useMutation({
|
|
7
|
+
mutationFn: AuthService.login,
|
|
8
|
+
onSuccess: (res) => {
|
|
9
|
+
// Handle mutation success
|
|
10
|
+
},
|
|
11
|
+
onError: (error) => {
|
|
12
|
+
const errorMessage = getAPIErrorMessage(error);
|
|
13
|
+
console.error(errorMessage)
|
|
14
|
+
// Display error message to user
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const register = useMutation({
|
|
19
|
+
mutationFn: AuthService.register,
|
|
20
|
+
onSuccess: (res) => {
|
|
21
|
+
// Handle mutation success
|
|
22
|
+
},
|
|
23
|
+
onError: (error) => {
|
|
24
|
+
const errorMessage = getAPIErrorMessage(error);
|
|
25
|
+
console.error(errorMessage)
|
|
26
|
+
// Display error message to user
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const logout = useMutation({
|
|
31
|
+
mutationFn: AuthService.logout,
|
|
32
|
+
onSuccess: (res) => {
|
|
33
|
+
// Handle mutation success
|
|
34
|
+
},
|
|
35
|
+
onError: (error) => {
|
|
36
|
+
const errorMessage = getAPIErrorMessage(error);
|
|
37
|
+
console.error(errorMessage)
|
|
38
|
+
// Display error message to user
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return { login, register, logout }
|
|
43
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
|
|
3
|
+
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
|
4
|
+
|
|
5
|
+
export function ReactQueryProvider({ children }: { children: React.ReactNode }) {
|
|
6
|
+
const [queryClient] = useState(() => new QueryClient({
|
|
7
|
+
defaultOptions: {
|
|
8
|
+
queries: {
|
|
9
|
+
refetchOnWindowFocus: false,
|
|
10
|
+
retry: 1,
|
|
11
|
+
staleTime: 5 * 1000,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<QueryClientProvider client={queryClient}>
|
|
18
|
+
{children}
|
|
19
|
+
<ReactQueryDevtools initialIsOpen={false} />
|
|
20
|
+
</QueryClientProvider>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ReactQueryProvider'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
|
|
3
|
+
interface User {
|
|
4
|
+
id: string;
|
|
5
|
+
email: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface AuthState {
|
|
10
|
+
user: User | null;
|
|
11
|
+
isAuthenticated: boolean;
|
|
12
|
+
setAuth: (user: User) => void;
|
|
13
|
+
clearAuth: () => void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const useAuthStore = create<AuthState>((set) => ({
|
|
17
|
+
user: null,
|
|
18
|
+
isAuthenticated: false,
|
|
19
|
+
|
|
20
|
+
setAuth: (user) => set({
|
|
21
|
+
user,
|
|
22
|
+
isAuthenticated: true
|
|
23
|
+
}),
|
|
24
|
+
|
|
25
|
+
clearAuth: () => set({
|
|
26
|
+
user: null,
|
|
27
|
+
isAuthenticated: false
|
|
28
|
+
}),
|
|
29
|
+
}));
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { create } from 'zustand';
|
|
2
|
+
import { persist, devtools } from 'zustand/middleware';
|
|
3
|
+
|
|
4
|
+
interface Theme {
|
|
5
|
+
theme: 'light' | 'dark';
|
|
6
|
+
setTheme: (theme: 'light' | 'dark') => void;
|
|
7
|
+
toggleTheme: () => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const useThemeStore = create<Theme>()(
|
|
11
|
+
devtools(
|
|
12
|
+
persist((set) => ({
|
|
13
|
+
theme: 'light',
|
|
14
|
+
setTheme: (theme) => set({ theme }),
|
|
15
|
+
toggleTheme: () => set((state) => ({
|
|
16
|
+
theme: state.theme === 'light' ? 'dark' : 'light'
|
|
17
|
+
})),
|
|
18
|
+
}),
|
|
19
|
+
{
|
|
20
|
+
name: 'theme'
|
|
21
|
+
}
|
|
22
|
+
),
|
|
23
|
+
{
|
|
24
|
+
name: 'ThemeStore'
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
@import "./variables.css";
|
|
2
|
+
|
|
3
|
+
.not-found-wrapper {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
min-height: 100vh;
|
|
9
|
+
padding: 20px;
|
|
10
|
+
text-align: center;
|
|
11
|
+
font-family: var(--font-main);
|
|
12
|
+
background: var(--bg-light);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.not-found-content {
|
|
16
|
+
padding: 100px 70px;
|
|
17
|
+
background: var(--white);
|
|
18
|
+
max-width: 700px;
|
|
19
|
+
width: 100%;
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
align-items: center;
|
|
24
|
+
border-radius: var(--radius-lg);
|
|
25
|
+
box-shadow: var(--shadow-main);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.not-found-content span {
|
|
29
|
+
color: var(--text-muted);
|
|
30
|
+
font-weight: 700;
|
|
31
|
+
font-size: 6rem;
|
|
32
|
+
line-height: 1;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.not-found-content h1 {
|
|
36
|
+
font-size: 2.5rem;
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
margin: 0;
|
|
39
|
+
color: var(--text-dark);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.not-found-content p {
|
|
43
|
+
font-size: 1rem;
|
|
44
|
+
color: var(--text-muted);
|
|
45
|
+
max-width: 360px;
|
|
46
|
+
width: 100%;
|
|
47
|
+
margin: 10px 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.not-found-content a {
|
|
51
|
+
font-size: 0.9rem;
|
|
52
|
+
color: var(--white);
|
|
53
|
+
background-color: var(--primary);
|
|
54
|
+
padding: 10px 30px;
|
|
55
|
+
margin-top: 20px;
|
|
56
|
+
border-radius: var(--radius-sm);
|
|
57
|
+
transition: 0.2s;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
text-decoration: none;
|
|
60
|
+
display: inline-block;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.not-found-content a:hover {
|
|
64
|
+
background: var(--primary-hover);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@media (max-width: 768px) {
|
|
68
|
+
.not-found-content {
|
|
69
|
+
padding: 60px 40px;
|
|
70
|
+
max-width: 90%;
|
|
71
|
+
}
|
|
72
|
+
.not-found-content span {
|
|
73
|
+
font-size: 4.5rem;
|
|
74
|
+
margin-bottom: 20px;
|
|
75
|
+
}
|
|
76
|
+
.not-found-content h1 {
|
|
77
|
+
font-size: 2rem;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@media (max-width: 480px) {
|
|
82
|
+
.not-found-content {
|
|
83
|
+
padding: 40px 20px;
|
|
84
|
+
border-radius: var(--radius-md);
|
|
85
|
+
}
|
|
86
|
+
.not-found-content span {
|
|
87
|
+
font-size: 3.5rem;
|
|
88
|
+
margin-bottom: 20px;
|
|
89
|
+
}
|
|
90
|
+
.not-found-content h1 {
|
|
91
|
+
font-size: 1.5rem;
|
|
92
|
+
}
|
|
93
|
+
.not-found-content p {
|
|
94
|
+
font-size: 0.9rem;
|
|
95
|
+
}
|
|
96
|
+
.not-found-content a {
|
|
97
|
+
width: 100%;
|
|
98
|
+
box-sizing: border-box;
|
|
99
|
+
text-align: center;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
@import "./variables.css";
|
|
2
|
+
|
|
3
|
+
* {
|
|
4
|
+
margin: 0;
|
|
5
|
+
padding: 0;
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
html {
|
|
10
|
+
scroll-behavior: smooth;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
body {
|
|
14
|
+
font-family: var(--font-main);
|
|
15
|
+
background-color: var(--white);
|
|
16
|
+
color: var(--text-dark);
|
|
17
|
+
line-height: 1.6;
|
|
18
|
+
min-height: 100vh;
|
|
19
|
+
-webkit-font-smoothing: antialiased;
|
|
20
|
+
-moz-osx-font-smoothing: grayscale;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
h1,
|
|
24
|
+
h2,
|
|
25
|
+
h3,
|
|
26
|
+
h4,
|
|
27
|
+
h5,
|
|
28
|
+
h6 {
|
|
29
|
+
color: var(--text-dark);
|
|
30
|
+
font-weight: 600;
|
|
31
|
+
line-height: 1.2;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
p {
|
|
35
|
+
color: var(--text-muted);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.container {
|
|
39
|
+
width: 100%;
|
|
40
|
+
max-width: 1200px;
|
|
41
|
+
margin: 0 auto;
|
|
42
|
+
padding: 0 20px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.flex-center {
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: center;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
a {
|
|
52
|
+
text-decoration: none;
|
|
53
|
+
color: var(--primary);
|
|
54
|
+
transition: 0.3s ease;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
a:hover {
|
|
58
|
+
color: var(--primary-hover);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
button {
|
|
62
|
+
font-family: inherit;
|
|
63
|
+
cursor: pointer;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
img {
|
|
67
|
+
max-width: 100%;
|
|
68
|
+
height: auto;
|
|
69
|
+
display: block;
|
|
70
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--primary: #37538a;
|
|
3
|
+
--primary-hover: #263c67;
|
|
4
|
+
--bg-light: #e6edfc;
|
|
5
|
+
--white: #ffffff;
|
|
6
|
+
--text-dark: #111111;
|
|
7
|
+
--text-muted: #666666;
|
|
8
|
+
|
|
9
|
+
--font-main: "Arial", sans-serif;
|
|
10
|
+
|
|
11
|
+
--shadow-main: 0px 0px 14px -14px rgba(0, 0, 0, 0.75);
|
|
12
|
+
--radius-lg: 24px;
|
|
13
|
+
--radius-md: 16px;
|
|
14
|
+
--radius-sm: 10px;
|
|
15
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
@use "./index.scss" as *;
|
|
2
|
+
|
|
3
|
+
.not-found-wrapper {
|
|
4
|
+
display: flex;
|
|
5
|
+
flex-direction: column;
|
|
6
|
+
align-items: center;
|
|
7
|
+
justify-content: center;
|
|
8
|
+
min-height: 100vh;
|
|
9
|
+
padding: 20px;
|
|
10
|
+
text-align: center;
|
|
11
|
+
font-family: $font-main;
|
|
12
|
+
background: $bg-light;
|
|
13
|
+
|
|
14
|
+
.not-found-content {
|
|
15
|
+
padding: 100px 70px;
|
|
16
|
+
background: $white;
|
|
17
|
+
max-width: 700px;
|
|
18
|
+
width: 100%;
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
justify-content: center;
|
|
22
|
+
align-items: center;
|
|
23
|
+
border-radius: $radius-lg;
|
|
24
|
+
box-shadow: $shadow-main;
|
|
25
|
+
|
|
26
|
+
span {
|
|
27
|
+
color: $text-muted;
|
|
28
|
+
font-weight: 700;
|
|
29
|
+
font-size: 6rem;
|
|
30
|
+
line-height: 1;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
h1 {
|
|
34
|
+
font-size: 2.5rem;
|
|
35
|
+
font-weight: 600;
|
|
36
|
+
margin: 0;
|
|
37
|
+
color: $text-dark;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
p {
|
|
41
|
+
font-size: 1rem;
|
|
42
|
+
color: $text-muted;
|
|
43
|
+
max-width: 360px;
|
|
44
|
+
width: 100%;
|
|
45
|
+
margin: 10px 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
a {
|
|
49
|
+
font-size: 0.9rem;
|
|
50
|
+
color: $white;
|
|
51
|
+
background-color: $primary;
|
|
52
|
+
padding: 10px 30px;
|
|
53
|
+
margin-top: 20px;
|
|
54
|
+
border-radius: $radius-sm;
|
|
55
|
+
transition: 0.2s;
|
|
56
|
+
cursor: pointer;
|
|
57
|
+
text-decoration: none;
|
|
58
|
+
display: inline-block;
|
|
59
|
+
|
|
60
|
+
&:hover {
|
|
61
|
+
background: $primary-hover;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@media (max-width: 768px) {
|
|
66
|
+
padding: 60px 40px;
|
|
67
|
+
max-width: 90%;
|
|
68
|
+
|
|
69
|
+
span {
|
|
70
|
+
font-size: 4.5rem;
|
|
71
|
+
margin-bottom: 20px;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
h1 {
|
|
75
|
+
font-size: 2rem;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@media (max-width: 480px) {
|
|
80
|
+
padding: 40px 20px;
|
|
81
|
+
border-radius: $radius-md;
|
|
82
|
+
|
|
83
|
+
span {
|
|
84
|
+
font-size: 3.5rem;
|
|
85
|
+
margin-bottom: 20px;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
h1 {
|
|
89
|
+
font-size: 1.5rem;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
p {
|
|
93
|
+
font-size: 0.9rem;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
a {
|
|
97
|
+
width: 100%;
|
|
98
|
+
box-sizing: border-box;
|
|
99
|
+
text-align: center;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
$breakpoint-tablet: 768px;
|
|
2
|
+
$breakpoint-mobile: 480px;
|
|
3
|
+
|
|
4
|
+
@mixin tablet {
|
|
5
|
+
@media (max-width: #{$breakpoint-tablet}) {
|
|
6
|
+
@content;
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@mixin mobile {
|
|
11
|
+
@media (max-width: #{$breakpoint-mobile}) {
|
|
12
|
+
@content;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// helpers
|
|
17
|
+
@mixin flex-center {
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@mixin text-truncate {
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
text-overflow: ellipsis;
|
|
26
|
+
white-space: nowrap;
|
|
27
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Brand Colors
|
|
2
|
+
$primary: #37538a;
|
|
3
|
+
$primary-hover: #263c67;
|
|
4
|
+
$primary-dark: #263c67;
|
|
5
|
+
$accent: #e6edfc;
|
|
6
|
+
|
|
7
|
+
// Text
|
|
8
|
+
$text-dark: #111111;
|
|
9
|
+
$text-muted: #666666;
|
|
10
|
+
|
|
11
|
+
// Background
|
|
12
|
+
$bg-light: #e6edfc;
|
|
13
|
+
|
|
14
|
+
// Radius
|
|
15
|
+
$radius-sm: 10px;
|
|
16
|
+
$radius-md: 16px;
|
|
17
|
+
$radius-lg: 24px;
|
|
18
|
+
|
|
19
|
+
// Shadows
|
|
20
|
+
$shadow-main: 0px 0px 14px -14px rgba(0, 0, 0, 0.75);
|
|
21
|
+
|
|
22
|
+
// Neutrals
|
|
23
|
+
$white: #ffffff;
|
|
24
|
+
$black: #111111;
|
|
25
|
+
$gray: #666666;
|
|
26
|
+
|
|
27
|
+
// Fonts
|
|
28
|
+
$font-main: "Arial", sans-serif;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
@use "index" as *;
|
|
2
|
+
|
|
3
|
+
* {
|
|
4
|
+
margin: 0;
|
|
5
|
+
padding: 0;
|
|
6
|
+
box-sizing: border-box;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
body {
|
|
10
|
+
font-family: $font-main;
|
|
11
|
+
background-color: $white;
|
|
12
|
+
color: $black;
|
|
13
|
+
line-height: 1.5;
|
|
14
|
+
-webkit-font-smoothing: antialiased;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
a {
|
|
18
|
+
text-decoration: none;
|
|
19
|
+
color: inherit;
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import path from "path"
|
|
2
|
+
import tailwindcss from "@tailwindcss/vite"
|
|
3
|
+
import react from "@vitejs/plugin-react"
|
|
4
|
+
import { defineConfig } from "vite"
|
|
5
|
+
|
|
6
|
+
// https://vite.dev/config/
|
|
7
|
+
export default defineConfig({
|
|
8
|
+
plugins: [react(), tailwindcss()],
|
|
9
|
+
resolve: {
|
|
10
|
+
alias: {
|
|
11
|
+
"@": path.resolve(__dirname, "./src"),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
})
|