create-react-scaffold-cli 1.0.6 → 1.1.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/bin/index.js +15 -5
- package/package.json +21 -7
- package/scripts/createProject.js +8 -2
- package/templates/{base → base-js}/src/shared/utils/parser.js +1 -1
- package/templates/base-ts/.husky/pre-commit +1 -0
- package/templates/base-ts/.husky/pre-push +0 -0
- package/templates/base-ts/.prettierrc +8 -0
- package/templates/base-ts/.vscode/extensions.json +8 -0
- package/templates/base-ts/.vscode/settings.json +16 -0
- package/templates/base-ts/README.md +165 -0
- package/templates/base-ts/docs/DOCS.md +7 -0
- package/templates/base-ts/eslint.config.js +56 -0
- package/templates/base-ts/index.html +16 -0
- package/templates/base-ts/jsconfig.json +7 -0
- package/templates/base-ts/package.json +69 -0
- package/templates/base-ts/postcss.config.mjs +7 -0
- package/templates/base-ts/public/icons/react.svg +1 -0
- package/templates/base-ts/src/app/APP.md +74 -0
- package/templates/base-ts/src/app/App.tsx +15 -0
- package/templates/base-ts/src/app/Router.tsx +48 -0
- package/templates/base-ts/src/app/index.css +37 -0
- package/templates/base-ts/src/app/main.tsx +13 -0
- package/templates/base-ts/src/app/middlewares/AuthMiddleware.tsx +6 -0
- package/templates/base-ts/src/app/middlewares/index.ts +1 -0
- package/templates/base-ts/src/app/providers/QueryProvider.tsx +75 -0
- package/templates/base-ts/src/app/providers/index.ts +1 -0
- package/templates/base-ts/src/app/routes.registry.ts +9 -0
- package/templates/base-ts/src/features/FEATURES.md +102 -0
- package/templates/base-ts/src/features/sample/components/index.ts +0 -0
- package/templates/base-ts/src/features/sample/constants/index.ts +3 -0
- package/templates/base-ts/src/features/sample/constants/sample.assets.ts +8 -0
- package/templates/base-ts/src/features/sample/constants/sample.navigations.ts +5 -0
- package/templates/base-ts/src/features/sample/constants/sample.queryKeys.ts +3 -0
- package/templates/base-ts/src/features/sample/hooks/index.ts +0 -0
- package/templates/base-ts/src/features/sample/index.ts +1 -0
- package/templates/base-ts/src/features/sample/pages/SamplePage.tsx +7 -0
- package/templates/base-ts/src/features/sample/pages/index.ts +1 -0
- package/templates/base-ts/src/features/sample/sample.routes.ts +13 -0
- package/templates/base-ts/src/features/welcome/components/CodeLine.tsx +59 -0
- package/templates/base-ts/src/features/welcome/components/Divider.tsx +11 -0
- package/templates/base-ts/src/features/welcome/components/Footer.tsx +78 -0
- package/templates/base-ts/src/features/welcome/components/Hero.tsx +131 -0
- package/templates/base-ts/src/features/welcome/components/IconLink.tsx +24 -0
- package/templates/base-ts/src/features/welcome/components/QuickStartPanel.tsx +63 -0
- package/templates/base-ts/src/features/welcome/components/RingSoft.tsx +21 -0
- package/templates/base-ts/src/features/welcome/components/StorySections.tsx +63 -0
- package/templates/base-ts/src/features/welcome/components/WhatYouGet.tsx +49 -0
- package/templates/base-ts/src/features/welcome/components/index.ts +5 -0
- package/templates/base-ts/src/features/welcome/constants/index.ts +2 -0
- package/templates/base-ts/src/features/welcome/constants/welcome.constants.ts +21 -0
- package/templates/base-ts/src/features/welcome/constants/welcome.navigations.ts +3 -0
- package/templates/base-ts/src/features/welcome/index.ts +1 -0
- package/templates/base-ts/src/features/welcome/pages/WelcomePage.tsx +28 -0
- package/templates/base-ts/src/features/welcome/pages/index.ts +1 -0
- package/templates/base-ts/src/features/welcome/welcome.routes.ts +13 -0
- package/templates/base-ts/src/shared/SHARED.md +104 -0
- package/templates/base-ts/src/shared/constants/app.constants.ts +11 -0
- package/templates/base-ts/src/shared/constants/assets.constants.ts +5 -0
- package/templates/base-ts/src/shared/constants/index.ts +2 -0
- package/templates/base-ts/src/shared/contexts/index.ts +0 -0
- package/templates/base-ts/src/shared/hooks/index.ts +3 -0
- package/templates/base-ts/src/shared/hooks/useBooleanState.ts +19 -0
- package/templates/base-ts/src/shared/hooks/useDebounce.ts +17 -0
- package/templates/base-ts/src/shared/hooks/useToggleState.ts +11 -0
- package/templates/base-ts/src/shared/layouts/index.ts +0 -0
- package/templates/base-ts/src/shared/libs/axios.ts +6 -0
- package/templates/base-ts/src/shared/libs/cn.ts +6 -0
- package/templates/base-ts/src/shared/libs/index.ts +2 -0
- package/templates/base-ts/src/shared/theme/index.ts +1 -0
- package/templates/base-ts/src/shared/theme/theme.ts +2149 -0
- package/templates/base-ts/src/shared/types/navigation.ts +9 -0
- package/templates/base-ts/src/shared/types/ui.ts +5 -0
- package/templates/base-ts/src/shared/ui/Box.tsx +153 -0
- package/templates/base-ts/src/shared/ui/Button.tsx +124 -0
- package/templates/base-ts/src/shared/ui/Checkbox.tsx +87 -0
- package/templates/base-ts/src/shared/ui/DropdownMenu.tsx +134 -0
- package/templates/base-ts/src/shared/ui/Flex.tsx +96 -0
- package/templates/base-ts/src/shared/ui/FlexItem.tsx +67 -0
- package/templates/base-ts/src/shared/ui/FormField.tsx +139 -0
- package/templates/base-ts/src/shared/ui/Grid.tsx +96 -0
- package/templates/base-ts/src/shared/ui/GridItem.tsx +67 -0
- package/templates/base-ts/src/shared/ui/Modal.tsx +42 -0
- package/templates/base-ts/src/shared/ui/Scrollable.tsx +48 -0
- package/templates/base-ts/src/shared/ui/Select.tsx +212 -0
- package/templates/base-ts/src/shared/ui/Sheet.tsx +126 -0
- package/templates/base-ts/src/shared/ui/Text.tsx +99 -0
- package/templates/base-ts/src/shared/ui/Toaster.tsx +31 -0
- package/templates/base-ts/src/shared/ui/index.ts +20 -0
- package/templates/base-ts/src/shared/utils/getClassName.ts +8 -0
- package/templates/base-ts/src/shared/utils/index.ts +4 -0
- package/templates/base-ts/src/shared/utils/localStorage.ts +18 -0
- package/templates/base-ts/src/shared/utils/memo.ts +9 -0
- package/templates/base-ts/src/shared/utils/motion.ts +0 -0
- package/templates/base-ts/src/shared/utils/parser.ts +41 -0
- package/templates/base-ts/src/shared/utils/regix.ts +3 -0
- package/templates/base-ts/src/shared/utils/tryCatch.ts +16 -0
- package/templates/base-ts/src/vite-env.d.ts +1 -0
- package/templates/base-ts/tsconfig.json +33 -0
- package/templates/base-ts/tsconfig.node.json +11 -0
- package/templates/base-ts/tsconfig.tsbuildinfo +1 -0
- package/templates/base-ts/vercel.json +3 -0
- package/templates/base-ts/vite.config.d.ts +2 -0
- package/templates/base-ts/vite.config.ts +18 -0
- /package/templates/{base → base-js}/.husky/pre-commit +0 -0
- /package/templates/{base → base-js}/.husky/pre-push +0 -0
- /package/templates/{base → base-js}/.prettierrc +0 -0
- /package/templates/{base → base-js}/.vscode/extensions.json +0 -0
- /package/templates/{base → base-js}/.vscode/settings.json +0 -0
- /package/templates/{base → base-js}/README.md +0 -0
- /package/templates/{base → base-js}/docs/DOCS.md +0 -0
- /package/templates/{base → base-js}/eslint.config.js +0 -0
- /package/templates/{base → base-js}/index.html +0 -0
- /package/templates/{base → base-js}/jsconfig.json +0 -0
- /package/templates/{base → base-js}/package.json +0 -0
- /package/templates/{base → base-js}/postcss.config.mjs +0 -0
- /package/templates/{base → base-js}/public/icons/react.svg +0 -0
- /package/templates/{base → base-js}/src/app/APP.md +0 -0
- /package/templates/{base → base-js}/src/app/App.jsx +0 -0
- /package/templates/{base → base-js}/src/app/Router.jsx +0 -0
- /package/templates/{base → base-js}/src/app/index.css +0 -0
- /package/templates/{base → base-js}/src/app/main.jsx +0 -0
- /package/templates/{base → base-js}/src/app/middlewares/AuthMiddleware.jsx +0 -0
- /package/templates/{base → base-js}/src/app/middlewares/index.js +0 -0
- /package/templates/{base → base-js}/src/app/providers/QueryProvider.jsx +0 -0
- /package/templates/{base → base-js}/src/app/providers/index.js +0 -0
- /package/templates/{base → base-js}/src/app/routes.registry.js +0 -0
- /package/templates/{base → base-js}/src/features/FEATURES.md +0 -0
- /package/templates/{base → base-js}/src/features/sample/components/index.js +0 -0
- /package/templates/{base → base-js}/src/features/sample/constants/index.js +0 -0
- /package/templates/{base → base-js}/src/features/sample/constants/sample.assets.js +0 -0
- /package/templates/{base → base-js}/src/features/sample/constants/sample.constants.js +0 -0
- /package/templates/{base → base-js}/src/features/sample/constants/sample.navigations.js +0 -0
- /package/templates/{base → base-js}/src/features/sample/constants/sample.queryKeys.js +0 -0
- /package/templates/{base → base-js}/src/features/sample/hooks/index.js +0 -0
- /package/templates/{base → base-js}/src/features/sample/index.js +0 -0
- /package/templates/{base → base-js}/src/features/sample/pages/SamplePage.jsx +0 -0
- /package/templates/{base → base-js}/src/features/sample/pages/index.js +0 -0
- /package/templates/{base → base-js}/src/features/sample/sample.context.js +0 -0
- /package/templates/{base → base-js}/src/features/sample/sample.routes.js +0 -0
- /package/templates/{base → base-js}/src/features/welcome/components/CodeLine.jsx +0 -0
- /package/templates/{base → base-js}/src/features/welcome/components/Divider.jsx +0 -0
- /package/templates/{base → base-js}/src/features/welcome/components/Footer.jsx +0 -0
- /package/templates/{base → base-js}/src/features/welcome/components/Hero.jsx +0 -0
- /package/templates/{base → base-js}/src/features/welcome/components/IconLink.jsx +0 -0
- /package/templates/{base → base-js}/src/features/welcome/components/QuickStartPanel.jsx +0 -0
- /package/templates/{base → base-js}/src/features/welcome/components/RingSoft.jsx +0 -0
- /package/templates/{base → base-js}/src/features/welcome/components/StorySections.jsx +0 -0
- /package/templates/{base → base-js}/src/features/welcome/components/WhatYouGet.jsx +0 -0
- /package/templates/{base → base-js}/src/features/welcome/components/index.js +0 -0
- /package/templates/{base → base-js}/src/features/welcome/constants/index.js +0 -0
- /package/templates/{base → base-js}/src/features/welcome/constants/welcome.constants.js +0 -0
- /package/templates/{base → base-js}/src/features/welcome/constants/welcome.navigations.js +0 -0
- /package/templates/{base → base-js}/src/features/welcome/index.js +0 -0
- /package/templates/{base → base-js}/src/features/welcome/pages/WelcomePage.jsx +0 -0
- /package/templates/{base → base-js}/src/features/welcome/pages/index.js +0 -0
- /package/templates/{base → base-js}/src/features/welcome/welcome.routes.js +0 -0
- /package/templates/{base → base-js}/src/shared/SHARED.md +0 -0
- /package/templates/{base → base-js}/src/shared/constants/app.constants.js +0 -0
- /package/templates/{base → base-js}/src/shared/constants/assets.constants.js +0 -0
- /package/templates/{base → base-js}/src/shared/constants/index.js +0 -0
- /package/templates/{base → base-js}/src/shared/contexts/index.js +0 -0
- /package/templates/{base → base-js}/src/shared/hooks/index.js +0 -0
- /package/templates/{base → base-js}/src/shared/hooks/useBooleanState.js +0 -0
- /package/templates/{base → base-js}/src/shared/hooks/useDebounce.js +0 -0
- /package/templates/{base → base-js}/src/shared/hooks/useToggleState.js +0 -0
- /package/templates/{base → base-js}/src/shared/layouts/index.js +0 -0
- /package/templates/{base → base-js}/src/shared/libs/axios.js +0 -0
- /package/templates/{base → base-js}/src/shared/libs/cn.js +0 -0
- /package/templates/{base → base-js}/src/shared/libs/index.js +0 -0
- /package/templates/{base → base-js}/src/shared/theme/index.js +0 -0
- /package/templates/{base → base-js}/src/shared/theme/theme.js +0 -0
- /package/templates/{base → base-js}/src/shared/ui/Box.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/Button.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/Checkbox.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/DropdownMenu.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/Flex.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/FlexItem.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/FormField.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/Grid.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/GridItem.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/Modal.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/Scrollable.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/Select.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/Sheet.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/Text.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/Toaster.jsx +0 -0
- /package/templates/{base → base-js}/src/shared/ui/index.js +0 -0
- /package/templates/{base → base-js}/src/shared/utils/getClassName.js +0 -0
- /package/templates/{base → base-js}/src/shared/utils/index.js +0 -0
- /package/templates/{base → base-js}/src/shared/utils/localStorage.js +0 -0
- /package/templates/{base → base-js}/src/shared/utils/memo.js +0 -0
- /package/templates/{base → base-js}/src/shared/utils/motion.js +0 -0
- /package/templates/{base → base-js}/src/shared/utils/regix.js +0 -0
- /package/templates/{base → base-js}/src/shared/utils/tryCatch.js +0 -0
- /package/templates/{base → base-js}/vercel.json +0 -0
- /package/templates/{base → base-js}/vite.config.js +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { StrictMode } from 'react';
|
|
2
|
+
import { createRoot } from 'react-dom/client';
|
|
3
|
+
import './index.css';
|
|
4
|
+
import { App } from './App';
|
|
5
|
+
|
|
6
|
+
const rootElement = document.getElementById('root');
|
|
7
|
+
if (rootElement) {
|
|
8
|
+
createRoot(rootElement).render(
|
|
9
|
+
<StrictMode>
|
|
10
|
+
<App />
|
|
11
|
+
</StrictMode>
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AuthMiddleware } from './AuthMiddleware';
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ReactNode, useState } from 'react';
|
|
2
|
+
import { AxiosError } from 'axios';
|
|
3
|
+
import toast from 'react-hot-toast';
|
|
4
|
+
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
|
|
5
|
+
import { MutationCache, QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
6
|
+
|
|
7
|
+
export const QueryProvider = ({ children }: { children: ReactNode }) => {
|
|
8
|
+
const [queryCache] = useState(
|
|
9
|
+
new QueryCache({
|
|
10
|
+
onError: () => {},
|
|
11
|
+
})
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const [mutationCache] = useState(
|
|
15
|
+
new MutationCache({
|
|
16
|
+
onError: (error) => {
|
|
17
|
+
const isAxiosError = error instanceof AxiosError;
|
|
18
|
+
|
|
19
|
+
if (!isAxiosError) {
|
|
20
|
+
return toast.error('Something went wrong.');
|
|
21
|
+
}
|
|
22
|
+
if (error.code === AxiosError.ERR_NETWORK) {
|
|
23
|
+
return toast.error('Network Error');
|
|
24
|
+
}
|
|
25
|
+
if (error.status === 500) {
|
|
26
|
+
return toast.error('Network Error');
|
|
27
|
+
}
|
|
28
|
+
const { response } = error;
|
|
29
|
+
if (!response) {
|
|
30
|
+
return toast.error('Something went wrong');
|
|
31
|
+
}
|
|
32
|
+
const { data } = response;
|
|
33
|
+
if (!data) {
|
|
34
|
+
return toast.error('Something went wrong');
|
|
35
|
+
}
|
|
36
|
+
const { message } = data as { message: string | string[] };
|
|
37
|
+
if (!message) {
|
|
38
|
+
return toast.error('Something went wrong');
|
|
39
|
+
}
|
|
40
|
+
const isIterable = Array.isArray(message);
|
|
41
|
+
if (!isIterable) {
|
|
42
|
+
return toast.error(message);
|
|
43
|
+
}
|
|
44
|
+
for (let index = 0; index < message.length; index++) {
|
|
45
|
+
const errorMsg = message[index];
|
|
46
|
+
toast.error(errorMsg);
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const [client] = useState(
|
|
53
|
+
new QueryClient({
|
|
54
|
+
queryCache: queryCache,
|
|
55
|
+
mutationCache: mutationCache,
|
|
56
|
+
defaultOptions: {
|
|
57
|
+
queries: {
|
|
58
|
+
retry: false,
|
|
59
|
+
refetchOnWindowFocus: false,
|
|
60
|
+
refetchOnReconnect: false,
|
|
61
|
+
},
|
|
62
|
+
mutations: {
|
|
63
|
+
retry: false,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<QueryClientProvider client={client}>
|
|
71
|
+
{children}
|
|
72
|
+
<ReactQueryDevtools />
|
|
73
|
+
</QueryClientProvider>
|
|
74
|
+
);
|
|
75
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { QueryProvider } from './QueryProvider';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { sampleRoutes } from '@/features/sample';
|
|
2
|
+
import { welcomeRoutes } from '@/features/welcome';
|
|
3
|
+
import { AppRoute } from '@/shared/types/navigation';
|
|
4
|
+
|
|
5
|
+
export const featureRoutes: AppRoute[] = [
|
|
6
|
+
...sampleRoutes,
|
|
7
|
+
...welcomeRoutes,
|
|
8
|
+
// ...otherFeatureRoutes
|
|
9
|
+
];
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Features Module
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
The `features` folder is the **core of the application**.
|
|
6
|
+
|
|
7
|
+
Each feature represents a **self-contained unit of business functionality** with clear ownership and boundaries.
|
|
8
|
+
|
|
9
|
+
> If a user-facing requirement exists, it belongs to a feature.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Feature-First Architecture
|
|
14
|
+
|
|
15
|
+
Instead of grouping by technical layers (components, hooks, services), we group by **business capability**.
|
|
16
|
+
|
|
17
|
+
This improves:
|
|
18
|
+
|
|
19
|
+
- Scalability
|
|
20
|
+
- Ownership
|
|
21
|
+
- Discoverability
|
|
22
|
+
- Refactor safety
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Feature Structure
|
|
27
|
+
|
|
28
|
+
```txt
|
|
29
|
+
features/<feature-name>/
|
|
30
|
+
assets/ # Feature-specific images/icons
|
|
31
|
+
components/ # Reusable UI within the feature
|
|
32
|
+
constants/ # Feature constants
|
|
33
|
+
hooks/ # Feature-specific hooks
|
|
34
|
+
pages/ # Route-level components
|
|
35
|
+
|
|
36
|
+
<feature>.assets.js
|
|
37
|
+
<feature>.context.js
|
|
38
|
+
<feature>.navigations.js
|
|
39
|
+
<feature>.queryKeys.js
|
|
40
|
+
<feature>.routes.jsx
|
|
41
|
+
|
|
42
|
+
index.js # Public API of the feature
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Rules
|
|
48
|
+
|
|
49
|
+
### Feature Ownership
|
|
50
|
+
|
|
51
|
+
- A feature **owns its logic, UI, assets, and state**
|
|
52
|
+
- Features should not depend on other features
|
|
53
|
+
|
|
54
|
+
### Imports
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
// ✅ Allowed
|
|
58
|
+
import { Button } from '@/shared/ui';
|
|
59
|
+
import { useAuth } from '@/features/auth';
|
|
60
|
+
|
|
61
|
+
// ❌ Not Allowed
|
|
62
|
+
import { useProfile } from '@/features/profile';
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Constants Policy
|
|
68
|
+
|
|
69
|
+
Feature constants include:
|
|
70
|
+
|
|
71
|
+
- Query keys
|
|
72
|
+
- Navigation definitions
|
|
73
|
+
- Route names
|
|
74
|
+
- Feature-level config
|
|
75
|
+
|
|
76
|
+
> Constants should stay **inside the feature** unless reused across multiple features.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Contexts
|
|
81
|
+
|
|
82
|
+
- Contexts should be **feature-scoped by default**
|
|
83
|
+
- Global contexts require strong justification
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Deletion Rule
|
|
88
|
+
|
|
89
|
+
If a feature is removed:
|
|
90
|
+
|
|
91
|
+
- Delete the entire folder
|
|
92
|
+
- No side effects elsewhere
|
|
93
|
+
|
|
94
|
+
If deletion breaks other features, boundaries are violated.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Example Feature
|
|
99
|
+
|
|
100
|
+
`sample/` is a reference feature and can be safely removed.
|
|
101
|
+
|
|
102
|
+
Use it as a blueprint for new features.
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { sampleRoutes } from './sample.routes';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as SamplePage } from './SamplePage';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Layouts } from '@/shared/constants';
|
|
2
|
+
import { SamplePage } from './pages';
|
|
3
|
+
import { SampleNavigation } from './constants';
|
|
4
|
+
import { AppRoute } from '@/shared/types/navigation';
|
|
5
|
+
|
|
6
|
+
export const sampleRoutes: AppRoute[] = [
|
|
7
|
+
{
|
|
8
|
+
path: SampleNavigation.Sample,
|
|
9
|
+
element: SamplePage,
|
|
10
|
+
protected: true,
|
|
11
|
+
layout: Layouts.None,
|
|
12
|
+
},
|
|
13
|
+
];
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { memo, useCallback } from 'react';
|
|
2
|
+
import { PiCopySimpleBold, PiCheckBold } from 'react-icons/pi';
|
|
3
|
+
import { Flex } from '@/shared/ui/Flex';
|
|
4
|
+
import { Text } from '@/shared/ui/Text';
|
|
5
|
+
import { useToggleState } from '@/shared/hooks';
|
|
6
|
+
import { Box, FlexItem } from '@/shared/ui';
|
|
7
|
+
import { cn } from '@/shared/libs';
|
|
8
|
+
|
|
9
|
+
interface CodeLineProps {
|
|
10
|
+
value: string;
|
|
11
|
+
isStorySection?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const CodeLine = memo(({ value, isStorySection }: CodeLineProps) => {
|
|
15
|
+
const [copied, toggleCopied] = useToggleState();
|
|
16
|
+
|
|
17
|
+
const onCopy = useCallback(async () => {
|
|
18
|
+
try {
|
|
19
|
+
await navigator.clipboard.writeText(value);
|
|
20
|
+
} catch {
|
|
21
|
+
const ta = document.createElement('textarea');
|
|
22
|
+
ta.value = value;
|
|
23
|
+
document.body.appendChild(ta);
|
|
24
|
+
ta.select();
|
|
25
|
+
document.execCommand('copy');
|
|
26
|
+
document.body.removeChild(ta);
|
|
27
|
+
}
|
|
28
|
+
toggleCopied();
|
|
29
|
+
window.setTimeout(() => toggleCopied(), 1200);
|
|
30
|
+
}, [value, toggleCopied]);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Box
|
|
34
|
+
radius="xl"
|
|
35
|
+
padding={{ x: 4, y: 2 }}
|
|
36
|
+
className={cn('ring-1 ring-border/50', isStorySection ? 'bg-surface' : 'bg-bg/80 ')}
|
|
37
|
+
>
|
|
38
|
+
<Flex gap={3} align="center" justify="between">
|
|
39
|
+
<FlexItem className="min-w-0">
|
|
40
|
+
<Text size="sm" color="ink" truncate>
|
|
41
|
+
{value}
|
|
42
|
+
</Text>
|
|
43
|
+
</FlexItem>
|
|
44
|
+
|
|
45
|
+
<FlexItem>
|
|
46
|
+
<button
|
|
47
|
+
type="button"
|
|
48
|
+
onClick={onCopy}
|
|
49
|
+
className="shrink-0 inline-flex size-9 items-center justify-center rounded-lg bg-black/5 text-ink hover:bg-black/10 transition-colors cursor-pointer"
|
|
50
|
+
aria-label={copied ? 'Copied' : 'Copy command'}
|
|
51
|
+
title={copied ? 'Copied' : 'Copy'}
|
|
52
|
+
>
|
|
53
|
+
{copied ? <PiCheckBold className="size-4" /> : <PiCopySimpleBold className="size-4" />}
|
|
54
|
+
</button>
|
|
55
|
+
</FlexItem>
|
|
56
|
+
</Flex>
|
|
57
|
+
</Box>
|
|
58
|
+
);
|
|
59
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { cn } from '@/shared/libs';
|
|
3
|
+
import { Box } from '@/shared/ui';
|
|
4
|
+
|
|
5
|
+
interface DividerProps {
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const Divider = memo(({ className = '' }: DividerProps) => {
|
|
10
|
+
return <Box className={cn('h-px w-full bg-border/70', className)} />;
|
|
11
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { Box, Flex, FlexItem, Text } from '@/shared/ui';
|
|
3
|
+
import { Links } from '../constants';
|
|
4
|
+
import { Divider } from './Divider';
|
|
5
|
+
|
|
6
|
+
export const Footer = memo(() => {
|
|
7
|
+
return (
|
|
8
|
+
<Box className="mt-12 pt-8">
|
|
9
|
+
<Divider className="mb-6" />
|
|
10
|
+
|
|
11
|
+
<Flex
|
|
12
|
+
gap={4}
|
|
13
|
+
align={{ md: 'center' }}
|
|
14
|
+
justify={{ md: 'between' }}
|
|
15
|
+
direction={{ base: 'column', md: 'row' }}
|
|
16
|
+
>
|
|
17
|
+
<FlexItem>
|
|
18
|
+
<Text size="sm" color="muted">
|
|
19
|
+
<span className="font-semibold text-ink">React Scaffold</span>
|
|
20
|
+
<>
|
|
21
|
+
<span className="mx-2">•</span>
|
|
22
|
+
Created by{' '}
|
|
23
|
+
<a
|
|
24
|
+
href={Links.CreatorGithub}
|
|
25
|
+
target="_blank"
|
|
26
|
+
rel="noreferrer"
|
|
27
|
+
className="font-semibold text-ink hover:text-primary transition-colors"
|
|
28
|
+
>
|
|
29
|
+
{Links.CreatorName}
|
|
30
|
+
</a>
|
|
31
|
+
</>
|
|
32
|
+
</Text>
|
|
33
|
+
</FlexItem>
|
|
34
|
+
|
|
35
|
+
<FlexItem>
|
|
36
|
+
<Flex gap={5} className="flex-wrap text-sm font-medium">
|
|
37
|
+
<a
|
|
38
|
+
className="text-muted hover:text-primary"
|
|
39
|
+
href={Links.AppGithub}
|
|
40
|
+
target="_blank"
|
|
41
|
+
rel="noreferrer"
|
|
42
|
+
>
|
|
43
|
+
Project Repo
|
|
44
|
+
</a>
|
|
45
|
+
<a
|
|
46
|
+
className="text-muted hover:text-primary"
|
|
47
|
+
href={Links.CliGithub}
|
|
48
|
+
target="_blank"
|
|
49
|
+
rel="noreferrer"
|
|
50
|
+
>
|
|
51
|
+
CLI Repo
|
|
52
|
+
</a>
|
|
53
|
+
<a
|
|
54
|
+
className="text-muted hover:text-primary"
|
|
55
|
+
href={Links.NpmPackage}
|
|
56
|
+
target="_blank"
|
|
57
|
+
rel="noreferrer"
|
|
58
|
+
>
|
|
59
|
+
NPM Package
|
|
60
|
+
</a>
|
|
61
|
+
|
|
62
|
+
<>
|
|
63
|
+
<span className="text-border">|</span>
|
|
64
|
+
<a
|
|
65
|
+
className="text-muted hover:text-primary"
|
|
66
|
+
href={Links.CreatorLinkedIn}
|
|
67
|
+
target="_blank"
|
|
68
|
+
rel="noreferrer"
|
|
69
|
+
>
|
|
70
|
+
Creator
|
|
71
|
+
</a>
|
|
72
|
+
</>
|
|
73
|
+
</Flex>
|
|
74
|
+
</FlexItem>
|
|
75
|
+
</Flex>
|
|
76
|
+
</Box>
|
|
77
|
+
);
|
|
78
|
+
});
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { Box, Button, Flex, FlexItem, Grid, Text } from '@/shared/ui';
|
|
3
|
+
import { FaGithub, FaLinkedin, FaNpm } from 'react-icons/fa';
|
|
4
|
+
import { QuickStartPanel } from './QuickStartPanel';
|
|
5
|
+
import { Links } from '../constants';
|
|
6
|
+
import { IconLink } from './IconLink';
|
|
7
|
+
|
|
8
|
+
export const Hero = memo(() => {
|
|
9
|
+
return (
|
|
10
|
+
<Grid
|
|
11
|
+
align="start"
|
|
12
|
+
gap={{ base: 10, md: 12 }}
|
|
13
|
+
className="md:grid-cols-[1.25fr_0.75fr] md:gap-12"
|
|
14
|
+
>
|
|
15
|
+
<Flex gap={5} direction="column">
|
|
16
|
+
<Box padding={{ x: 3, y: 1 }} width="fit" className="rounded-full bg-badge">
|
|
17
|
+
<Flex gap={2} align="center">
|
|
18
|
+
<FlexItem>
|
|
19
|
+
<Box radius="full" className="size-1.5 bg-primary" />
|
|
20
|
+
</FlexItem>
|
|
21
|
+
<FlexItem>
|
|
22
|
+
<Text size="sm" weight="semibold">
|
|
23
|
+
React Scaffold
|
|
24
|
+
</Text>
|
|
25
|
+
</FlexItem>
|
|
26
|
+
</Flex>
|
|
27
|
+
</Box>
|
|
28
|
+
|
|
29
|
+
<Flex gap={2} align="center" className="-mt-2">
|
|
30
|
+
<FlexItem>
|
|
31
|
+
<Text size="sm" color="muted">
|
|
32
|
+
Created by
|
|
33
|
+
</Text>
|
|
34
|
+
</FlexItem>
|
|
35
|
+
<FlexItem>
|
|
36
|
+
<a
|
|
37
|
+
href={Links.CreatorGithub}
|
|
38
|
+
target="_blank"
|
|
39
|
+
rel="noreferrer"
|
|
40
|
+
className="font-semibold text-ink hover:text-primary transition-colors"
|
|
41
|
+
>
|
|
42
|
+
{Links.CreatorName}
|
|
43
|
+
</a>
|
|
44
|
+
</FlexItem>
|
|
45
|
+
</Flex>
|
|
46
|
+
|
|
47
|
+
<FlexItem>
|
|
48
|
+
<Text
|
|
49
|
+
weight="black"
|
|
50
|
+
font="plus-jakarta-sans"
|
|
51
|
+
size={{ base: '4xl', lg: '5xl', '2xl': '6xl' }}
|
|
52
|
+
className="tracking-tight bg-linear-to-r from-ink to-primary bg-clip-text text-transparent"
|
|
53
|
+
>
|
|
54
|
+
Start building with React Scaffold
|
|
55
|
+
</Text>
|
|
56
|
+
</FlexItem>
|
|
57
|
+
|
|
58
|
+
<FlexItem>
|
|
59
|
+
<Text size="base" color="muted" className="max-w-xl text-base leading-relaxed">
|
|
60
|
+
A modern, opinionated starter that keeps your codebase clean as it grows — feature-first
|
|
61
|
+
structure, shared UI primitives, and best-practice tooling out of the box.
|
|
62
|
+
</Text>
|
|
63
|
+
</FlexItem>
|
|
64
|
+
|
|
65
|
+
<FlexItem>
|
|
66
|
+
<Text size="sm" color="muted" className="max-w-xl">
|
|
67
|
+
This is the default starter page. Replace it with your product UI when you’re ready.
|
|
68
|
+
</Text>
|
|
69
|
+
</FlexItem>
|
|
70
|
+
|
|
71
|
+
<FlexItem>
|
|
72
|
+
<Flex gap={3} direction={{ base: 'column', md: 'row' }} className="mt-2">
|
|
73
|
+
<FlexItem>
|
|
74
|
+
<Button
|
|
75
|
+
className="rounded-full"
|
|
76
|
+
onClick={() => window.open(Links.AppGithub, '_blank', 'noopener,noreferrer')}
|
|
77
|
+
>
|
|
78
|
+
Open Project Repo
|
|
79
|
+
</Button>
|
|
80
|
+
</FlexItem>
|
|
81
|
+
|
|
82
|
+
<FlexItem>
|
|
83
|
+
<Button
|
|
84
|
+
variant="outline"
|
|
85
|
+
className="rounded-full"
|
|
86
|
+
onClick={() => window.open(Links.NpmPackage, '_blank', 'noopener,noreferrer')}
|
|
87
|
+
>
|
|
88
|
+
View NPM Package
|
|
89
|
+
</Button>
|
|
90
|
+
</FlexItem>
|
|
91
|
+
</Flex>
|
|
92
|
+
</FlexItem>
|
|
93
|
+
|
|
94
|
+
<FlexItem>
|
|
95
|
+
<Flex gap={2} align="center">
|
|
96
|
+
<FlexItem>
|
|
97
|
+
<IconLink
|
|
98
|
+
href={Links.AppGithub}
|
|
99
|
+
label="App Repo"
|
|
100
|
+
icon={<FaGithub className="size-4" />}
|
|
101
|
+
/>
|
|
102
|
+
</FlexItem>
|
|
103
|
+
<FlexItem>
|
|
104
|
+
<IconLink
|
|
105
|
+
href={Links.CliGithub}
|
|
106
|
+
label="CLI Repo"
|
|
107
|
+
icon={<FaGithub className="size-4" />}
|
|
108
|
+
/>
|
|
109
|
+
</FlexItem>
|
|
110
|
+
<FlexItem>
|
|
111
|
+
<IconLink href={Links.NpmPackage} label="NPM" icon={<FaNpm className="size-4" />} />
|
|
112
|
+
</FlexItem>
|
|
113
|
+
|
|
114
|
+
<>
|
|
115
|
+
<span className="text-border -mt-1">•</span>
|
|
116
|
+
<FlexItem>
|
|
117
|
+
<IconLink
|
|
118
|
+
href={Links.CreatorLinkedIn}
|
|
119
|
+
label="Creator"
|
|
120
|
+
icon={<FaLinkedin className="size-4" />}
|
|
121
|
+
/>
|
|
122
|
+
</FlexItem>
|
|
123
|
+
</>
|
|
124
|
+
</Flex>
|
|
125
|
+
</FlexItem>
|
|
126
|
+
</Flex>
|
|
127
|
+
|
|
128
|
+
<QuickStartPanel />
|
|
129
|
+
</Grid>
|
|
130
|
+
);
|
|
131
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { memo, ReactNode } from 'react';
|
|
2
|
+
import { Text } from '@/shared/ui';
|
|
3
|
+
|
|
4
|
+
interface IconLinkProps {
|
|
5
|
+
href: string;
|
|
6
|
+
icon: ReactNode;
|
|
7
|
+
label: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const IconLink = memo(({ href, icon, label }: IconLinkProps) => {
|
|
11
|
+
return (
|
|
12
|
+
<a
|
|
13
|
+
href={href}
|
|
14
|
+
target="_blank"
|
|
15
|
+
rel="noreferrer"
|
|
16
|
+
className="inline-flex items-center gap-2 text-muted hover:text-primary transition-colors"
|
|
17
|
+
>
|
|
18
|
+
<span className="text-primary">{icon}</span>
|
|
19
|
+
<Text size="sm" className="leading-none">
|
|
20
|
+
{label}
|
|
21
|
+
</Text>
|
|
22
|
+
</a>
|
|
23
|
+
);
|
|
24
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { memo, ReactNode } from 'react';
|
|
2
|
+
import { Box, Flex, FlexItem, Text } from '@/shared/ui';
|
|
3
|
+
import { RingSoft } from './RingSoft';
|
|
4
|
+
import { CodeLine } from './CodeLine';
|
|
5
|
+
import { Commands } from '../constants';
|
|
6
|
+
|
|
7
|
+
function StepLight({ label, children }: { label: string; children: ReactNode }) {
|
|
8
|
+
return (
|
|
9
|
+
<Box>
|
|
10
|
+
<Text className="text-sm font-semibold text-ink">{label}</Text>
|
|
11
|
+
<Box className="mt-2">{children}</Box>
|
|
12
|
+
</Box>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const QuickStartPanel = memo(() => {
|
|
17
|
+
return (
|
|
18
|
+
<Box className="relative overflow-hidden rounded-2xl bg-surface/70 p-6 md:p-7 ring-1 ring-border">
|
|
19
|
+
<Box className="pointer-events-none absolute inset-0 opacity-100">
|
|
20
|
+
<RingSoft className="right-[-7rem] top-[-6rem]" size="22rem" />
|
|
21
|
+
<RingSoft className="right-[-5rem] top-[9rem]" size="14rem" />
|
|
22
|
+
<RingSoft className="right-[-3.5rem] top-[16rem]" size="6rem" />
|
|
23
|
+
</Box>
|
|
24
|
+
|
|
25
|
+
<Box className="relative z-10 space-y-6">
|
|
26
|
+
<Flex gap={1} direction="column">
|
|
27
|
+
<FlexItem>
|
|
28
|
+
<Text
|
|
29
|
+
size="base"
|
|
30
|
+
weight="bold"
|
|
31
|
+
font="plus-jakarta-sans"
|
|
32
|
+
className="tracking-tight bg-linear-to-r from-primary to-ink bg-clip-text text-transparent"
|
|
33
|
+
>
|
|
34
|
+
Quick start
|
|
35
|
+
</Text>
|
|
36
|
+
</FlexItem>
|
|
37
|
+
<FlexItem>
|
|
38
|
+
<Text size="sm" color="muted">
|
|
39
|
+
Create an app in seconds — clean architecture, ready to scale.
|
|
40
|
+
</Text>
|
|
41
|
+
</FlexItem>
|
|
42
|
+
</Flex>
|
|
43
|
+
|
|
44
|
+
<Flex gap={4} direction="column">
|
|
45
|
+
<FlexItem>
|
|
46
|
+
<StepLight label="1) Scaffold">
|
|
47
|
+
<CodeLine value={Commands.Scaffold} />
|
|
48
|
+
</StepLight>
|
|
49
|
+
</FlexItem>
|
|
50
|
+
<FlexItem>
|
|
51
|
+
<StepLight label="2) Run">
|
|
52
|
+
<CodeLine value={Commands.Run} />
|
|
53
|
+
</StepLight>
|
|
54
|
+
</FlexItem>
|
|
55
|
+
</Flex>
|
|
56
|
+
|
|
57
|
+
<Text size="sm" color="muted">
|
|
58
|
+
Tip: add your first feature inside your feature modules and delete this page when ready.
|
|
59
|
+
</Text>
|
|
60
|
+
</Box>
|
|
61
|
+
</Box>
|
|
62
|
+
);
|
|
63
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { memo } from 'react';
|
|
2
|
+
import { Box } from '@/shared/ui/Box';
|
|
3
|
+
import { cn } from '@/shared/libs';
|
|
4
|
+
|
|
5
|
+
interface RingSoftProps {
|
|
6
|
+
className?: string;
|
|
7
|
+
size: string | number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const RingSoft = memo(({ className, size }: RingSoftProps) => {
|
|
11
|
+
return (
|
|
12
|
+
<Box
|
|
13
|
+
className={cn('absolute rounded-full', className)}
|
|
14
|
+
style={{
|
|
15
|
+
width: size,
|
|
16
|
+
height: size,
|
|
17
|
+
border: '1px solid rgba(52, 82, 255, 0.22)',
|
|
18
|
+
}}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
});
|