update login dan password

This commit is contained in:
2024-06-27 14:43:01 +07:00
parent 32a45814b8
commit 4ceaf41a7d
5 changed files with 189 additions and 74 deletions

View File

@@ -8,6 +8,7 @@ use App\Models\User;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\View;
@@ -16,7 +17,8 @@ class AuthController extends Controller
public function login(Request $request) public function login(Request $request)
{ {
$request->validate([ $request->validate([
'phoneOrEmail' => 'required' 'phoneOrEmail' => 'required',
'password' => 'required'
]); ]);
$user = User::query() $user = User::query()
@@ -32,45 +34,61 @@ class AuthController extends Controller
return Helper::responseJson(statusCode: Response::HTTP_NOT_FOUND, message: $message); 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 = rand(1000, 9999); // Menghasilkan angka acak antara 100000 dan 999999
$token = 4444; // if($request->phoneOrEmail == 'manager+one@gmail.com' || $request->phoneOrEmail == 'manager+two@gmail.com')
} // {
if (filter_var($request->phoneOrEmail, FILTER_VALIDATE_EMAIL)) { // $token = 4444;
User::query()->find($user->id)->update([ // }
'email' => $request->phoneOrEmail, // if (filter_var($request->phoneOrEmail, FILTER_VALIDATE_EMAIL)) {
'otp' => $token, // User::query()->find($user->id)->update([
'otp_created_at' => now() // 'email' => $request->phoneOrEmail,
]); // 'otp' => $token,
} else { // 'otp_created_at' => now()
User::query()->find($user->id)->update([ // ]);
'phone' => $request->phoneOrEmail, // } else {
'otp' => $token, // User::query()->find($user->id)->update([
'otp_created_at' => now() // '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 return Helper::responseJson(
if (filter_var($request->phoneOrEmail, FILTER_VALIDATE_EMAIL)) { data: [
// Send Email 'token' => $user->createToken('app')->plainTextToken,
//send to alarm 'user' => $user,
if($request->phoneOrEmail != 'manager+one@gmail.com' && $request->phoneOrEmail != 'manager+two@gmail.com') ],
{ message: 'Selamat Datang'
$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');
} }
public function validateOtp(Request $request) public function validateOtp(Request $request)

View File

@@ -26,7 +26,7 @@ export type JWTContextType = {
isInitialized: boolean; isInitialized: boolean;
user: AuthUser; user: AuthUser;
method: 'jwt'; method: 'jwt';
login: (phoneOrEmail: string) => Promise<void>; login: (phoneOrEmail: string, password: string, rememberMe: boolean) => Promise<void>;
validateOtp: (phoneOrEmail: string, otp: string) => Promise<void> validateOtp: (phoneOrEmail: string, otp: string) => Promise<void>
logout: () => void; logout: () => void;
}; };

View File

@@ -1,11 +1,12 @@
import { createContext, ReactNode, useEffect, useReducer } from 'react'; import { createContext, ReactNode, useEffect, useReducer } from 'react';
// utils // utils
import axios from '../utils/axios'; import axios from '../utils/axios';
import { setSession, getSession } from '../utils/token'; import { setSession, getSession, setUser, getUser, getCookie } from '../utils/token';
// @types // @types
import { ActionMap, AuthState, AuthUser, JWTContextType } from '../@types/auth'; import { ActionMap, AuthState, AuthUser, JWTContextType } from '../@types/auth';
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
import { Navigate, useLocation } from 'react-router-dom';
enum Types { enum Types {
Initial = 'INITIALIZE', Initial = 'INITIALIZE',
@@ -19,7 +20,9 @@ type JWTAuthPayload = {
isAuthenticated: boolean; isAuthenticated: boolean;
user: AuthUser; user: AuthUser;
}; };
[Types.Login]: undefined; [Types.Login]: {
user: AuthUser;
};
[Types.ValidateOtp]: { [Types.ValidateOtp]: {
user: AuthUser; user: AuthUser;
}; };
@@ -45,8 +48,8 @@ const JWTReducer = (state: AuthState, action: JWTActions) => {
case 'LOGIN': case 'LOGIN':
return { return {
...state, ...state,
isAuthenticated: false, isAuthenticated: true,
user: null, user: action.payload.user,
}; };
case 'VALIDATE-OTP': case 'VALIDATE-OTP':
return { return {
@@ -75,7 +78,46 @@ type AuthProviderProps = {
function AuthProvider({ children }: AuthProviderProps) { function AuthProvider({ children }: AuthProviderProps) {
const [state, dispatch] = useReducer(JWTReducer, initialState); const [state, dispatch] = useReducer(JWTReducer, initialState);
let location = useLocation();
const accessToken = getSession(); 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(() => { useEffect(() => {
(async () => { (async () => {
@@ -116,12 +158,28 @@ function AuthProvider({ children }: AuthProviderProps) {
})(); })();
}, [accessToken]); }, [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 axios
.post('/login', { phoneOrEmail }) .post('/login', { phoneOrEmail, password }, headers)
.then(() => { .then((response) => {
const { user, token } = response.data.data;
setSession(token);
setUser(user);
dispatch({ dispatch({
type: Types.Login, type: Types.Login,
payload: {
user,
}
}); });
}) })
.catch((error) => { .catch((error) => {

View File

@@ -97,7 +97,7 @@ export default function Login() {
</video> </video>
</Grid> </Grid>
<Grid item xs={6} sx={{ padding: 3 }}> <Grid item xs={6} sx={{ padding: 3 }}>
{loginOrVerifyCode && emailOrPhone ? ( {/* {loginOrVerifyCode && emailOrPhone ? (
<> <>
<Stack direction="column" sx={{ mb: 5 }}> <Stack direction="column" sx={{ mb: 5 }}>
<Stack direction="row" alignItems="center"> <Stack direction="row" alignItems="center">
@@ -138,7 +138,7 @@ export default function Login() {
>Kirim Ulang Kode OTP</Link> >Kirim Ulang Kode OTP</Link>
</Stack> </Stack>
</> </>
) : ( ) : ( */}
<> <>
<Stack direction="row" alignItems="center" sx={{ mb: 5 }}> <Stack direction="row" alignItems="center" sx={{ mb: 5 }}>
<Logo sx={{ width: 90, height: 90 }} /> <Logo sx={{ width: 90, height: 90 }} />
@@ -152,19 +152,16 @@ export default function Login() {
</Box> </Box>
</Stack> </Stack>
{emailOrPhoneForm ? ( {/* {emailOrPhoneForm ? (
<LoginPhoneForm <LoginPhoneForm
setEmailOrPhone={setEmailOrPhone} setEmailOrPhone={setEmailOrPhone}
setLoginOrVerifyCode={setLoginOrVerifyCode} setLoginOrVerifyCode={setLoginOrVerifyCode}
/> />
) : ( ) : ( */}
<LoginEmailForm <LoginEmailForm/>
setEmailOrPhone={setEmailOrPhone} {/* )} */}
setLoginOrVerifyCode={setLoginOrVerifyCode}
/>
)}
</> </>
)} {/* )} */}
{/* <Divider sx={{ marginTop: 5 }}>Atau</Divider> {/* <Divider sx={{ marginTop: 5 }}>Atau</Divider>

View File

@@ -1,42 +1,52 @@
/* ----------------------------------- yup ---------------------------------- */ /* ----------------------------------- yup ---------------------------------- */
import * as Yup from 'yup'; import * as Yup from 'yup';
import React, { useContext, useRef, useState, useEffect } from 'react';
/* ---------------------------------- form ---------------------------------- */ /* ---------------------------------- form ---------------------------------- */
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup'; import { yupResolver } from '@hookform/resolvers/yup';
/* ---------------------------------- @mui ---------------------------------- */ /* ---------------------------------- @mui ---------------------------------- */
import { Stack, Alert } from '@mui/material';
import { LoadingButton } from '@mui/lab'; import { LoadingButton } from '@mui/lab';
import { Link, Stack, Alert, IconButton, InputAdornment } from '@mui/material';
/* ---------------------------------- hooks --------------------------------- */ /* ---------------------------------- hooks --------------------------------- */
import useAuth from '../../../hooks/useAuth'; import useAuth from '../../../hooks/useAuth';
import useIsMountedRef from '../../../hooks/useIsMountedRef'; import useIsMountedRef from '../../../hooks/useIsMountedRef';
/* ------------------------------- components ------------------------------- */ /* ------------------------------- 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 { enqueueSnackbar } from 'notistack';
import { useNavigate } from 'react-router-dom';
/* ---------------------------------- types --------------------------------- */ /* ---------------------------------- types --------------------------------- */
type LoginFormProps = { // type LoginFormProps = {
setEmailOrPhone: Function; // setEmailOrPhone: Function;
setLoginOrVerifyCode: Function; // setLoginOrVerifyCode: Function;
}; // };
type FormValuesProps = { type FormValuesProps = {
email: string; email: string;
password: string;
remember: boolean;
afterSubmit?: string; afterSubmit?: string;
}; };
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
export default function LoginForm({ setEmailOrPhone, setLoginOrVerifyCode }: LoginFormProps) { export default function LoginForm() {
const { login } = useAuth(); const { login } = useAuth();
const navigate = useNavigate();
const isMountedRef = useIsMountedRef(); const isMountedRef = useIsMountedRef();
const [showPassword, setShowPassword] = useState(false);
const LoginSchema = Yup.object().shape({ 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: '', email: '',
password: '',
remember: true,
}; };
const methods = useForm<FormValuesProps>({ const methods = useForm<FormValuesProps>({
@@ -51,17 +61,34 @@ export default function LoginForm({ setEmailOrPhone, setLoginOrVerifyCode }: Log
formState: { errors, isSubmitting }, formState: { errors, isSubmitting },
} = methods; } = 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) => { const onSubmit = async (data: FormValuesProps) => {
try { try {
await login(data.email); const loginResult = await login(data.email, data.password, data.remember);
setEmailOrPhone(data.email); navigate('/dashboard');
setLoginOrVerifyCode(true); } catch (error) {
reset();
enqueueSnackbar('Kode OTP telah dikirim, silahkan cek email dan spam folder', {
variant: 'success',
autoHideDuration: 5000,
});
} catch (error: any) {
reset(); reset();
if (isMountedRef.current) { if (isMountedRef.current) {
@@ -73,10 +100,25 @@ export default function LoginForm({ setEmailOrPhone, setLoginOrVerifyCode }: Log
return ( return (
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}> <FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
<Stack spacing={3}> <Stack spacing={3}>
<Alert severity="info">Masukkan akun yang telah terdaftar</Alert> <Alert severity="info">Masukan Email atau Username dan Password</Alert>
{!!errors.afterSubmit && <Alert severity="error">{errors.afterSubmit.message}</Alert>} {!!errors.afterSubmit && <Alert severity="error">{errors.afterSubmit.message}</Alert>}
<RHFTextField name="email" label="Email address" /> <RHFTextField name="email" label="Email address" />
<RHFTextField
name="password"
label="Password"
type={showPassword ? 'text' : 'password'}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton onClick={() => setShowPassword(!showPassword)} edge="end">
<Iconify icon={showPassword ? 'eva:eye-fill' : 'eva:eye-off-fill'} />
</IconButton>
</InputAdornment>
),
}}
required
/>
</Stack> </Stack>
<LoadingButton <LoadingButton