diff --git a/Modules/Client/Http/Controllers/Api/AuthController.php b/Modules/Client/Http/Controllers/Api/AuthController.php index 530d5a68..dd8dcc4a 100644 --- a/Modules/Client/Http/Controllers/Api/AuthController.php +++ b/Modules/Client/Http/Controllers/Api/AuthController.php @@ -8,6 +8,7 @@ use App\Models\User; use Illuminate\Database\Eloquent\Builder; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Hash; use Symfony\Component\HttpFoundation\Response; use Illuminate\Support\Facades\View; @@ -16,7 +17,8 @@ class AuthController extends Controller public function login(Request $request) { $request->validate([ - 'phoneOrEmail' => 'required' + 'phoneOrEmail' => 'required', + 'password' => 'required' ]); $user = User::query() @@ -32,45 +34,61 @@ class AuthController extends Controller return Helper::responseJson(statusCode: Response::HTTP_NOT_FOUND, message: $message); } - $token = rand(1000, 9999); // Menghasilkan angka acak antara 100000 dan 999999 - if($request->phoneOrEmail == 'manager+one@gmail.com' || $request->phoneOrEmail == 'manager+two@gmail.com') - { - $token = 4444; - } - if (filter_var($request->phoneOrEmail, FILTER_VALIDATE_EMAIL)) { - User::query()->find($user->id)->update([ - 'email' => $request->phoneOrEmail, - 'otp' => $token, - 'otp_created_at' => now() - ]); - } else { - User::query()->find($user->id)->update([ - 'phone' => $request->phoneOrEmail, - 'otp' => $token, - 'otp_created_at' => now() - ]); + + + // $token = rand(1000, 9999); // Menghasilkan angka acak antara 100000 dan 999999 + // if($request->phoneOrEmail == 'manager+one@gmail.com' || $request->phoneOrEmail == 'manager+two@gmail.com') + // { + // $token = 4444; + // } + // if (filter_var($request->phoneOrEmail, FILTER_VALIDATE_EMAIL)) { + // User::query()->find($user->id)->update([ + // 'email' => $request->phoneOrEmail, + // 'otp' => $token, + // 'otp_created_at' => now() + // ]); + // } else { + // User::query()->find($user->id)->update([ + // 'phone' => $request->phoneOrEmail, + // 'otp' => $token, + // 'otp_created_at' => now() + // ]); + // } + + // // TODO Send the OTP + // if (filter_var($request->phoneOrEmail, FILTER_VALIDATE_EMAIL)) { + // // Send Email + // //send to alarm + // if($request->phoneOrEmail != 'manager+one@gmail.com' && $request->phoneOrEmail != 'manager+two@gmail.com') + // { + // $nameTo = 'User'; + // $dataEmail = [ + // 'email' => $request->phoneOrEmail, + // 'name' => $nameTo, + // 'subject' => 'OTP Login Client Portal Tanggal '. date('Y-m-d H:i:s'), + // 'body' => View::make('email/forgot_password', ['token' => $token])->render(), + // ]; + // Helper::sendEmail($dataEmail); + // } + // } else { + // // Send Whatsapp + // } + + // return Helper::responseJson(message: 'OTP Terkirim'); + + + if (!Hash::check($request->password, $user->password)) { + return response(['message' => 'Password Salah'], 403); } - // TODO Send the OTP - if (filter_var($request->phoneOrEmail, FILTER_VALIDATE_EMAIL)) { - // Send Email - //send to alarm - if($request->phoneOrEmail != 'manager+one@gmail.com' && $request->phoneOrEmail != 'manager+two@gmail.com') - { - $nameTo = 'User'; - $dataEmail = [ - 'email' => $request->phoneOrEmail, - 'name' => $nameTo, - 'subject' => 'OTP Login Client Portal Tanggal '. date('Y-m-d H:i:s'), - 'body' => View::make('email/forgot_password', ['token' => $token])->render(), - ]; - Helper::sendEmail($dataEmail); - } - } else { - // Send Whatsapp - } + return Helper::responseJson( + data: [ + 'token' => $user->createToken('app')->plainTextToken, + 'user' => $user, + ], + message: 'Selamat Datang' + ); - return Helper::responseJson(message: 'OTP Terkirim'); } public function validateOtp(Request $request) diff --git a/frontend/client-portal/src/@types/auth.ts b/frontend/client-portal/src/@types/auth.ts index a5d2236c..680ca4d7 100644 --- a/frontend/client-portal/src/@types/auth.ts +++ b/frontend/client-portal/src/@types/auth.ts @@ -26,7 +26,7 @@ export type JWTContextType = { isInitialized: boolean; user: AuthUser; method: 'jwt'; - login: (phoneOrEmail: string) => Promise; + login: (phoneOrEmail: string, password: string, rememberMe: boolean) => Promise; validateOtp: (phoneOrEmail: string, otp: string) => Promise logout: () => void; }; diff --git a/frontend/client-portal/src/contexts/LaravelAuthContext.tsx b/frontend/client-portal/src/contexts/LaravelAuthContext.tsx index 0ce83544..b0c9a8f0 100644 --- a/frontend/client-portal/src/contexts/LaravelAuthContext.tsx +++ b/frontend/client-portal/src/contexts/LaravelAuthContext.tsx @@ -1,11 +1,12 @@ import { createContext, ReactNode, useEffect, useReducer } from 'react'; // utils import axios from '../utils/axios'; -import { setSession, getSession } from '../utils/token'; +import { setSession, getSession, setUser, getUser, getCookie } from '../utils/token'; // @types import { ActionMap, AuthState, AuthUser, JWTContextType } from '../@types/auth'; // ---------------------------------------------------------------------- +import { Navigate, useLocation } from 'react-router-dom'; enum Types { Initial = 'INITIALIZE', @@ -19,7 +20,9 @@ type JWTAuthPayload = { isAuthenticated: boolean; user: AuthUser; }; - [Types.Login]: undefined; + [Types.Login]: { + user: AuthUser; + }; [Types.ValidateOtp]: { user: AuthUser; }; @@ -45,8 +48,8 @@ const JWTReducer = (state: AuthState, action: JWTActions) => { case 'LOGIN': return { ...state, - isAuthenticated: false, - user: null, + isAuthenticated: true, + user: action.payload.user, }; case 'VALIDATE-OTP': return { @@ -75,7 +78,46 @@ type AuthProviderProps = { function AuthProvider({ children }: AuthProviderProps) { const [state, dispatch] = useReducer(JWTReducer, initialState); + let location = useLocation(); const accessToken = getSession(); + // useEffect(() => { + // (async () => { + // try { + // // const accessToken = getSession(); + + // if (accessToken) { + // setSession(accessToken); + + // const response = await axios.get('/user'); + // const user = response.data; + + // dispatch({ + // type: Types.Initial, + // payload: { + // isAuthenticated: true, + // user, + // }, + // }); + // } else { + // dispatch({ + // type: Types.Initial, + // payload: { + // isAuthenticated: false, + // user: null, + // }, + // }); + // } + // } catch (err) { + // dispatch({ + // type: Types.Initial, + // payload: { + // isAuthenticated: false, + // user: null, + // }, + // }); + // } + // })(); + // }, [accessToken]); useEffect(() => { (async () => { @@ -116,12 +158,28 @@ function AuthProvider({ children }: AuthProviderProps) { })(); }, [accessToken]); - const login = async (phoneOrEmail: string) => + + const headers = { + headers: { + 'Accept': 'application/json', + 'Content-Type' : 'application/json', + 'Accept-Language': localStorage.getItem('currentLocale') ?? 'id-ID', + }, + }; + + const login = async (phoneOrEmail: string, password: string, remember:boolean) => axios - .post('/login', { phoneOrEmail }) - .then(() => { + .post('/login', { phoneOrEmail, password }, headers) + .then((response) => { + const { user, token } = response.data.data; + setSession(token); + setUser(user); + dispatch({ type: Types.Login, + payload: { + user, + } }); }) .catch((error) => { diff --git a/frontend/client-portal/src/pages/auth/Login.tsx b/frontend/client-portal/src/pages/auth/Login.tsx index ccea33d6..a0b84ebc 100644 --- a/frontend/client-portal/src/pages/auth/Login.tsx +++ b/frontend/client-portal/src/pages/auth/Login.tsx @@ -97,7 +97,7 @@ export default function Login() { - {loginOrVerifyCode && emailOrPhone ? ( + {/* {loginOrVerifyCode && emailOrPhone ? ( <> @@ -138,7 +138,7 @@ export default function Login() { >Kirim Ulang Kode OTP - ) : ( + ) : ( */} <> @@ -152,19 +152,16 @@ export default function Login() { - {emailOrPhoneForm ? ( + {/* {emailOrPhoneForm ? ( - ) : ( - - )} + ) : ( */} + + {/* )} */} - )} + {/* )} */} {/* Atau diff --git a/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx b/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx index 17259d4c..75d7e7e2 100644 --- a/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx +++ b/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx @@ -1,42 +1,52 @@ /* ----------------------------------- yup ---------------------------------- */ import * as Yup from 'yup'; +import React, { useContext, useRef, useState, useEffect } from 'react'; /* ---------------------------------- form ---------------------------------- */ import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; /* ---------------------------------- @mui ---------------------------------- */ -import { Stack, Alert } from '@mui/material'; import { LoadingButton } from '@mui/lab'; +import { Link, Stack, Alert, IconButton, InputAdornment } from '@mui/material'; /* ---------------------------------- hooks --------------------------------- */ import useAuth from '../../../hooks/useAuth'; import useIsMountedRef from '../../../hooks/useIsMountedRef'; /* ------------------------------- components ------------------------------- */ -import { FormProvider, RHFTextField } from '../../../components/hook-form'; +import Iconify from '../../../components/Iconify'; +import { FormProvider, RHFTextField, RHFCheckbox } from '../../../components/hook-form'; import { enqueueSnackbar } from 'notistack'; +import { useNavigate } from 'react-router-dom'; /* ---------------------------------- types --------------------------------- */ -type LoginFormProps = { - setEmailOrPhone: Function; - setLoginOrVerifyCode: Function; -}; +// type LoginFormProps = { +// setEmailOrPhone: Function; +// setLoginOrVerifyCode: Function; +// }; type FormValuesProps = { email: string; + password: string; + remember: boolean; afterSubmit?: string; }; /* -------------------------------------------------------------------------- */ -export default function LoginForm({ setEmailOrPhone, setLoginOrVerifyCode }: LoginFormProps) { +export default function LoginForm() { const { login } = useAuth(); + const navigate = useNavigate(); const isMountedRef = useIsMountedRef(); + const [showPassword, setShowPassword] = useState(false); const LoginSchema = Yup.object().shape({ - email: Yup.string().email('Email must be a valid email address').required('Email is required'), + email: Yup.string().email('Format email tidak valid').required('Email harus diisi'), + password: Yup.string().required('Password harus diisi'), }); - const defaultValues = { + const defaultValues = { email: '', + password: '', + remember: true, }; const methods = useForm({ @@ -51,17 +61,34 @@ export default function LoginForm({ setEmailOrPhone, setLoginOrVerifyCode }: Log formState: { errors, isSubmitting }, } = methods; + // const onSubmit = async (data: FormValuesProps) => { + // try { + // const loginResult = await login(data.email, data.password, data.remember); + // // setEmailOrPhone(data.email); + // // setLoginOrVerifyCode(true); + // // reset(); + // console.log('test'); + // navigate('/dashboard'); + // // enqueueSnackbar('Kode OTP telah dikirim, silahkan cek email dan spam folder', { + // // variant: 'success', + // // autoHideDuration: 5000, + // // }); + // } catch (error: any) { + // reset(); + // console.log(error, 'test'); + + // if (isMountedRef.current) { + // setError('afterSubmit', { ...error, message: error.data.message }); + // } + // } + // }; + const onSubmit = async (data: FormValuesProps) => { try { - await login(data.email); - setEmailOrPhone(data.email); - setLoginOrVerifyCode(true); - reset(); - enqueueSnackbar('Kode OTP telah dikirim, silahkan cek email dan spam folder', { - variant: 'success', - autoHideDuration: 5000, - }); - } catch (error: any) { + const loginResult = await login(data.email, data.password, data.remember); + navigate('/dashboard'); + } catch (error) { + reset(); if (isMountedRef.current) { @@ -73,10 +100,25 @@ export default function LoginForm({ setEmailOrPhone, setLoginOrVerifyCode }: Log return ( - Masukkan akun yang telah terdaftar + Masukan Email atau Username dan Password {!!errors.afterSubmit && {errors.afterSubmit.message}} + + setShowPassword(!showPassword)} edge="end"> + + + + ), + }} + required + />