Skip to content

Commit 827e022

Browse files
author
Ahmad Zani Syechkar
committed
update: adding feature from uploadImage and remove smth useless
1 parent c3f2002 commit 827e022

8 files changed

Lines changed: 201 additions & 33 deletions

File tree

server/server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ app.use(
2020
);
2121

2222
// Middleware JSON parser
23+
app.use(express.json());
2324

2425
// Connect database
2526
connectDB();
7.47 KB
Loading

src/components/inputs/ProfilePhotoSelector.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,13 @@ export default function ProfilePhotoSelector({
6868
</div>
6969
) : (
7070
<div className="relative">
71-
<img
72-
src={previewURL}
73-
alt="Selected profile photo preview"
74-
className="w-20 h-20 rounded-full object-cover"
75-
/>
71+
{previewURL && (
72+
<img
73+
src={previewURL}
74+
alt="Selected profile photo preview"
75+
className="w-20 h-20 rounded-full object-cover"
76+
/>
77+
)}
7678
<button
7779
type="button"
7880
onClick={handleRemoveImage}

src/context/userContext.tsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import React, { createContext, useState, useEffect } from "react";
2+
import type { ReactNode } from "react";
3+
import axiosInstance from "../utils/axiosInstance";
4+
import { API_PATHS } from "../utils/ApiPaths";
5+
6+
// Definisikan tipe user sesuai struktur yang dikembalikan dari API
7+
interface User {
8+
id: number;
9+
name: string;
10+
email: string;
11+
token: string;
12+
// tambahkan properti lain jika ada
13+
}
14+
15+
// Definisikan tipe konteks
16+
interface UserContextType {
17+
user: User | null;
18+
loading: boolean;
19+
updateUser: (userData: User) => void;
20+
clearUser: () => void;
21+
}
22+
23+
export const UserContext = createContext<UserContextType | undefined>(undefined);
24+
25+
// Props untuk provider
26+
interface UserProviderProps {
27+
children: ReactNode;
28+
}
29+
30+
export default function UserProvider({ children }: UserProviderProps) {
31+
const [user, setUser] = useState<User | null>(null);
32+
const [loading, setLoading] = useState<boolean>(true);
33+
34+
useEffect(() => {
35+
if (user) return;
36+
37+
const accessToken = localStorage.getItem("token");
38+
39+
if (!accessToken) {
40+
setLoading(false);
41+
return;
42+
}
43+
44+
const fetchUser = async (): Promise<void> => {
45+
try {
46+
const response = await axiosInstance.get<User>(API_PATHS.AUTH.GET_PROFILE);
47+
setUser(response.data);
48+
} catch (err) {
49+
console.error(`User not authenticated`, err);
50+
clearUser();
51+
} finally {
52+
setLoading(false);
53+
}
54+
};
55+
56+
fetchUser();
57+
}, [user]);
58+
59+
const updateUser = (userData: User) => {
60+
setUser(userData);
61+
localStorage.setItem("token", userData.token);
62+
setLoading(false);
63+
};
64+
65+
const clearUser = () => {
66+
setUser(null);
67+
localStorage.removeItem("token");
68+
};
69+
70+
return (
71+
<UserContext.Provider value={{ user, loading, updateUser, clearUser }}>
72+
{children}
73+
</UserContext.Provider>
74+
);
75+
}

src/routes/Router.tsx

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React from 'react'
1+
import React, { useContext } from 'react'
2+
import { Link, Navigate, Outlet } from 'react-router-dom';
23
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
34
import PrivateRouter from './PrivateRouter';
45

@@ -12,32 +13,46 @@ import ManageTasks from '../views/Admin/MangeTasks'
1213
import DashboardUser from '../views/Users/DashboardUser'
1314
import MyTasks from '../views/Users/MyTasks'
1415
import ViewTaskDetails from '../views/Users/ViewTaskDetails'
16+
import UserProvider, { UserContext } from '../context/userContext';
1517

1618
export default function AppRouter() {
1719
return (
18-
<Router>
19-
<Routes>
20-
<Route path='/' element={ <Index /> }/>
21-
<Route path='/login' element={<Login />}/>
22-
<Route path='/signup' element={<SignUp />}></Route>
20+
<UserProvider>
21+
<Router>
22+
<Routes>
23+
<Route path='/' element={ <Root /> }/>
24+
<Route path='/login' element={<Login />}/>
25+
<Route path='/signup' element={<SignUp />}></Route>
2326

24-
<Route element={<PrivateRouter allowedRoles={["admin"]} />} >
25-
<Route path='/admin/dashboard' element={<DashboardAdmin />}/>
26-
<Route path='/admin/create-task' element={<CreateTask />}/>
27-
<Route path='/admin/manage-user' element={<ManageUser />}/>
28-
<Route path='/admin/manage-task' element={<ManageTasks />}/>
29-
</Route>
27+
<Route element={<PrivateRouter allowedRoles={["admin"]} />} >
28+
<Route path='/admin/dashboard' element={<DashboardAdmin />}/>
29+
<Route path='/admin/create-task' element={<CreateTask />}/>
30+
<Route path='/admin/manage-user' element={<ManageUser />}/>
31+
<Route path='/admin/manage-task' element={<ManageTasks />}/>
32+
</Route>
3033

31-
<Route element={<PrivateRouter allowedRoles={["users"] } />}>
32-
<Route path='/user/dashboard' element={<DashboardUser />} />
33-
<Route path='/task' element={<MyTasks />} />
34-
<Route path='/task-detail/:id' element={<ViewTaskDetails />}/>
35-
{/*
36-
<Route path='/purchase' element={<DashboardUser />} />
37-
<Route path='/inventory' element={<DashboardUser />} />
38-
*/}
39-
</Route>
40-
</Routes>
41-
</Router>
34+
<Route element={<PrivateRouter allowedRoles={["users"] } />}>
35+
<Route path='/user/dashboard' element={<DashboardUser />} />
36+
<Route path='/task' element={<MyTasks />} />
37+
<Route path='/task-detail/:id' element={<ViewTaskDetails />}/>
38+
{/*
39+
<Route path='/purchase' element={<DashboardUser />} />
40+
<Route path='/inventory' element={<DashboardUser />} />
41+
*/}
42+
</Route>
43+
</Routes>
44+
</Router>
45+
</UserProvider>
4246
)
47+
}
48+
49+
const Root = () => {
50+
const { user, loading } = useContext(UserContext);
51+
52+
if (loading) return <Outlet />;
53+
if (!user) {
54+
return <Navigate to="/login"/>
55+
}
56+
57+
return user.role === "admin" ? <Navigate to="/admin/dashboard"/> : <Navigate to="/user/dashboard"/>
4358
}

src/utils/upload.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { API_PATHS } from "./ApiPaths";
2+
import axiosInstance from "./axiosInstance";
3+
4+
const uploadImage = async (imageFile: any) => {
5+
const formData = new FormData();
6+
7+
formData.append('image', imageFile);
8+
9+
try {
10+
const response = await axiosInstance.post(API_PATHS.IMAGE.UPLOAD_IMAGE, formData, {
11+
headers: {
12+
'Content-Type': 'multipart/form-data'
13+
},
14+
});
15+
16+
return response.data;
17+
} catch(err: any) {
18+
console.error(`Error uploading the image ${err}`);
19+
throw err;
20+
}
21+
}
22+
23+
export default uploadImage;

src/views/auth/Login.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react'
1+
import React, { useContext, useState } from 'react'
22
import { Link, useNavigate } from 'react-router-dom';
33

44
import Form from '../../components/Form';
@@ -9,6 +9,7 @@ import AuthLayout from '../../components/layout/AuthLayout';
99
import axiosInstance from '../../utils/axiosInstance';
1010
import { validateEmail } from '../../utils/helper';
1111
import { API_PATHS } from '../../utils/ApiPaths';
12+
import { UserContext } from '../../context/userContext';
1213

1314
export default function Login() {
1415
const [email, setEmail] = useState("");
@@ -24,6 +25,12 @@ export default function Login() {
2425
setTimeout(() => setIsShaking(false), 400);
2526
};
2627

28+
const context = useContext(UserContext);
29+
if (!context) {
30+
throw new Error("UserContext must be used within a UserProvider");
31+
}
32+
const { updateUser } = context;
33+
2734
const navigate = useNavigate();
2835

2936
const handleLogin = async (e: any): Promise<void> => {
@@ -53,6 +60,7 @@ export default function Login() {
5360
if (token) {
5461
localStorage.setItem("token", token);
5562

63+
updateUser(response.data);
5664
if (user.role === "admin") {
5765
navigate("/admin/dashboard");
5866
} else {

src/views/auth/SignUp.tsx

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1-
import React, { useState } from 'react'
2-
import { Link } from 'react-router-dom';
1+
import React, { useContext, useState } from 'react'
2+
import { Link, useNavigate } from 'react-router-dom';
33

44
import Form from '../../components/Form';
55
import AuthLayout from '../../components/layout/AuthLayout';
66
import ProfilePhotoSelector from '../../components/inputs/ProfilePhotoSelector';
77
import Input from '../../components/Input';
88
import Button from '../../components/Button';
9+
import axiosInstance from '../../utils/axiosInstance';
10+
import { API_PATHS } from '../../utils/ApiPaths';
11+
import { UserContext } from '../../context/userContext';
12+
import uploadImage from '../../utils/upload';
913

1014
export default function SignUp() {
1115
const [profilePic, setProfilePic] = useState<File | null>(null);
@@ -15,6 +19,13 @@ export default function SignUp() {
1519
const [buttonText, setButtonText] = useState("sign up");
1620
const [adminInviteToken, setAdminInviteToken] = useState("");
1721

22+
const navigate = useNavigate();
23+
24+
const context = useContext(UserContext);
25+
if (!context) {
26+
throw new Error("UserContext must be used within a UserProvider");
27+
}
28+
const { updateUser } = context;
1829
const [error, setError] = useState("");
1930
const [isShaking, setIsShaking] = useState(false);
2031

@@ -27,6 +38,8 @@ export default function SignUp() {
2738
const handleSignUp = async (e: any): Promise<void> => {
2839
e.preventDefault();
2940

41+
let setProfilePicture = '';
42+
3043
if (!fullname) {
3144
triggerError("Please enter fullname");
3245
return;
@@ -45,9 +58,40 @@ export default function SignUp() {
4558
setError("");
4659

4760
try {
48-
61+
if (profilePic) {
62+
const imgUploadRes = await uploadImage(profilePic);
63+
setProfilePicture = imgUploadRes.imageUrl || "";
64+
}
65+
66+
const response = await axiosInstance.post(API_PATHS.AUTH.REGISTER, {
67+
name: fullname,
68+
email,
69+
password,
70+
setProfilePicture,
71+
adminInviteToken
72+
});
73+
74+
const { token, user } = response.data;
75+
76+
if (token) {
77+
localStorage.setItem("token", token);
78+
updateUser(response.data);
79+
80+
if (user.role === "admin") {
81+
navigate("/admin/dashboard");
82+
} else {
83+
navigate("/user/dashboard");
84+
}
85+
}
86+
4987
} catch (err: any) {
50-
88+
if (err.response && err.response.data.message) {
89+
triggerError(err.response.data.message);
90+
} else {
91+
triggerError("Something went wrong. Please try again.");
92+
}
93+
} finally {
94+
setButtonText("sign up");
5195
}
5296
}
5397

0 commit comments

Comments
 (0)