@anmol0493/fullstack-app 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/frontend/src/components/Layout.tsx +13 -5
- package/frontend/src/lib/utils.ts +16 -16
- package/frontend/src/redux/slice/auth.ts +5 -5
- package/frontend/vite.config.ts +20 -13
- package/package.json +1 -1
- package/scripts/setup.js +1 -1
package/README.md
CHANGED
|
@@ -15,10 +15,10 @@ This is a **full-stack template** designed to help developers quickly bootstrap
|
|
|
15
15
|
## Getting Started
|
|
16
16
|
|
|
17
17
|
### Prerequisites
|
|
18
|
-
- [Node.js](https://nodejs.org/) (
|
|
18
|
+
- [Node.js](https://nodejs.org/) (v20 or higher)
|
|
19
19
|
- [npm](https://www.npmjs.com/)
|
|
20
20
|
|
|
21
21
|
### Installation
|
|
22
22
|
- ```bash
|
|
23
23
|
npx @anmol0493/fullstack-app my-project
|
|
24
|
-
```
|
|
24
|
+
```
|
|
@@ -1,21 +1,29 @@
|
|
|
1
|
-
import { useSelector } from "react-redux";
|
|
2
|
-
import { actions, RootState } from "../redux/store";
|
|
3
|
-
import { Button } from "./ui/Button";
|
|
4
1
|
import { LogOut, TriangleAlert } from "lucide-react";
|
|
5
|
-
import { cn } from "../lib/utils";
|
|
6
|
-
import toast from "react-hot-toast";
|
|
7
2
|
import { useState } from "react";
|
|
3
|
+
import toast from "react-hot-toast";
|
|
4
|
+
import { useDispatch, useSelector } from "react-redux";
|
|
5
|
+
import { cn } from "../lib/utils";
|
|
6
|
+
import { authApi } from "../redux/api/auth";
|
|
7
|
+
import { actions, RootState } from "../redux/store";
|
|
8
|
+
import { Button } from "./ui/Button";
|
|
8
9
|
import { CommonAlertDialog } from "./ui/CommonAlertDialog";
|
|
9
10
|
|
|
10
11
|
interface LayoutProps {
|
|
11
12
|
children: React.ReactNode;
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
const apiArray = [authApi];
|
|
16
|
+
|
|
14
17
|
export const Layout = ({ children }: LayoutProps) => {
|
|
15
18
|
const { currentUser } = useSelector((state: RootState) => state.auth);
|
|
16
19
|
const [isDialogOpen, setIsDialogOpen] = useState(false);
|
|
17
20
|
|
|
21
|
+
const dispatch = useDispatch();
|
|
22
|
+
|
|
18
23
|
const handleLogout = () => {
|
|
24
|
+
apiArray.forEach(api => {
|
|
25
|
+
dispatch(api.util.resetApiState());
|
|
26
|
+
});
|
|
19
27
|
actions.auth.clearToken(null);
|
|
20
28
|
toast.success("Logged out successfully");
|
|
21
29
|
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { clsx, type ClassValue } from "clsx";
|
|
2
|
-
import { twMerge } from "tailwind-merge";
|
|
3
1
|
import {
|
|
4
2
|
BaseQueryFn,
|
|
5
3
|
FetchArgs,
|
|
6
4
|
fetchBaseQuery,
|
|
7
5
|
FetchBaseQueryError
|
|
8
|
-
} from
|
|
9
|
-
import {
|
|
10
|
-
import
|
|
11
|
-
import
|
|
6
|
+
} from '@reduxjs/toolkit/query/react';
|
|
7
|
+
import { clsx, type ClassValue } from 'clsx';
|
|
8
|
+
import toast from 'react-hot-toast';
|
|
9
|
+
import { twMerge } from 'tailwind-merge';
|
|
10
|
+
import { clearToken } from '../redux/slice/auth';
|
|
11
|
+
import { AUTH_TOKEN } from './constants';
|
|
12
12
|
|
|
13
13
|
export function cn(...inputs: ClassValue[]) {
|
|
14
14
|
return twMerge(clsx(inputs));
|
|
@@ -21,7 +21,7 @@ export const isAuth = (): boolean => {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
try {
|
|
24
|
-
const decoded = JSON.parse(atob(token.split(
|
|
24
|
+
const decoded = JSON.parse(atob(token.split('.')[1]));
|
|
25
25
|
const currentTime = Date.now() / 1000;
|
|
26
26
|
return decoded.exp > currentTime;
|
|
27
27
|
} catch (error) {
|
|
@@ -29,7 +29,7 @@ export const isAuth = (): boolean => {
|
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
31
|
|
|
32
|
-
const Headers = () => localStorage.getItem(AUTH_TOKEN) ||
|
|
32
|
+
const Headers = () => localStorage.getItem(AUTH_TOKEN) || '';
|
|
33
33
|
|
|
34
34
|
export const baseQueryInterceptor = (
|
|
35
35
|
baseUrl: string
|
|
@@ -37,7 +37,7 @@ export const baseQueryInterceptor = (
|
|
|
37
37
|
const baseQuery = fetchBaseQuery({
|
|
38
38
|
baseUrl: baseUrl,
|
|
39
39
|
prepareHeaders: (headers) => {
|
|
40
|
-
headers.set(
|
|
40
|
+
headers.set('Authorization', `Bearer ${Headers()}`);
|
|
41
41
|
return headers;
|
|
42
42
|
}
|
|
43
43
|
});
|
|
@@ -45,7 +45,7 @@ export const baseQueryInterceptor = (
|
|
|
45
45
|
return async (args, api, extraOptions) => {
|
|
46
46
|
const result = await baseQuery(args, api, extraOptions);
|
|
47
47
|
if (result.error && result.error.status === 401) {
|
|
48
|
-
|
|
48
|
+
api.dispatch(clearToken());
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
return result;
|
|
@@ -54,18 +54,18 @@ export const baseQueryInterceptor = (
|
|
|
54
54
|
|
|
55
55
|
export const handleError = async (error: any) => {
|
|
56
56
|
toast.error(
|
|
57
|
-
error?.data?.message ? error.data.message :
|
|
57
|
+
error?.data?.message ? error.data.message : 'Something went wrong'
|
|
58
58
|
);
|
|
59
59
|
};
|
|
60
60
|
|
|
61
61
|
export const handleSuccess = async (obj: any) => {
|
|
62
|
-
toast.success(obj?.message ? obj.message :
|
|
62
|
+
toast.success(obj?.message ? obj.message : 'Success');
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
export const handleValue = (row: any, field: any) => {
|
|
66
|
-
if (!field) return
|
|
67
|
-
if (typeof field ===
|
|
66
|
+
if (!field) return '-';
|
|
67
|
+
if (typeof field === 'function') return field(row) ?? '-';
|
|
68
68
|
return field
|
|
69
|
-
.split(
|
|
70
|
-
.reduce((acc: any, part: any) => acc && (acc[part] ??
|
|
69
|
+
.split('.')
|
|
70
|
+
.reduce((acc: any, part: any) => acc && (acc[part] ?? '-'), row);
|
|
71
71
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createSlice } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { createSlice } from '@reduxjs/toolkit';
|
|
2
|
+
import { AUTH_TOKEN } from '../../lib/constants';
|
|
3
|
+
import { User } from '../../types';
|
|
4
4
|
|
|
5
5
|
interface AuthState {
|
|
6
6
|
currentUser: null | User;
|
|
@@ -15,7 +15,7 @@ const initialState: AuthState = {
|
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
export const authSlice = createSlice({
|
|
18
|
-
name:
|
|
18
|
+
name: 'auth',
|
|
19
19
|
initialState,
|
|
20
20
|
reducers: {
|
|
21
21
|
setCurrentUser: (state, action) => {
|
|
@@ -35,5 +35,5 @@ export const authSlice = createSlice({
|
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
export const { setIsLoading, setCurrentUser } = authSlice.actions;
|
|
38
|
+
export const { setIsLoading, setCurrentUser, clearToken } = authSlice.actions;
|
|
39
39
|
export default authSlice.reducer;
|
package/frontend/vite.config.ts
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
import { defineConfig } from 'vite';
|
|
2
|
-
import react from '@vitejs/plugin-react-swc';
|
|
3
1
|
import tailwindcss from '@tailwindcss/vite';
|
|
2
|
+
import react from '@vitejs/plugin-react-swc';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { defineConfig, loadEnv } from 'vite';
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
export default defineConfig(({ mode }) => {
|
|
7
|
+
const env = loadEnv(mode, process.cwd(), '');
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
return {
|
|
10
|
+
plugins: [react(), tailwindcss()],
|
|
11
|
+
resolve: {
|
|
12
|
+
alias: {
|
|
13
|
+
'@': path.resolve(__dirname, './src')
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
server: {
|
|
17
|
+
proxy: {
|
|
18
|
+
'/api': {
|
|
19
|
+
target: env.VITE_API_URL,
|
|
20
|
+
changeOrigin: true,
|
|
21
|
+
rewrite: (path) => path.replace(/^\/api/, '')
|
|
22
|
+
}
|
|
16
23
|
}
|
|
17
24
|
}
|
|
18
|
-
}
|
|
25
|
+
};
|
|
19
26
|
});
|
package/package.json
CHANGED
package/scripts/setup.js
CHANGED
|
@@ -4,7 +4,7 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as path from 'path';
|
|
5
5
|
import enquirer from 'enquirer'; // Install enquirer: npm install enquirer
|
|
6
6
|
const { prompt } = enquirer;
|
|
7
|
-
|
|
7
|
+
import { execa } from 'execa'; // Install execa: npm install execa
|
|
8
8
|
import { fileURLToPath } from 'url';
|
|
9
9
|
import { dirname } from 'path';
|
|
10
10
|
|