client portal login api
This commit is contained in:
@@ -33,17 +33,13 @@ class AuthController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (filter_var($request->phone_or_email, FILTER_VALIDATE_EMAIL)) {
|
if (filter_var($request->phone_or_email, FILTER_VALIDATE_EMAIL)) {
|
||||||
$user = User::query()->update([
|
User::query()->find($user->id)->update([
|
||||||
'email' => $request->phone_or_email
|
|
||||||
], [
|
|
||||||
'email' => $request->phone_or_email,
|
'email' => $request->phone_or_email,
|
||||||
'otp' => rand(1000, 9999),
|
'otp' => rand(1000, 9999),
|
||||||
'otp_created_at' => now()
|
'otp_created_at' => now()
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$user = User::query()->update([
|
User::query()->find($user->id)->update([
|
||||||
'phone' => $request->phone_or_email
|
|
||||||
], [
|
|
||||||
'phone' => $request->phone_or_email,
|
'phone' => $request->phone_or_email,
|
||||||
'otp' => rand(1000, 9999),
|
'otp' => rand(1000, 9999),
|
||||||
'otp_created_at' => now()
|
'otp_created_at' => now()
|
||||||
|
|||||||
21
frontend/client-portal/package-lock.json
generated
21
frontend/client-portal/package-lock.json
generated
@@ -42,6 +42,7 @@
|
|||||||
"react-hook-form": "^7.34.2",
|
"react-hook-form": "^7.34.2",
|
||||||
"react-intersection-observer": "^8.34.0",
|
"react-intersection-observer": "^8.34.0",
|
||||||
"react-lazy-load-image-component": "^1.5.5",
|
"react-lazy-load-image-component": "^1.5.5",
|
||||||
|
"react-number-format": "^5.1.1",
|
||||||
"react-quill": "2.0.0-beta.4",
|
"react-quill": "2.0.0-beta.4",
|
||||||
"react-router": "^6.3.0",
|
"react-router": "^6.3.0",
|
||||||
"react-router-dom": "^6.3.0",
|
"react-router-dom": "^6.3.0",
|
||||||
@@ -7668,6 +7669,18 @@
|
|||||||
"react-dom": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x"
|
"react-dom": "^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-number-format": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-wLRUP9G0B79hZrTRgj380/ygtKRJWrCnTBIlQkoYAp/AMNQK7qGkUAWM5EpS1YRxz1w003UmZoon8WRutnlFuw==",
|
||||||
|
"dependencies": {
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^18.0.0",
|
||||||
|
"react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-quill": {
|
"node_modules/react-quill": {
|
||||||
"version": "2.0.0-beta.4",
|
"version": "2.0.0-beta.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0-beta.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0-beta.4.tgz",
|
||||||
@@ -14632,6 +14645,14 @@
|
|||||||
"lodash.throttle": "^4.1.1"
|
"lodash.throttle": "^4.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-number-format": {
|
||||||
|
"version": "5.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.1.1.tgz",
|
||||||
|
"integrity": "sha512-wLRUP9G0B79hZrTRgj380/ygtKRJWrCnTBIlQkoYAp/AMNQK7qGkUAWM5EpS1YRxz1w003UmZoon8WRutnlFuw==",
|
||||||
|
"requires": {
|
||||||
|
"prop-types": "^15.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-quill": {
|
"react-quill": {
|
||||||
"version": "2.0.0-beta.4",
|
"version": "2.0.0-beta.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0-beta.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0-beta.4.tgz",
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
"react-hook-form": "^7.34.2",
|
"react-hook-form": "^7.34.2",
|
||||||
"react-intersection-observer": "^8.34.0",
|
"react-intersection-observer": "^8.34.0",
|
||||||
"react-lazy-load-image-component": "^1.5.5",
|
"react-lazy-load-image-component": "^1.5.5",
|
||||||
|
"react-number-format": "^5.1.1",
|
||||||
"react-quill": "2.0.0-beta.4",
|
"react-quill": "2.0.0-beta.4",
|
||||||
"react-router": "^6.3.0",
|
"react-router": "^6.3.0",
|
||||||
"react-router-dom": "^6.3.0",
|
"react-router-dom": "^6.3.0",
|
||||||
|
|||||||
BIN
frontend/client-portal/public/images/login-image.gif
Normal file
BIN
frontend/client-portal/public/images/login-image.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 MiB |
@@ -26,8 +26,7 @@ export type JWTContextType = {
|
|||||||
isInitialized: boolean;
|
isInitialized: boolean;
|
||||||
user: AuthUser;
|
user: AuthUser;
|
||||||
method: 'jwt';
|
method: 'jwt';
|
||||||
login: (email: string, password: string) => Promise<void>;
|
// login: (phone_or_email: string) => Promise<void>;
|
||||||
register: (email: string, password: string, firstName: string, lastName: string) => Promise<void>;
|
|
||||||
logout: () => Promise<void>;
|
logout: () => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -80,15 +80,15 @@ 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();
|
// let location = useLocation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const initialize = async () => {
|
const initialize = async () => {
|
||||||
console.log('initialize', state)
|
console.log('initialize', state);
|
||||||
try {
|
try {
|
||||||
const accessToken = getSession();
|
const accessToken = getSession();
|
||||||
|
|
||||||
console.log('')
|
console.log('');
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
setSession(accessToken);
|
setSession(accessToken);
|
||||||
|
|
||||||
@@ -125,56 +125,56 @@ function AuthProvider({ children }: AuthProviderProps) {
|
|||||||
initialize();
|
initialize();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const login = async (email: string, password: string) => {
|
// const login = async (phone_or_email: string) => {
|
||||||
return axios
|
// axios
|
||||||
.post('/login', { email, password })
|
// .post('/otp-request', { phone_or_email })
|
||||||
.then((response) => {
|
// .then((response: any) => {
|
||||||
const { user, token } = response.data;
|
// const { user, token } = response.data;
|
||||||
setSession(token);
|
// setSession(token);
|
||||||
|
|
||||||
dispatch({
|
// dispatch({
|
||||||
type: Types.Login,
|
// type: Types.Login,
|
||||||
payload: {
|
// payload: {
|
||||||
user,
|
// user,
|
||||||
}
|
// },
|
||||||
});
|
// });
|
||||||
})
|
// })
|
||||||
.catch(error => {
|
// .catch((error) => {
|
||||||
if (error.response.status !== 404) throw error.response
|
// if (error.response.status !== 404) throw error.response;
|
||||||
if (error.response.status !== 422) throw error.response
|
// if (error.response.status !== 422) throw error.response;
|
||||||
})
|
// });
|
||||||
};
|
// };
|
||||||
|
|
||||||
const register = async (email: string, password: string, firstName: string, lastName: string) => {
|
// const register = async (email: string, password: string, firstName: string, lastName: string) => {
|
||||||
const response = await axios.post('/api/register', {
|
// const response = await axios.post('/api/register', {
|
||||||
email,
|
// email,
|
||||||
password,
|
// password,
|
||||||
firstName,
|
// firstName,
|
||||||
lastName,
|
// lastName,
|
||||||
});
|
// });
|
||||||
const { accessToken, user } = response.data;
|
// const { accessToken, user } = response.data;
|
||||||
|
|
||||||
window.localStorage.setItem('accessToken', accessToken);
|
// window.localStorage.setItem('accessToken', accessToken);
|
||||||
dispatch({
|
// dispatch({
|
||||||
type: Types.Register,
|
// type: Types.Register,
|
||||||
payload: {
|
// payload: {
|
||||||
user,
|
// user,
|
||||||
},
|
// },
|
||||||
});
|
// });
|
||||||
};
|
// };
|
||||||
|
|
||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
|
await axios.post('/logout', { token: getSession() });
|
||||||
setSession(null);
|
setSession(null);
|
||||||
dispatch({ type: Types.Logout });
|
dispatch({ type: Types.Logout });
|
||||||
};
|
};
|
||||||
|
|
||||||
return (<AuthContext.Provider
|
return (
|
||||||
|
<AuthContext.Provider
|
||||||
value={{
|
value={{
|
||||||
...state,
|
...state,
|
||||||
method: 'jwt',
|
method: 'jwt',
|
||||||
login,
|
|
||||||
logout,
|
logout,
|
||||||
register,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
@@ -182,9 +182,9 @@ function AuthProvider({ children }: AuthProviderProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// if (state.isInitialized) {
|
// if (state.isInitialized) {
|
||||||
// return (!state.isAuthenticated && location.pathname !== '/auth/login') ?
|
// return (!state.isAuthenticated && location.pathname !== '/auth/login') ?
|
||||||
// (<Navigate to="/auth/login" replace={true} />)
|
// (<Navigate to="/auth/login" replace={true} />)
|
||||||
// : false && location.pathname == '/auth/login' ?
|
// : false && location.pathname == '/auth/login' ?
|
||||||
// (<Navigate to="/dashboard" replace={true} />)
|
// (<Navigate to="/dashboard" replace={true} />)
|
||||||
// : (
|
// : (
|
||||||
// <AuthContext.Provider
|
// <AuthContext.Provider
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
import { capitalCase } from 'change-case';
|
import { capitalCase } from 'change-case';
|
||||||
import { Link as RouterLink } from 'react-router-dom';
|
|
||||||
// @mui
|
// @mui
|
||||||
import { styled } from '@mui/material/styles';
|
import { styled } from '@mui/material/styles';
|
||||||
import { Box, Card, Stack, Link, Alert, Tooltip, Container, Typography } from '@mui/material';
|
import { Box, Card, Divider, Grid, Link, Stack, Tooltip, Typography } from '@mui/material';
|
||||||
// routes
|
|
||||||
import { PATH_AUTH } from '../../routes/paths';
|
|
||||||
// hooks
|
// hooks
|
||||||
import useAuth from '../../hooks/useAuth';
|
import useAuth from '../../hooks/useAuth';
|
||||||
import useResponsive from '../../hooks/useResponsive';
|
|
||||||
// components
|
// components
|
||||||
import Page from '../../components/Page';
|
import Page from '../../components/Page';
|
||||||
import Logo from '../../components/Logo';
|
|
||||||
import Image from '../../components/Image';
|
import Image from '../../components/Image';
|
||||||
// sections
|
// sections
|
||||||
import { LoginForm } from '../../sections/auth/login';
|
import { LoginEmailForm, LoginPhoneForm } from '../../sections/auth/login';
|
||||||
|
import Logo from '../../components/Logo';
|
||||||
|
// react
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -21,117 +19,82 @@ const RootStyle = styled('div')(({ theme }) => ({
|
|||||||
[theme.breakpoints.up('md')]: {
|
[theme.breakpoints.up('md')]: {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
},
|
},
|
||||||
}));
|
|
||||||
|
|
||||||
const HeaderStyle = styled('header')(({ theme }) => ({
|
|
||||||
top: 0,
|
|
||||||
zIndex: 9,
|
|
||||||
lineHeight: 0,
|
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
position: 'absolute',
|
|
||||||
padding: theme.spacing(3),
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
[theme.breakpoints.up('md')]: {
|
|
||||||
alignItems: 'flex-start',
|
|
||||||
padding: theme.spacing(7, 5, 0, 7),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const SectionStyle = styled(Card)(({ theme }) => ({
|
|
||||||
width: '100%',
|
|
||||||
maxWidth: 464,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
justifyContent: 'center',
|
|
||||||
margin: theme.spacing(2, 0, 2, 2),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const ContentStyle = styled('div')(({ theme }) => ({
|
|
||||||
maxWidth: 480,
|
|
||||||
margin: 'auto',
|
|
||||||
display: 'flex',
|
|
||||||
minHeight: '100vh',
|
minHeight: '100vh',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
padding: theme.spacing(12, 0),
|
alignItems: 'center',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ContentStyle = styled(Card)(({ theme }) => ({
|
||||||
|
[theme.breakpoints.up('md')]: {
|
||||||
|
maxHeight: '600px',
|
||||||
|
maxWidth: '1000px',
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// ----------------------------------------------------------------------
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const { method } = useAuth();
|
const { method } = useAuth();
|
||||||
|
const [formPhone, setFormPhone] = useState(false);
|
||||||
|
|
||||||
const smUp = useResponsive('up', 'sm');
|
const handlerChange = (event: any, setForm: boolean) => {
|
||||||
|
event.preventDefault();
|
||||||
const mdUp = useResponsive('up', 'md');
|
setFormPhone(setForm);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page title="Login">
|
<Page title="Login">
|
||||||
<RootStyle>
|
<RootStyle>
|
||||||
<HeaderStyle>
|
<ContentStyle>
|
||||||
<Logo sx={{ width: 150, height: 150 }} />
|
<Grid container>
|
||||||
{smUp && (
|
<Grid item xs={6}>
|
||||||
<Typography variant="body2" sx={{ mt: { md: -2 } }}>
|
<Image visibleByDefault disabledEffect src="/images/login-image.gif" alt="login" />
|
||||||
Has problem with your account? {''}
|
</Grid>
|
||||||
<Link variant="subtitle2" component={RouterLink} to="#" onClick={(e) => {
|
<Grid item xs={6} sx={{ padding: 3 }}>
|
||||||
window.location.href = "mailto:admin@linksehat.com";
|
<Stack direction="row" alignItems="center" sx={{ mb: 5 }}>
|
||||||
e.preventDefault();
|
<Tooltip title={capitalCase(method)} placement="left">
|
||||||
}}>
|
<Logo sx={{ width: 90, height: 90 }} />
|
||||||
Contact Us
|
</Tooltip>
|
||||||
</Link>
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</HeaderStyle>
|
|
||||||
|
|
||||||
{/* {mdUp && (
|
<Box sx={{ flexGrow: 1 }}>
|
||||||
<SectionStyle>
|
<Typography variant="h4" gutterBottom>
|
||||||
<Typography variant="h3" sx={{ px: 5, mt: 10, mb: 5 }}>
|
Sign in to LinkSehat
|
||||||
Hi, Welcome Back
|
</Typography>
|
||||||
</Typography>
|
<Typography variant="body1" sx={{ color: 'text.secondary' }}>
|
||||||
<Image
|
Enter your details below.
|
||||||
visibleByDefault
|
</Typography>
|
||||||
disabledEffect
|
</Box>
|
||||||
src="https://minimal-assets-api.vercel.app/assets/illustrations/illustration_login.png"
|
</Stack>
|
||||||
alt="login"
|
|
||||||
/>
|
|
||||||
</SectionStyle>
|
|
||||||
)} */}
|
|
||||||
|
|
||||||
<Container maxWidth="sm">
|
{formPhone === false ? <LoginEmailForm /> : <LoginPhoneForm />}
|
||||||
<ContentStyle>
|
|
||||||
<Stack direction="row" alignItems="center" sx={{ mb: 5 }}>
|
|
||||||
<Box sx={{ flexGrow: 1 }}>
|
|
||||||
<Typography variant="h4" gutterBottom>
|
|
||||||
Sign in to LinkSehat
|
|
||||||
</Typography>
|
|
||||||
<Typography sx={{ color: 'text.secondary' }}>Enter your details below.</Typography>
|
|
||||||
</Box>
|
|
||||||
|
|
||||||
<Tooltip title={capitalCase(method)} placement="right">
|
<Divider sx={{ marginTop: 5 }}>Atau</Divider>
|
||||||
<>
|
|
||||||
<Image
|
|
||||||
disabledEffect
|
|
||||||
src={`https://minimal-assets-api.vercel.app/assets/icons/auth/ic_${method}.png`}
|
|
||||||
sx={{ width: 32, height: 32 }}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
</Tooltip>
|
|
||||||
</Stack>
|
|
||||||
|
|
||||||
<LoginForm />
|
<Stack sx={{ marginTop: 5 }}>
|
||||||
|
{formPhone === false ? (
|
||||||
{false && !smUp && (
|
<Link
|
||||||
<Typography variant="body2" align="center" sx={{ mt: 3 }}>
|
href=""
|
||||||
Don’t have an account?{' '}
|
align="center"
|
||||||
<Link variant="subtitle2" component={RouterLink} to={PATH_AUTH.register}>
|
underline="hover"
|
||||||
Get started
|
onClick={(event) => handlerChange(event, true)}
|
||||||
</Link>
|
>
|
||||||
</Typography>
|
Masuk menggunakan nomor handphone
|
||||||
)}
|
</Link>
|
||||||
</ContentStyle>
|
) : (
|
||||||
</Container>
|
<Link
|
||||||
|
href=""
|
||||||
|
align="center"
|
||||||
|
underline="hover"
|
||||||
|
onClick={(event) => handlerChange(event, false)}
|
||||||
|
>
|
||||||
|
Masuk menggunakan email
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
</Stack>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</ContentStyle>
|
||||||
</RootStyle>
|
</RootStyle>
|
||||||
</Page>
|
</Page>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export default function LoginForm() {
|
|||||||
|
|
||||||
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('Email must be a valid email address').required('Email is required'),
|
||||||
password: Yup.string().required('Password is required'),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const methods = useForm<FormValuesProps>({
|
const methods = useForm<FormValuesProps>({
|
||||||
@@ -71,6 +70,7 @@ export default function LoginForm() {
|
|||||||
type="submit"
|
type="submit"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
loading={isSubmitting}
|
loading={isSubmitting}
|
||||||
|
sx={{ marginTop: 2 }}
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
</LoadingButton>
|
</LoadingButton>
|
||||||
83
frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx
Executable file
83
frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx
Executable file
@@ -0,0 +1,83 @@
|
|||||||
|
import * as Yup from 'yup';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import axios from '../../../utils/axios';
|
||||||
|
// form
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
|
// @mui
|
||||||
|
import { Stack, Alert, InputAdornment } from '@mui/material';
|
||||||
|
import { LoadingButton } from '@mui/lab';
|
||||||
|
// components
|
||||||
|
import { FormProvider, RHFTextField } from '../../../components/hook-form';
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
type FormValuesProps = {
|
||||||
|
phone: string;
|
||||||
|
afterSubmit?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function LoginPhoneForm() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const LoginSchema = Yup.object().shape({
|
||||||
|
phone: Yup.string().required('Phone is required'),
|
||||||
|
});
|
||||||
|
|
||||||
|
const defaultValues = {
|
||||||
|
phone: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const methods = useForm<FormValuesProps>({
|
||||||
|
resolver: yupResolver(LoginSchema),
|
||||||
|
defaultValues,
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
reset,
|
||||||
|
setError,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors, isSubmitting },
|
||||||
|
} = methods;
|
||||||
|
|
||||||
|
const onSubmit = async (data: FormValuesProps) => {
|
||||||
|
try {
|
||||||
|
await axios.post('/otp-request', { phone_or_email: 0 + data.phone });
|
||||||
|
// console.log(response);
|
||||||
|
// navigate('/dashboard');
|
||||||
|
} catch (error: any) {
|
||||||
|
reset();
|
||||||
|
setError('afterSubmit', { ...error, message: error.response.data.message });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormProvider methods={methods} onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<Stack spacing={3}>
|
||||||
|
<Alert severity="info">Masukkan akun yang telah terdaftar</Alert>
|
||||||
|
{!!errors.afterSubmit && <Alert severity="error">{errors.afterSubmit.message}</Alert>}
|
||||||
|
|
||||||
|
<RHFTextField
|
||||||
|
name="phone"
|
||||||
|
label="Phone Number"
|
||||||
|
type={'number'}
|
||||||
|
InputProps={{
|
||||||
|
startAdornment: <InputAdornment position="start">+62</InputAdornment>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<LoadingButton
|
||||||
|
fullWidth
|
||||||
|
size="large"
|
||||||
|
type="submit"
|
||||||
|
variant="contained"
|
||||||
|
loading={isSubmitting}
|
||||||
|
sx={{ marginTop: 2 }}
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</LoadingButton>
|
||||||
|
</FormProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
export { default as LoginForm } from './LoginForm';
|
export { default as LoginEmailForm } from './LoginEmailForm';
|
||||||
|
export { default as LoginPhoneForm } from './LoginPhoneForm';
|
||||||
|
|||||||
@@ -4205,7 +4205,7 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"prop-types" "^15.5.7"
|
"prop-types" "^15.5.7"
|
||||||
|
|
||||||
"react-dom@^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0 || ^17.0 || ^18.0.0", "react-dom@^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x", "react-dom@^16 || ^17", "react-dom@^16.6.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", "react-dom@^17.0.2", "react-dom@^17.0.2 || ^18.0.0", "react-dom@>=16.6.0", "react-dom@>=16.8", "react-dom@>=16.8 || ^17.0.0 || ^18.0.0":
|
"react-dom@^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^18.0.0", "react-dom@^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0 || ^17.0 || ^18.0.0", "react-dom@^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x", "react-dom@^16 || ^17", "react-dom@^16.6.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", "react-dom@^17.0.2", "react-dom@^17.0.2 || ^18.0.0", "react-dom@>=16.6.0", "react-dom@>=16.8", "react-dom@>=16.8 || ^17.0.0 || ^18.0.0":
|
||||||
"integrity" "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA=="
|
"integrity" "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA=="
|
||||||
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
|
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz"
|
||||||
"version" "17.0.2"
|
"version" "17.0.2"
|
||||||
@@ -4277,6 +4277,13 @@
|
|||||||
"lodash.debounce" "^4.0.8"
|
"lodash.debounce" "^4.0.8"
|
||||||
"lodash.throttle" "^4.1.1"
|
"lodash.throttle" "^4.1.1"
|
||||||
|
|
||||||
|
"react-number-format@^5.1.1":
|
||||||
|
"integrity" "sha512-wLRUP9G0B79hZrTRgj380/ygtKRJWrCnTBIlQkoYAp/AMNQK7qGkUAWM5EpS1YRxz1w003UmZoon8WRutnlFuw=="
|
||||||
|
"resolved" "https://registry.npmjs.org/react-number-format/-/react-number-format-5.1.1.tgz"
|
||||||
|
"version" "5.1.1"
|
||||||
|
dependencies:
|
||||||
|
"prop-types" "^15.7.2"
|
||||||
|
|
||||||
"react-quill@2.0.0-beta.4":
|
"react-quill@2.0.0-beta.4":
|
||||||
"integrity" "sha512-KyAHvAlPjP4xLElKZJefMth91Z6FbbXRvq9OSu6xN3KBaoasLP9p+3dcxg4Ywr4tBlpMGXcPszYSAgd5CpJ45Q=="
|
"integrity" "sha512-KyAHvAlPjP4xLElKZJefMth91Z6FbbXRvq9OSu6xN3KBaoasLP9p+3dcxg4Ywr4tBlpMGXcPszYSAgd5CpJ45Q=="
|
||||||
"resolved" "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0-beta.4.tgz"
|
"resolved" "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0-beta.4.tgz"
|
||||||
@@ -4316,7 +4323,7 @@
|
|||||||
"loose-envify" "^1.4.0"
|
"loose-envify" "^1.4.0"
|
||||||
"prop-types" "^15.6.2"
|
"prop-types" "^15.6.2"
|
||||||
|
|
||||||
"react@^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0 || ^17.0 || ^18.0.0", "react@^15.0.0 || ^16.0.0 || ^17.0.0|| ^18.0.0", "react@^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x", "react@^16 || ^17", "react@^16.6.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^17.0.0 || ^18.0.0", "react@^17.0.2", "react@^17.0.2 || ^18.0.0", "react@>= 16.8 || 18.0.0", "react@>=0.13", "react@>=16", "react@>=16.6.0", "react@>=16.8", "react@>=16.8 || ^17.0.0 || ^18.0.0", "react@>=16.8.0", "react@17.0.2":
|
"react@^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^18.0.0", "react@^0.14.9 || ^15.3.0 || ^16.0.0-rc || ^16.0 || ^17.0 || ^18.0.0", "react@^15.0.0 || ^16.0.0 || ^17.0.0|| ^18.0.0", "react@^15.x.x || ^16.x.x || ^17.x.x || ^18.x.x", "react@^16 || ^17", "react@^16.6.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^17.0.0 || ^18.0.0", "react@^17.0.2", "react@^17.0.2 || ^18.0.0", "react@>= 16.8 || 18.0.0", "react@>=0.13", "react@>=16", "react@>=16.6.0", "react@>=16.8", "react@>=16.8 || ^17.0.0 || ^18.0.0", "react@>=16.8.0", "react@17.0.2":
|
||||||
"integrity" "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA=="
|
"integrity" "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA=="
|
||||||
"resolved" "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
|
"resolved" "https://registry.npmjs.org/react/-/react-17.0.2.tgz"
|
||||||
"version" "17.0.2"
|
"version" "17.0.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user