diff --git a/src/App.jsx b/src/App.jsx index 4872856..659b307 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,44 +1,27 @@ import React from 'react'; import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; import SignIn from './pages/auth/SignIn'; +import SignUp from './pages/auth/Signup'; import { ProtectedRoute } from './ProtectedRoute'; import NotFound from './pages/blank/NotFound'; -import { getSessionData } from './components/Global/Formatter'; -// dashboard +// Dashboard import Home from './pages/home/Home'; import Blank from './pages/blank/Blank'; -// master +// Master import IndexDevice from './pages/master/device/IndexDevice'; -// Setting - const App = () => { - const session = getSessionData(); - // console.log(session); - - const isAdmin = - session?.user?.role_id != `${import.meta.env.VITE_ROLE_VENDOR}` && - session?.user?.role_id && - session?.user?.role_id != null && - session?.user?.role_id != 0; - return ( - + - {isAdmin ? ( - } /> - ) : ( - } /> - )} - + {/* Public Routes */} + } /> } /> + } /> + + {/* Protected Routes */} }> } /> } /> @@ -48,6 +31,7 @@ const App = () => { } /> + {/* Catch-all */} } /> diff --git a/src/ProtectedRoute.jsx b/src/ProtectedRoute.jsx index 1015c1f..f2e4811 100644 --- a/src/ProtectedRoute.jsx +++ b/src/ProtectedRoute.jsx @@ -1,20 +1,25 @@ import React from 'react'; import { Navigate, Outlet } from 'react-router-dom'; import MainLayout from './layout/MainLayout'; - -import { getSessionData } from './components/Global/Formatter'; +import { NotifAlert } from './components/Global/ToastNotif'; export const ProtectedRoute = () => { - const session = getSessionData(); - // console.log(session); + // cek token di localStorage + const token = localStorage.getItem('token'); + const isAuthenticated = !!token; - const isAuthenticated = session?.auth ?? false; - if (!isAuthenticated) { - return ; - } - return ( - - - - ); + if (!isAuthenticated) { + NotifAlert({ + icon: 'warning', + title: 'Session Habis', + message: 'Silahkan login terlebih dahulu', + }); + return ; + } + + return ( + + + + ); }; diff --git a/src/Utils/Auth/Logout.jsx b/src/Utils/Auth/Logout.jsx index 957d1aa..f610b52 100644 --- a/src/Utils/Auth/Logout.jsx +++ b/src/Utils/Auth/Logout.jsx @@ -1,7 +1,12 @@ -const handleLogOut = () => { - localStorage.removeItem('Auth'); - localStorage.removeItem('session'); +const handleLogOut = (navigate) => { + localStorage.removeItem('Auth'); + localStorage.removeItem('session'); + + if (navigate) { + navigate('/signin', { replace: true }); + } else { window.location.replace('/signin'); -} + } +}; export default handleLogOut; \ No newline at end of file diff --git a/src/Utils/Auth/SignIn.jsx b/src/Utils/Auth/SignIn.jsx index e038f6a..d67b81d 100644 --- a/src/Utils/Auth/SignIn.jsx +++ b/src/Utils/Auth/SignIn.jsx @@ -8,18 +8,14 @@ const handleSignIn = async (values) => { // return false if (response?.status == 200) { - /* you can change this according to your authentication protocol */ let token = JSON.stringify(response.data?.token); - let role = JSON.stringify(response.data?.user?.role_id); localStorage.setItem('token', token); response.data.auth = true; localStorage.setItem('session', encryptData(response?.data)); - if (role === `${import.meta.env.VITE_ROLE_VENDOR}`) { - window.location.replace('/dashboard/home-vendor'); - } else { - window.location.replace('/dashboard/home'); - } + + // langsung redirect ke dashboard utama + window.location.replace('/dashboard/home'); } else { NotifAlert({ icon: 'error', diff --git a/src/api/auth.jsx b/src/api/auth.jsx index 468e938..fd96460 100644 --- a/src/api/auth.jsx +++ b/src/api/auth.jsx @@ -10,20 +10,10 @@ const login = async (params) => { return response || []; }; -const uploadFile = async (formData) => { - const response = await RegistrationRequest({ - method: 'post', - prefix: 'file-upload', - params: formData, - headers: { 'Content-Type': 'multipart/form-data' }, - }); - return response || {}; -}; - const register = async (params) => { const response = await RegistrationRequest({ method: 'post', - prefix: 'register', + prefix: 'auth/register', params: params, headers: { 'Content-Type': 'application/json' }, }); @@ -49,4 +39,4 @@ const checkUsername = async (queryParams) => { }; -export { login, uploadFile, register, verifyRedirect, checkUsername }; +export { login, register, verifyRedirect, checkUsername }; diff --git a/src/components/Global/ApiRequest.jsx b/src/components/Global/ApiRequest.jsx index 8811919..20cf2ae 100644 --- a/src/components/Global/ApiRequest.jsx +++ b/src/components/Global/ApiRequest.jsx @@ -1,127 +1,92 @@ -import axios from 'axios'; -import Swal from 'sweetalert2'; +import axios from "axios"; +import Swal from "sweetalert2"; -async function ApiRequest( - urlParams = { method: 'GET', params: {}, url: '', prefix: '/', token: true } -) { - const baseURLDef = `${import.meta.env.VITE_API_SERVER}`; - const instance = axios.create({ - baseURL: urlParams.url ?? baseURLDef, +async function ApiRequest({ + method = "GET", + params = {}, + url = "", + prefix = "/", + token = true, +} = {}) { + const baseURL = url || import.meta.env.VITE_API_SERVER; + const instance = axios.create({ baseURL }); + + const isFormData = params instanceof FormData; + + // request config + const request = { + method, + url: prefix, + data: params, + headers: { + "Accept-Language": "en_US", + ...(isFormData ? {} : { "Content-Type": "application/json" }), + }, + }; + + // handle download (doc) + if (params === "doc") { + request.responseType = "blob"; + } + + // ambil token + const rawToken = + sessionStorage.getItem("token_redirect") || localStorage.getItem("token"); + + if (token && rawToken) { + const cleanToken = rawToken.replace(/"/g, ""); + instance.defaults.headers.common[ + "Authorization" + ] = `Bearer ${cleanToken}`; + } + + try { + const response = await instance(request); + return { ...response, error: false }; + } catch (error) { + const status = error?.response?.status || 500; + const message = + error?.response?.data?.message || error.message || "Something Wrong"; + + cekError(status, message); + return { ...error.response, error: true }; + } +} + +// global error handler +async function cekError(status, message = "") { + console.log("status code", status); + + if (status === 401) { + Swal.fire({ + icon: "warning", + title: "Peringatan", + text: `${message}, Silahkan login`, + }).then((result) => { + if (result.isConfirmed) { + localStorage.clear(); + location.replace("/signin"); + } }); - - const isFormData = urlParams.params instanceof FormData; - - const request = { - method: urlParams.method, - url: urlParams.prefix ?? '/', - data: urlParams.params, - // yang lama - // headers: { - // 'Content-Type': 'application/json', - // 'Accept-Language': 'en_US', - // }, - - // yang baru - headers: { - 'Accept-Language': 'en_US', - ...(isFormData ? {} : { 'Content-Type': 'application/json' }), - }, - }; - - if (urlParams.params === 'doc') { - request.responseType = 'arraybuffer'; - request.headers['Content-Type'] = 'blob'; - } - - // console.log(request); - - // console.log('prefix', urlParams.prefix); - - const tokenRedirect = sessionStorage.getItem('token_redirect'); - - let stringToken = ''; - - if (tokenRedirect !== null) { - stringToken = tokenRedirect; - // console.log(`sessionStorage: ${tokenRedirect}`); - } else { - stringToken = localStorage.getItem('token'); - // console.log(`localStorage: ${stringToken}`); - } - - if (urlParams.prefix !== 'auth/login') { - const tokenWithQuotes = stringToken; - const token = tokenWithQuotes.replace(/"/g, ''); - const AUTH_TOKEN = `Bearer ${token}`; - instance.defaults.headers.common['Authorization'] = AUTH_TOKEN; - } else if (urlParams.token == true) { - const tokenWithQuotes = stringToken; - const token = tokenWithQuotes.replace(/"/g, ''); - const AUTH_TOKEN = `Bearer ${token}`; - instance.defaults.headers.common['Authorization'] = AUTH_TOKEN; - } - - return await instance(request) - .then(function (response) { - const responseCustom = response; - responseCustom.error = false; - return responseCustom; - }) - .catch(function (error) { - console.log('error', error.toJSON()); - - const errorData = error.toJSON(); - - const respError = error.response ?? {}; - cekError( - errorData.status, - error?.response?.data?.message ?? errorData?.message ?? 'Something Wrong' - ); - respError.error = true; - return respError; - }); -} - -async function cekError(props, message = '') { - console.log('status code', props); - if (props === 401) { - Swal.fire({ - icon: 'warning', - title: 'Peringatan', - text: `${message}, Silahkan login`, - }).then((result) => { - if (result.isConfirmed) { - localStorage.clear(); - location.replace('/signin'); - } else if (result.isDenied) { - Swal.fire('Changes are not saved', '', 'info'); - } - }); - } else { - Swal.fire({ - icon: 'warning', - title: 'Peringatan', - text: message, - }).then((result) => {}); - } + } else { + Swal.fire({ + icon: "warning", + title: "Peringatan", + text: message, + }); + } } +// wrapper biar frontend lebih simple const SendRequest = async (queryParams) => { - try { - const response = await ApiRequest(queryParams); - return response || []; - } catch (error) { - console.log('error', error); - if (error.response) { - console.error('Error Status:', error.response.status); // Status error, misal: 401 - console.error('Error Data:', error.response.data); // Detail pesan error - console.error('Error Pesan:', error.response.data.message); //Pesan error - } else { - console.error('Error:', error.message); - } - Swal.fire({ icon: 'error', text: error }); - // return error; - } + try { + const response = await ApiRequest(queryParams); + return response?.data || []; + } catch (error) { + console.error("Request error:", error); + Swal.fire({ icon: "error", text: error.message || "Something went wrong" }); + return []; + } }; export { ApiRequest, SendRequest }; diff --git a/src/layout/LayoutHeader.jsx b/src/layout/LayoutHeader.jsx index 21a7717..457db57 100644 --- a/src/layout/LayoutHeader.jsx +++ b/src/layout/LayoutHeader.jsx @@ -1,10 +1,10 @@ import React from 'react'; -import { Layout, theme, Space, Typography, Breadcrumb, Button } from 'antd'; +import { Layout, Typography, Breadcrumb, Button, theme } from 'antd'; import { UserOutlined } from '@ant-design/icons'; import handleLogOut from '../Utils/Auth/Logout'; import { useBreadcrumb } from './LayoutBreadcrumb'; import { decryptData } from '../components/Global/Formatter'; -import { useNavigate } from 'react-router-dom'; +import { replace, useNavigate } from 'react-router-dom'; const { Link, Text } = Typography; const { Header } = Layout; @@ -12,17 +12,18 @@ const { Header } = Layout; const LayoutHeader = () => { const { breadcrumbItems } = useBreadcrumb(); const navigate = useNavigate(); - const { - token: { colorBgContainer, colorBorder, colorText }, - } = theme.useToken(); + + // Ambil token warna dari theme Ant Design, dengan fallback default + const { token } = theme.useToken() || {}; + const colorBgContainer = token?.colorBgContainer || '#fff'; + const colorBorder = token?.colorBorder || '#d9d9d9'; + const colorText = token?.colorText || '#000'; // Ambil data user dari localStorage dan dekripsi const sessionData = localStorage.getItem('session'); const userData = sessionData ? decryptData(sessionData) : null; - // console.log(userData); const roleName = userData?.user?.approval || userData?.user?.partner_name || 'Guest'; - const userName = userData?.user?.name || userData?.user?.username || 'User'; return ( @@ -35,11 +36,11 @@ const LayoutHeader = () => { alignItems: 'center', flexWrap: 'wrap', rowGap: 10, - paddingTop:15, + paddingTop: 15, paddingBottom: 20, paddingLeft: 24, paddingRight: 24, - minHeight: 100, + minHeight: 100, boxSizing: 'border-box', }} > @@ -88,8 +89,7 @@ const LayoutHeader = () => { { - handleLogOut(); - navigate('/signin'); + handleLogOut(navigate); }} aria-label="Log out from the application" style={{ @@ -117,3 +117,4 @@ const LayoutHeader = () => { }; export default LayoutHeader; + \ No newline at end of file diff --git a/src/pages/auth/Registration.jsx b/src/pages/auth/Registration.jsx index 093c2b1..a836ffd 100644 --- a/src/pages/auth/Registration.jsx +++ b/src/pages/auth/Registration.jsx @@ -1,461 +1,461 @@ -import React, { useState } from 'react'; -import { - Flex, - Input, - InputNumber, - Form, - Button, - Card, - Space, - Upload, - Divider, - Tooltip, - message, - Select, -} from 'antd'; -import { - UploadOutlined, - UserOutlined, - IdcardOutlined, - PhoneOutlined, - LockOutlined, - InfoCircleOutlined, - MailOutlined, -} from '@ant-design/icons'; -const { Item } = Form; -const { Option } = Select; -import sypiu_ggcp from 'assets/sypiu_ggcp.jpg'; -import { useNavigate } from 'react-router-dom'; -import { register, uploadFile, checkUsername } from '../../api/auth'; -import { NotifAlert } from '../../components/Global/ToastNotif'; +// import React, { useState } from 'react'; +// import { +// Flex, +// Input, +// InputNumber, +// Form, +// Button, +// Card, +// Space, +// Upload, +// Divider, +// Tooltip, +// message, +// Select, +// } from 'antd'; +// import { +// UploadOutlined, +// UserOutlined, +// IdcardOutlined, +// PhoneOutlined, +// LockOutlined, +// InfoCircleOutlined, +// MailOutlined, +// } from '@ant-design/icons'; +// const { Item } = Form; +// const { Option } = Select; +// import sypiu_ggcp from 'assets/sypiu_ggcp.jpg'; +// import { useNavigate } from 'react-router-dom'; +// import { register, uploadFile, checkUsername } from '../../api/auth'; +// import { NotifAlert } from '../../components/Global/ToastNotif'; -const Registration = () => { - const [form] = Form.useForm(); - const navigate = useNavigate(); - const [loading, setLoading] = useState(false); - const [fileListKontrak, setFileListKontrak] = useState([]); - const [fileListHsse, setFileListHsse] = useState([]); - const [fileListIcon, setFileListIcon] = useState([]); +// const Registration = () => { +// const [form] = Form.useForm(); +// const navigate = useNavigate(); +// const [loading, setLoading] = useState(false); +// const [fileListKontrak, setFileListKontrak] = useState([]); +// const [fileListHsse, setFileListHsse] = useState([]); +// const [fileListIcon, setFileListIcon] = useState([]); - // Daftar jenis vendor - const vendorTypes = [ - { vendor_type: 1, vendor_type_name: 'One-Time' }, - { vendor_type: 2, vendor_type_name: 'Rutin' }, - ]; +// // Daftar jenis vendor +// const vendorTypes = [ +// { vendor_type: 1, vendor_type_name: 'One-Time' }, +// { vendor_type: 2, vendor_type_name: 'Rutin' }, +// ]; - const onFinish = async (values) => { - setLoading(true); - try { - if (!fileListKontrak.length || !fileListHsse.length) { - message.error('Harap unggah Lampiran Kontrak Kerja dan HSSE Plan!'); - setLoading(false); - return; - } +// const onFinish = async (values) => { +// setLoading(true); +// try { +// if (!fileListKontrak.length || !fileListHsse.length) { +// message.error('Harap unggah Lampiran Kontrak Kerja dan HSSE Plan!'); +// setLoading(false); +// return; +// } - const formData = new FormData(); - formData.append('path_kontrak', fileListKontrak[0].originFileObj); - formData.append('path_hse_plant', fileListHsse[0].originFileObj); - if (fileListIcon.length) { - formData.append('path_icon', fileListIcon[0].originFileObj); - } +// const formData = new FormData(); +// formData.append('path_kontrak', fileListKontrak[0].originFileObj); +// formData.append('path_hse_plant', fileListHsse[0].originFileObj); +// if (fileListIcon.length) { +// formData.append('path_icon', fileListIcon[0].originFileObj); +// } - const uploadResponse = await uploadFile(formData); +// const uploadResponse = await uploadFile(formData); - if (!uploadResponse.data?.pathKontrak && !uploadResponse.data?.pathHsePlant) { - message.error(uploadResponse.message || 'Gagal mengunggah file.'); - setLoading(false); - return; - } +// if (!uploadResponse.data?.pathKontrak && !uploadResponse.data?.pathHsePlant) { +// message.error(uploadResponse.message || 'Gagal mengunggah file.'); +// setLoading(false); +// return; +// } - const params = new URLSearchParams({ username: values.username }); - const usernameCheck = await checkUsername(params); - if (usernameCheck.data.data && usernameCheck.data.data.available === false) { - NotifAlert({ - icon: 'error', - title: 'Gagal', - message: usernameCheck.data.message || 'Terjadi kesalahan, silakan coba lagi', - }); - setLoading(false); - return; - } +// const params = new URLSearchParams({ username: values.username }); +// const usernameCheck = await checkUsername(params); +// if (usernameCheck.data.data && usernameCheck.data.data.available === false) { +// NotifAlert({ +// icon: 'error', +// title: 'Gagal', +// message: usernameCheck.data.message || 'Terjadi kesalahan, silakan coba lagi', +// }); +// setLoading(false); +// return; +// } - const registerData = { - nama_perusahaan: values.namaPerusahaan, - no_kontak_wo: values.noKontakWo, - path_kontrak: uploadResponse.data.pathKontrak || '', - durasi: values.durasiPekerjaan, - nilai_csms: values.nilaiCsms.toString(), - vendor_type: values.jenisVendor, // Tambahkan jenis vendor ke registerData - path_hse_plant: uploadResponse.data.pathHsePlant || '', - nama_leader: values.penanggungJawab, - no_identitas: values.noIdentitas, - no_hp: values.noHandphone, - email_register: values.username, - password_register: values.password, - }; +// const registerData = { +// nama_perusahaan: values.namaPerusahaan, +// no_kontak_wo: values.noKontakWo, +// path_kontrak: uploadResponse.data.pathKontrak || '', +// durasi: values.durasiPekerjaan, +// nilai_csms: values.nilaiCsms.toString(), +// vendor_type: values.jenisVendor, // Tambahkan jenis vendor ke registerData +// path_hse_plant: uploadResponse.data.pathHsePlant || '', +// nama_leader: values.penanggungJawab, +// no_identitas: values.noIdentitas, +// no_hp: values.noHandphone, +// email_register: values.username, +// password_register: values.password, +// }; - const response = await register(registerData); +// const response = await register(registerData); - if (response.data?.id_register) { - message.success('Data berhasil disimpan!'); +// if (response.data?.id_register) { +// message.success('Data berhasil disimpan!'); - try { - form.resetFields(); - setFileListKontrak([]); - setFileListHsse([]); - setFileListIcon([]); +// try { +// form.resetFields(); +// setFileListKontrak([]); +// setFileListHsse([]); +// setFileListIcon([]); - navigate('/registration-submitted'); - } catch (postSuccessError) { - message.warning( - 'Registrasi berhasil, tetapi ada masalah setelahnya. Silakan ke halaman login secara manual.' - ); - } - } else { - message.error(response.message || 'Pendaftaran gagal, silakan coba lagi.'); - } - } catch (error) { - console.error('Error saat registrasi:', error); - NotifAlert({ - icon: 'error', - title: 'Gagal', - message: error.message || 'Terjadi kesalahan, silakan coba lagi', - }); - } finally { - setLoading(false); - } - }; +// navigate('/registration-submitted'); +// } catch (postSuccessError) { +// message.warning( +// 'Registrasi berhasil, tetapi ada masalah setelahnya. Silakan ke halaman login secara manual.' +// ); +// } +// } else { +// message.error(response.message || 'Pendaftaran gagal, silakan coba lagi.'); +// } +// } catch (error) { +// console.error('Error saat registrasi:', error); +// NotifAlert({ +// icon: 'error', +// title: 'Gagal', +// message: error.message || 'Terjadi kesalahan, silakan coba lagi', +// }); +// } finally { +// setLoading(false); +// } +// }; - const onCancel = () => { - form.resetFields(); - setFileListKontrak([]); - setFileListHsse([]); - setFileListIcon([]); - navigate('/signin'); - }; +// const onCancel = () => { +// form.resetFields(); +// setFileListKontrak([]); +// setFileListHsse([]); +// setFileListIcon([]); +// navigate('/signin'); +// }; - const handleChangeKontrak = ({ fileList }) => { - setFileListKontrak(fileList); - }; +// const handleChangeKontrak = ({ fileList }) => { +// setFileListKontrak(fileList); +// }; - const handleChangeHsse = ({ fileList }) => { - setFileListHsse(fileList); - }; +// const handleChangeHsse = ({ fileList }) => { +// setFileListHsse(fileList); +// }; - const handleChangeIcon = ({ fileList }) => { - setFileListIcon(fileList); - }; +// const handleChangeIcon = ({ fileList }) => { +// setFileListIcon(fileList); +// }; - const beforeUpload = (file, fieldname) => { - const isValidType = [ - 'image/jpeg', - 'image/jpg', - 'image/png', - fieldname !== 'path_icon' ? 'application/pdf' : null, - ] - .filter(Boolean) - .includes(file.type); - const isNotEmpty = file.size > 0; - const isSizeValid = file.size / 1024 / 1024 < 10; +// const beforeUpload = (file, fieldname) => { +// const isValidType = [ +// 'image/jpeg', +// 'image/jpg', +// 'image/png', +// fieldname !== 'path_icon' ? 'application/pdf' : null, +// ] +// .filter(Boolean) +// .includes(file.type); +// const isNotEmpty = file.size > 0; +// const isSizeValid = file.size / 1024 / 1024 < 10; - if (!isValidType) { - message.error( - `Hanya file ${ - fieldname === 'path_icon' ? 'JPG/PNG' : 'PDF/JPG/PNG' - } yang diperbolehkan!` - ); - return false; - } - if (!isNotEmpty) { - message.error('File tidak boleh kosong!'); - return false; - } - if (!isSizeValid) { - message.error('Ukuran file maksimal 10MB!'); - return false; - } - return true; - }; +// if (!isValidType) { +// message.error( +// `Hanya file ${ +// fieldname === 'path_icon' ? 'JPG/PNG' : 'PDF/JPG/PNG' +// } yang diperbolehkan!` +// ); +// return false; +// } +// if (!isNotEmpty) { +// message.error('File tidak boleh kosong!'); +// return false; +// } +// if (!isSizeValid) { +// message.error('Ukuran file maksimal 10MB!'); +// return false; +// } +// return true; +// }; - return ( - - -

