create-modern-react 1.0.0 → 2.0.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 +270 -72
- package/bin/index.js +13 -13
- package/lib/install.js +103 -32
- package/lib/prompts.js +152 -179
- package/lib/setup.js +267 -159
- package/package.json +17 -8
- package/templates/base/.env.example +9 -0
- package/templates/base/.eslintrc.cjs +37 -0
- package/templates/base/.prettierrc +11 -0
- package/templates/base/components.json +17 -0
- package/templates/base/index.html +2 -1
- package/templates/base/package.json +33 -14
- package/templates/base/postcss.config.js +6 -0
- package/templates/base/src/App.tsx +5 -18
- package/templates/base/src/components/layout/error-boundary.tsx +60 -0
- package/templates/base/src/components/layout/index.ts +2 -0
- package/templates/base/src/components/layout/root-layout.tsx +36 -0
- package/templates/base/src/components/ui/button.tsx +55 -0
- package/templates/base/src/components/ui/card.tsx +85 -0
- package/templates/base/src/components/ui/index.ts +12 -0
- package/templates/base/src/components/ui/input.tsx +24 -0
- package/templates/base/src/components/ui/separator.tsx +29 -0
- package/templates/base/src/components/ui/skeleton.tsx +15 -0
- package/templates/base/src/hooks/index.ts +3 -0
- package/templates/base/src/hooks/use-cancel-token.ts +63 -0
- package/templates/base/src/hooks/use-debounce.ts +29 -0
- package/templates/base/src/hooks/use-loader.ts +39 -0
- package/templates/base/src/index.css +73 -60
- package/templates/base/src/lib/utils.ts +14 -0
- package/templates/base/src/main.tsx +6 -6
- package/templates/base/src/providers/index.tsx +27 -0
- package/templates/base/src/providers/theme-provider.tsx +92 -0
- package/templates/base/src/routes/index.tsx +40 -0
- package/templates/base/src/routes/routes.ts +36 -0
- package/templates/base/src/screens/home/index.tsx +132 -0
- package/templates/base/src/screens/not-found/index.tsx +29 -0
- package/templates/base/src/services/alertify-services.ts +133 -0
- package/templates/base/src/services/api/api-helpers.ts +130 -0
- package/templates/base/src/services/api/axios-instance.ts +77 -0
- package/templates/base/src/services/api/index.ts +9 -0
- package/templates/base/src/services/index.ts +2 -0
- package/templates/base/src/types/index.ts +55 -0
- package/templates/base/src/vite-env.d.ts +31 -0
- package/templates/base/tailwind.config.js +77 -0
- package/templates/base/tsconfig.json +4 -3
- package/templates/base/tsconfig.node.json +22 -0
- package/templates/base/vite.config.ts +65 -4
- package/templates/optional/antd/config-provider.tsx +33 -0
- package/templates/optional/antd/index.ts +2 -0
- package/templates/optional/antd/styles/antd-overrides.css +104 -0
- package/templates/optional/antd/theme.ts +75 -0
- package/templates/optional/husky/.husky/pre-commit +1 -0
- package/templates/optional/husky/.lintstagedrc.json +6 -0
- package/templates/optional/redux/hooks.ts +17 -0
- package/templates/optional/redux/index.ts +13 -0
- package/templates/optional/redux/provider.tsx +33 -0
- package/templates/optional/redux/store/index.ts +45 -0
- package/templates/optional/redux/store/slices/app-slice.ts +62 -0
- package/templates/base/src/App.css +0 -14
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ThemeConfig } from 'antd';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ant Design theme configuration
|
|
5
|
+
* Customizes the design tokens to match your brand
|
|
6
|
+
*
|
|
7
|
+
* @see https://ant.design/docs/react/customize-theme
|
|
8
|
+
*/
|
|
9
|
+
export const antdTheme: ThemeConfig = {
|
|
10
|
+
token: {
|
|
11
|
+
// Primary colors
|
|
12
|
+
colorPrimary: '#1677ff',
|
|
13
|
+
colorSuccess: '#52c41a',
|
|
14
|
+
colorWarning: '#faad14',
|
|
15
|
+
colorError: '#ff4d4f',
|
|
16
|
+
colorInfo: '#1677ff',
|
|
17
|
+
|
|
18
|
+
// Border radius
|
|
19
|
+
borderRadius: 6,
|
|
20
|
+
borderRadiusLG: 8,
|
|
21
|
+
borderRadiusSM: 4,
|
|
22
|
+
|
|
23
|
+
// Font
|
|
24
|
+
fontFamily:
|
|
25
|
+
'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
|
|
26
|
+
fontSize: 14,
|
|
27
|
+
|
|
28
|
+
// Control height
|
|
29
|
+
controlHeight: 36,
|
|
30
|
+
controlHeightLG: 44,
|
|
31
|
+
controlHeightSM: 28,
|
|
32
|
+
|
|
33
|
+
// Motion
|
|
34
|
+
motionDurationFast: '0.1s',
|
|
35
|
+
motionDurationMid: '0.2s',
|
|
36
|
+
motionDurationSlow: '0.3s',
|
|
37
|
+
},
|
|
38
|
+
components: {
|
|
39
|
+
Button: {
|
|
40
|
+
primaryShadow: 'none',
|
|
41
|
+
defaultShadow: 'none',
|
|
42
|
+
},
|
|
43
|
+
Input: {
|
|
44
|
+
activeBorderColor: '#1677ff',
|
|
45
|
+
hoverBorderColor: '#4096ff',
|
|
46
|
+
},
|
|
47
|
+
Card: {
|
|
48
|
+
headerBg: 'transparent',
|
|
49
|
+
},
|
|
50
|
+
Table: {
|
|
51
|
+
headerBg: '#fafafa',
|
|
52
|
+
},
|
|
53
|
+
Menu: {
|
|
54
|
+
itemBg: 'transparent',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Dark theme configuration
|
|
61
|
+
*/
|
|
62
|
+
export const antdDarkTheme: ThemeConfig = {
|
|
63
|
+
...antdTheme,
|
|
64
|
+
token: {
|
|
65
|
+
...antdTheme.token,
|
|
66
|
+
// Override for dark mode
|
|
67
|
+
colorBgContainer: '#141414',
|
|
68
|
+
colorBgElevated: '#1f1f1f',
|
|
69
|
+
colorBgLayout: '#000000',
|
|
70
|
+
colorBorderSecondary: '#303030',
|
|
71
|
+
colorText: 'rgba(255, 255, 255, 0.85)',
|
|
72
|
+
colorTextSecondary: 'rgba(255, 255, 255, 0.65)',
|
|
73
|
+
colorTextTertiary: 'rgba(255, 255, 255, 0.45)',
|
|
74
|
+
},
|
|
75
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx lint-staged
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useDispatch, useSelector, type TypedUseSelectorHook } from 'react-redux';
|
|
2
|
+
import type { RootState, AppDispatch } from './store';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Typed version of useDispatch hook
|
|
6
|
+
* Use this instead of plain `useDispatch` for better type inference
|
|
7
|
+
*/
|
|
8
|
+
export const useAppDispatch = () => useDispatch<AppDispatch>();
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Typed version of useSelector hook
|
|
12
|
+
* Use this instead of plain `useSelector` for better type inference
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* const isLoading = useAppSelector((state) => state.app.isLoading);
|
|
16
|
+
*/
|
|
17
|
+
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { store, persistor, type RootState, type AppDispatch } from './store';
|
|
2
|
+
export { useAppDispatch, useAppSelector } from './hooks';
|
|
3
|
+
export { ReduxProvider } from './provider';
|
|
4
|
+
|
|
5
|
+
// Re-export slice actions
|
|
6
|
+
export {
|
|
7
|
+
setLoading,
|
|
8
|
+
toggleSidebar,
|
|
9
|
+
setSidebarOpen,
|
|
10
|
+
addNotification,
|
|
11
|
+
removeNotification,
|
|
12
|
+
clearNotifications,
|
|
13
|
+
} from './store/slices/app-slice';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Provider } from 'react-redux';
|
|
2
|
+
import { PersistGate } from 'redux-persist/integration/react';
|
|
3
|
+
import { store, persistor } from './store';
|
|
4
|
+
import { Skeleton } from '~/components/ui';
|
|
5
|
+
|
|
6
|
+
interface ReduxProviderProps {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function LoadingFallback() {
|
|
11
|
+
return (
|
|
12
|
+
<div className="flex min-h-screen items-center justify-center">
|
|
13
|
+
<div className="flex flex-col items-center gap-4">
|
|
14
|
+
<Skeleton className="h-12 w-12 rounded-full" />
|
|
15
|
+
<Skeleton className="h-4 w-32" />
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Redux Provider with persistence
|
|
23
|
+
* Wraps the application with Redux store and persist gate
|
|
24
|
+
*/
|
|
25
|
+
export function ReduxProvider({ children }: ReduxProviderProps) {
|
|
26
|
+
return (
|
|
27
|
+
<Provider store={store}>
|
|
28
|
+
<PersistGate loading={<LoadingFallback />} persistor={persistor}>
|
|
29
|
+
{children}
|
|
30
|
+
</PersistGate>
|
|
31
|
+
</Provider>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { configureStore, combineReducers } from '@reduxjs/toolkit';
|
|
2
|
+
import {
|
|
3
|
+
persistStore,
|
|
4
|
+
persistReducer,
|
|
5
|
+
FLUSH,
|
|
6
|
+
REHYDRATE,
|
|
7
|
+
PAUSE,
|
|
8
|
+
PERSIST,
|
|
9
|
+
PURGE,
|
|
10
|
+
REGISTER,
|
|
11
|
+
} from 'redux-persist';
|
|
12
|
+
import storage from 'redux-persist/lib/storage';
|
|
13
|
+
|
|
14
|
+
import appReducer from './slices/app-slice';
|
|
15
|
+
|
|
16
|
+
const rootReducer = combineReducers({
|
|
17
|
+
app: appReducer,
|
|
18
|
+
// Add more reducers here
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const persistConfig = {
|
|
22
|
+
key: 'root',
|
|
23
|
+
version: 1,
|
|
24
|
+
storage,
|
|
25
|
+
whitelist: ['app'], // Only persist these reducers
|
|
26
|
+
// blacklist: [], // Don't persist these reducers
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const persistedReducer = persistReducer(persistConfig, rootReducer);
|
|
30
|
+
|
|
31
|
+
export const store = configureStore({
|
|
32
|
+
reducer: persistedReducer,
|
|
33
|
+
middleware: (getDefaultMiddleware) =>
|
|
34
|
+
getDefaultMiddleware({
|
|
35
|
+
serializableCheck: {
|
|
36
|
+
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
|
|
37
|
+
},
|
|
38
|
+
}),
|
|
39
|
+
devTools: import.meta.env.DEV,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
export const persistor = persistStore(store);
|
|
43
|
+
|
|
44
|
+
export type RootState = ReturnType<typeof store.getState>;
|
|
45
|
+
export type AppDispatch = typeof store.dispatch;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|
2
|
+
|
|
3
|
+
interface AppState {
|
|
4
|
+
isLoading: boolean;
|
|
5
|
+
sidebarOpen: boolean;
|
|
6
|
+
notifications: Notification[];
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface Notification {
|
|
10
|
+
id: string;
|
|
11
|
+
type: 'success' | 'error' | 'warning' | 'info';
|
|
12
|
+
message: string;
|
|
13
|
+
timestamp: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const initialState: AppState = {
|
|
17
|
+
isLoading: false,
|
|
18
|
+
sidebarOpen: true,
|
|
19
|
+
notifications: [],
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const appSlice = createSlice({
|
|
23
|
+
name: 'app',
|
|
24
|
+
initialState,
|
|
25
|
+
reducers: {
|
|
26
|
+
setLoading: (state, action: PayloadAction<boolean>) => {
|
|
27
|
+
state.isLoading = action.payload;
|
|
28
|
+
},
|
|
29
|
+
toggleSidebar: (state) => {
|
|
30
|
+
state.sidebarOpen = !state.sidebarOpen;
|
|
31
|
+
},
|
|
32
|
+
setSidebarOpen: (state, action: PayloadAction<boolean>) => {
|
|
33
|
+
state.sidebarOpen = action.payload;
|
|
34
|
+
},
|
|
35
|
+
addNotification: (state, action: PayloadAction<Omit<Notification, 'id' | 'timestamp'>>) => {
|
|
36
|
+
state.notifications.push({
|
|
37
|
+
...action.payload,
|
|
38
|
+
id: crypto.randomUUID(),
|
|
39
|
+
timestamp: Date.now(),
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
removeNotification: (state, action: PayloadAction<string>) => {
|
|
43
|
+
state.notifications = state.notifications.filter(
|
|
44
|
+
(n) => n.id !== action.payload
|
|
45
|
+
);
|
|
46
|
+
},
|
|
47
|
+
clearNotifications: (state) => {
|
|
48
|
+
state.notifications = [];
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export const {
|
|
54
|
+
setLoading,
|
|
55
|
+
toggleSidebar,
|
|
56
|
+
setSidebarOpen,
|
|
57
|
+
addNotification,
|
|
58
|
+
removeNotification,
|
|
59
|
+
clearNotifications,
|
|
60
|
+
} = appSlice.actions;
|
|
61
|
+
|
|
62
|
+
export default appSlice.reducer;
|