Formulir Pendaftaran

- -
- } - > -
- {/* Informasi Perusahaan */} - - Informasi Perusahaan - - - } - placeholder="Masukkan Nama Perusahaan" - size="large" - /> - - - - - - - - - beforeUpload(file, 'path_kontrak')} - fileList={fileListKontrak} - onChange={handleChangeKontrak} - maxCount={1} - > - - - - - beforeUpload(file, 'path_hse_plant')} - fileList={fileListHsse} - onChange={handleChangeHsse} - maxCount={1} - > - - - - - - - - - +// return ( +// +// +//

Formulir Pendaftaran

+// +//
+// } +// > +// +// {/* Informasi Perusahaan */} +// +// Informasi Perusahaan +// +// +// } +// placeholder="Masukkan Nama Perusahaan" +// size="large" +// /> +// +// +// +// +// +// +// +// +// beforeUpload(file, 'path_kontrak')} +// fileList={fileListKontrak} +// onChange={handleChangeKontrak} +// maxCount={1} +// > +// +// +// +// +// beforeUpload(file, 'path_hse_plant')} +// fileList={fileListHsse} +// onChange={handleChangeHsse} +// maxCount={1} +// > +// +// +// +// +// +// +// +// +// - {/* Informasi Penanggung Jawab */} - - Informasi Penanggung Jawab - - - } - placeholder="Masukkan Nama Penanggung Jawab" - size="large" - /> - - - } - placeholder="Masukkan No Handphone (+62)" - size="large" - /> - - - } - placeholder="Masukkan No Identitas" - size="large" - /> - +// {/* Informasi Penanggung Jawab */} +// +// Informasi Penanggung Jawab +// +// +// } +// placeholder="Masukkan Nama Penanggung Jawab" +// size="large" +// /> +// +// +// } +// placeholder="Masukkan No Handphone (+62)" +// size="large" +// /> +// +// +// } +// placeholder="Masukkan No Identitas" +// size="large" +// /> +// - {/* Akun Pengguna */} - - Akun Pengguna (digunakan sebagai user login SYPIU) - - - } - placeholder="Masukkan Email" - size="large" - /> - - - } - placeholder="Masukkan Password" - size="large" - /> - +// {/* Akun Pengguna */} +// +// Akun Pengguna (digunakan sebagai user login SYPIU) +// +// +// } +// placeholder="Masukkan Email" +// size="large" +// /> +// +// +// } +// placeholder="Masukkan Password" +// size="large" +// /> +// - {/* Tombol */} - - - - - - -
- - - ); -}; +// {/* Tombol */} +// +// +// +// +// +// +// +// +// +// ); +// }; -export default Registration; +// export default Registration; diff --git a/src/pages/auth/SignIn.jsx b/src/pages/auth/SignIn.jsx index d2f0d33..5f982e3 100644 --- a/src/pages/auth/SignIn.jsx +++ b/src/pages/auth/SignIn.jsx @@ -1,187 +1,117 @@ +import React, { useEffect, useState } from 'react'; import { Flex, Input, Form, Button, Card, Space, Image } from 'antd'; -import React from 'react'; -import handleSignIn from '../../Utils/Auth/SignIn'; +import { useNavigate } from 'react-router-dom'; +import { NotifAlert, NotifOk } from '../../components/Global/ToastNotif'; +import { SendRequest } from '../../components/Global/ApiRequest'; import sypiu_ggcp from 'assets/sypiu_ggcp.jpg'; import logo from 'assets/freepik/LOGOPIU.png'; -import { useNavigate } from 'react-router-dom'; -import { NotifAlert } from '../../components/Global/ToastNotif'; -import { decryptData } from '../../components/Global/Formatter'; const SignIn = () => { - const [captchaSvg, setCaptchaSvg] = React.useState(''); - const [userInput, setUserInput] = React.useState(''); - const [message, setMessage] = React.useState(''); - const [captchaText, setcaptchaText] = React.useState(''); + const [captchaSvg, setCaptchaSvg] = useState(''); + const [captchaText, setCaptchaText] = useState(''); + const [loading, setLoading] = useState(false); + const navigate = useNavigate(); - const navigate = useNavigate(); - // let url = `${import.meta.env.VITE_API_SERVER}/users`; + const moveToSignUp = () => { + navigate("/signup"); + }; - React.useEffect(() => { + // ambil captcha + const fetchCaptcha = async () => { + try { + const res = await SendRequest({ method: 'get', prefix: 'auth/generate-captcha', token: false }); + setCaptchaSvg(res.data.svg || ''); + setCaptchaText(res.data.text || ''); + } catch (err) { + console.error('Error fetching captcha:', err); + } + }; + + useEffect(() => { fetchCaptcha(); }, []); + + const handleOnSubmit = async (values) => { + setLoading(true); + try { + const res = await SendRequest({ + method: 'post', + prefix: 'auth/login', + params: { + email: values.email, + password: values.password, + captcha: values.captcha, + captchaText: captchaText, + } + }); + + const user = res?.data?.user || res?.user; + const tokens = res?.data?.tokens || res?.tokens; + + if (user && tokens?.accessToken) { + localStorage.setItem('token', tokens.accessToken); + localStorage.setItem('refreshToken', tokens.refreshToken); + localStorage.setItem('user', JSON.stringify(user)); + + NotifOk({ + icon: 'success', + title: 'Login Berhasil', + message: res?.message || 'Selamat datang!', + }); + + navigate('/dashboard/home'); + } else { + NotifAlert({ + icon: 'error', + title: 'Login Gagal', + message: res?.message || 'Terjadi kesalahan', + }); fetchCaptcha(); - }, []); + } + } catch (err) { + console.error(err); + NotifAlert({ icon: 'error', title: 'Login Gagal', message: err?.message || 'Terjadi kesalahan' }); + fetchCaptcha(); + } finally { setLoading(false); } + }; - // Fetch the CAPTCHA SVG from the backend - const fetchCaptcha = async () => { - try { - // let url = `${import.meta.env.VITE_API_SERVER}/operation` - // const response = await fetch('http://localhost:9528/generate-captcha'); - const response = await fetch( - `${import.meta.env.VITE_API_SERVER}/auth/generate-captcha`, - { - credentials: 'include', // Wajib untuk mengirim cookie - } - ); + return ( + + + + logo + +
+
+ + + - // Ambil header - const captchaToken = response.headers.get('X-Captcha-Token'); + + + - // console.log('Captcha Token:', decryptData(captchaToken)); +
- setcaptchaText(decryptData(captchaToken)); + + + - const data = await response.text(); - setCaptchaSvg(data); - } catch (error) { - console.error('Error fetching CAPTCHA:', error); - } - }; - - const handleOnSubmit = async (e) => { - // console.log('Received values of form: ', e); - // e.preventDefault(); - - try { - // const response = await fetch(`${import.meta.env.VITE_API_SERVER}/auth/verify-captcha`, { - // method: 'POST', - // headers: { 'Content-Type': 'application/json' }, - // credentials: 'include', // WAJIB: Agar cookie CAPTCHA dikirim - // body: JSON.stringify({ captcha: userInput }), - // }); - - // const data = await response.json(); - // console.log(data); - - const data = { - success: captchaText === userInput ? true : false, - }; - - if (data.success) { - setMessage('CAPTCHA verified successfully!'); - await handleSignIn(e); - } else { - setMessage('CAPTCHA verification failed. Try again.'); - fetchCaptcha(); // Refresh CAPTCHA on failure - NotifAlert({ - icon: 'error', - title: 'Gagal', - message: data.message || 'CAPTCHA verification failed. Try again.', - }); - } - } catch (error) { - console.error('Error verifying CAPTCHA:', error); - setMessage('An error occurred. Please try again.'); - } - - setUserInput(''); // Clear the input field - }; - - const moveToRegistration = (e) => { - // e.preventDefault(); - // navigate("/signup") - navigate('/registration'); - }; - - return ( - <> - - - - signin - -
- - - - - - - - -
- {/* {message} */} - setUserInput(e.target.value)} - /> - - - - - - - - - - - - ); + + + + + + + + + + ); }; export default SignIn; diff --git a/src/pages/auth/Signup.jsx b/src/pages/auth/Signup.jsx index 8bd1d14..42ecbac 100644 --- a/src/pages/auth/Signup.jsx +++ b/src/pages/auth/Signup.jsx @@ -1,158 +1,185 @@ -import { Flex, Input, Form, Button, Card, Space, Image } from 'antd' -import React from 'react' -// import handleSignIn from '../../Utils/Auth/SignIn'; +import React, { useState } from 'react'; +import { Flex, Input, Form, Button, Card, Space, Image, Row, Col, Typography } from 'antd'; +import { useNavigate } from 'react-router-dom'; import sypiu_ggcp from 'assets/sypiu_ggcp.jpg'; -import {useNavigate} from "react-router-dom"; +import logo from 'assets/freepik/LOGOPIU.png'; +import { register } from '../../api/auth'; +import { NotifOk, NotifAlert } from '../../components/Global/ToastNotif'; const SignUp = () => { - - const [captchaSvg, setCaptchaSvg] = React.useState(''); - const [userInput, setUserInput] = React.useState(''); - const [message, setMessage] = React.useState(''); - + const [loading, setLoading] = useState(false); const navigate = useNavigate(); - // let url = `${import.meta.env.VITE_API_SERVER}/users`; - React.useEffect(() => { - fetchCaptcha(); - }, []); + const moveToSignin = () => { + navigate('/signin'); + }; - // Fetch the CAPTCHA SVG from the backend - const fetchCaptcha = async () => { + const handleSignUp = async (values) => { + const { fullname, username, email, phone, password, confirmPassword } = values; + + if (password !== confirmPassword) { + NotifAlert({ + icon: 'error', + title: 'Password Tidak Sama', + message: 'Password dan confirm password harus sama', + }); + return; + } + + const phoneRegex = /^(?:\+62|62|0)8[1-9][0-9]{6,11}$/; + if (!phoneRegex.test(phone)) { + NotifAlert({ + icon: 'error', + title: 'Format Telepon Salah', + message: 'Nomor telepon tidak valid (harus nomor Indonesia)', + }); + return; + } + + const passwordErrors = []; + if (password.length < 8) passwordErrors.push('Minimal 8 karakter'); + if (!/[A-Z]/.test(password)) passwordErrors.push('Harus ada huruf kapital'); + if (!/[0-9]/.test(password)) passwordErrors.push('Harus ada angka'); + if (!/[!@#$%^&*(),.?":{}|<>]/.test(password)) passwordErrors.push('Harus ada karakter spesial'); + if (passwordErrors.length) { + NotifAlert({ + icon: 'error', + title: 'Password Tidak Valid', + message: passwordErrors.join(', '), + }); + return; + } + + setLoading(true); try { - const response = await fetch('http://localhost:9528/generate-captcha'); - const data = await response.text(); - setCaptchaSvg(data); - } catch (error) { - console.error('Error fetching CAPTCHA:', error); + const res = await register({ fullname, username, email, phone, password }); + // const user = res?.data?.user; + // // const tokens = res?.data?.tokens; + + + NotifOk({ + icon: 'success', + title: 'Registrasi Berhasil', + message: res?.data?.message || 'Berhasil menambahkan user.', + }); + + + // if (user) { + // // localStorage.setItem('token', tokens.accessToken); + // // localStorage.setItem('refreshToken', tokens.refreshToken); + // // localStorage.setItem('user', JSON.stringify(user)); + + // NotifOk({ + // icon: 'success', + // title: 'Registrasi Berhasil', + // message: res?.data?.message || 'Selamat datang! Anda akan diarahkan ke dashboard.', + // }); + + // navigate('/signin'); + // } else { + // NotifAlert({ + // icon: 'error', + // title: 'Registrasi Gagal', + // message: res?.data?.message || 'Terjadi kesalahan', + // }); + // } + } catch (err) { + console.error('Register error:', err); + NotifAlert({ + icon: 'error', + title: 'Registrasi Gagal', + message: err?.response?.data?.message || err.message || 'Terjadi kesalahan', + }); + } finally { + setLoading(false); } }; - const handleOnSubmt = async (e) => { - // console.log('Received values of form: ', e); - // await handleSignIn(e); - e.preventDefault(); - - try { - const response = await fetch('http://localhost:5000/verify-captcha', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ userInput }), - }); - - const data = await response.json(); - if (data.success) { - setMessage('CAPTCHA verified successfully!'); - } else { - setMessage('CAPTCHA verification failed. Try again.'); - fetchCaptcha(); // Refresh CAPTCHA on failure - } - } catch (error) { - console.error('Error verifying CAPTCHA:', error); - setMessage('An error occurred. Please try again.'); - } - - setUserInput(''); // Clear the input field - } - - const moveToSignin = (e) => { - // e.preventDefault(); - navigate("/signin") - } - return ( - <> - - + - + +

Registration

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ ); +}; - - - - - - - -
- - - - - - {/* setUserInput(e.target.value)} - /> */} - - - - - - - - - - - - - ) -} - -export default SignUp +export default SignUp; diff --git a/src/pages/home/Home.jsx b/src/pages/home/Home.jsx index 38bd5ab..b080d27 100644 --- a/src/pages/home/Home.jsx +++ b/src/pages/home/Home.jsx @@ -9,44 +9,39 @@ const Home = () => { const [isMobile, setIsMobile] = useState(window.innerWidth <= 768); useEffect(() => { - const handleResize = () => { - setIsMobile(window.innerWidth <= 768); - }; + const handleResize = () => setIsMobile(window.innerWidth <= 768); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); useEffect(() => { - const token = localStorage.getItem('token'); - if (token) { - setBreadcrumbItems([ - { - title: ( - - • Dashboard - - ), - }, - { - title: ( - - Home - - ), - }, - ]); - } else { - navigate('/signin'); - } + setBreadcrumbItems([ + { + title: ( + + Dashboard + + ), + }, + { + title: ( + + Home + + ), + }, + ]); }, []); return ( - Wellcome Call Of Duty App + + Welcome to Call Of Duty App + ); }; -export default Home; +export default Home; \ No newline at end of file