diff --git a/Modules/Client/Http/Controllers/Api/AuthController.php b/Modules/Client/Http/Controllers/Api/AuthController.php index 9761eafa..26eaee11 100755 --- a/Modules/Client/Http/Controllers/Api/AuthController.php +++ b/Modules/Client/Http/Controllers/Api/AuthController.php @@ -33,17 +33,13 @@ class AuthController extends Controller } if (filter_var($request->phone_or_email, FILTER_VALIDATE_EMAIL)) { - $user = User::query()->update([ - 'email' => $request->phone_or_email - ], [ + User::query()->find($user->id)->update([ 'email' => $request->phone_or_email, 'otp' => rand(1000, 9999), 'otp_created_at' => now() ]); } else { - $user = User::query()->update([ - 'phone' => $request->phone_or_email - ], [ + User::query()->find($user->id)->update([ 'phone' => $request->phone_or_email, 'otp' => rand(1000, 9999), 'otp_created_at' => now() diff --git a/frontend/client-portal/package-lock.json b/frontend/client-portal/package-lock.json index a11ba384..9c56faf2 100755 --- a/frontend/client-portal/package-lock.json +++ b/frontend/client-portal/package-lock.json @@ -42,6 +42,7 @@ "react-hook-form": "^7.34.2", "react-intersection-observer": "^8.34.0", "react-lazy-load-image-component": "^1.5.5", + "react-number-format": "^5.1.1", "react-quill": "2.0.0-beta.4", "react-router": "^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" } }, + "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": { "version": "2.0.0-beta.4", "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0-beta.4.tgz", @@ -14632,6 +14645,14 @@ "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": { "version": "2.0.0-beta.4", "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0-beta.4.tgz", diff --git a/frontend/client-portal/package.json b/frontend/client-portal/package.json index 0ed386bd..0ee98044 100755 --- a/frontend/client-portal/package.json +++ b/frontend/client-portal/package.json @@ -71,6 +71,7 @@ "react-hook-form": "^7.34.2", "react-intersection-observer": "^8.34.0", "react-lazy-load-image-component": "^1.5.5", + "react-number-format": "^5.1.1", "react-quill": "2.0.0-beta.4", "react-router": "^6.3.0", "react-router-dom": "^6.3.0", diff --git a/frontend/client-portal/public/images/login-image.gif b/frontend/client-portal/public/images/login-image.gif new file mode 100644 index 00000000..5f123cf8 Binary files /dev/null and b/frontend/client-portal/public/images/login-image.gif differ diff --git a/frontend/client-portal/src/@types/auth.ts b/frontend/client-portal/src/@types/auth.ts index 3df73c76..9d25638d 100755 --- a/frontend/client-portal/src/@types/auth.ts +++ b/frontend/client-portal/src/@types/auth.ts @@ -26,8 +26,7 @@ export type JWTContextType = { isInitialized: boolean; user: AuthUser; method: 'jwt'; - login: (email: string, password: string) => Promise; - register: (email: string, password: string, firstName: string, lastName: string) => Promise; + // login: (phone_or_email: string) => Promise; logout: () => Promise; }; diff --git a/frontend/client-portal/src/contexts/LaravelAuthContext.tsx b/frontend/client-portal/src/contexts/LaravelAuthContext.tsx index d30559b5..06112bb0 100755 --- a/frontend/client-portal/src/contexts/LaravelAuthContext.tsx +++ b/frontend/client-portal/src/contexts/LaravelAuthContext.tsx @@ -80,15 +80,15 @@ type AuthProviderProps = { function AuthProvider({ children }: AuthProviderProps) { const [state, dispatch] = useReducer(JWTReducer, initialState); - let location = useLocation(); + // let location = useLocation(); useEffect(() => { const initialize = async () => { - console.log('initialize', state) + console.log('initialize', state); try { const accessToken = getSession(); - - console.log('') + + console.log(''); if (accessToken) { setSession(accessToken); @@ -125,56 +125,56 @@ function AuthProvider({ children }: AuthProviderProps) { initialize(); }, []); - const login = async (email: string, password: string) => { - return axios - .post('/login', { email, password }) - .then((response) => { - const { user, token } = response.data; - setSession(token); + // const login = async (phone_or_email: string) => { + // axios + // .post('/otp-request', { phone_or_email }) + // .then((response: any) => { + // const { user, token } = response.data; + // setSession(token); - dispatch({ - type: Types.Login, - payload: { - user, - } - }); - }) - .catch(error => { - if (error.response.status !== 404) throw error.response - if (error.response.status !== 422) throw error.response - }) - }; + // dispatch({ + // type: Types.Login, + // payload: { + // user, + // }, + // }); + // }) + // .catch((error) => { + // if (error.response.status !== 404) throw error.response; + // if (error.response.status !== 422) throw error.response; + // }); + // }; - const register = async (email: string, password: string, firstName: string, lastName: string) => { - const response = await axios.post('/api/register', { - email, - password, - firstName, - lastName, - }); - const { accessToken, user } = response.data; + // const register = async (email: string, password: string, firstName: string, lastName: string) => { + // const response = await axios.post('/api/register', { + // email, + // password, + // firstName, + // lastName, + // }); + // const { accessToken, user } = response.data; - window.localStorage.setItem('accessToken', accessToken); - dispatch({ - type: Types.Register, - payload: { - user, - }, - }); - }; + // window.localStorage.setItem('accessToken', accessToken); + // dispatch({ + // type: Types.Register, + // payload: { + // user, + // }, + // }); + // }; const logout = async () => { + await axios.post('/logout', { token: getSession() }); setSession(null); dispatch({ type: Types.Logout }); }; - return ( {children} @@ -182,9 +182,9 @@ function AuthProvider({ children }: AuthProviderProps) { ); // if (state.isInitialized) { - // return (!state.isAuthenticated && location.pathname !== '/auth/login') ? + // return (!state.isAuthenticated && location.pathname !== '/auth/login') ? // () - // : false && location.pathname == '/auth/login' ? + // : false && location.pathname == '/auth/login' ? // () // : ( // ({ [theme.breakpoints.up('md')]: { 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', flexDirection: 'column', 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() { const { method } = useAuth(); + const [formPhone, setFormPhone] = useState(false); - const smUp = useResponsive('up', 'sm'); - - const mdUp = useResponsive('up', 'md'); + const handlerChange = (event: any, setForm: boolean) => { + event.preventDefault(); + setFormPhone(setForm); + }; return ( - - - {smUp && ( - - Has problem with your account? {''} - { - window.location.href = "mailto:admin@linksehat.com"; - e.preventDefault(); - }}> - Contact Us - - - )} - + + + + login + + + + + + - {/* {mdUp && ( - - - Hi, Welcome Back - - login - - )} */} + + + Sign in to LinkSehat + + + Enter your details below. + + + - - - - - - Sign in to LinkSehat - - Enter your details below. - + {formPhone === false ? : } - - <> - - - - + Atau - - - {false && !smUp && ( - - Don’t have an account?{' '} - - Get started - - - )} - - + + {formPhone === false ? ( + handlerChange(event, true)} + > + Masuk menggunakan nomor handphone + + ) : ( + handlerChange(event, false)} + > + Masuk menggunakan email + + )} + + + + ); diff --git a/frontend/client-portal/src/sections/auth/login/LoginForm.tsx b/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx similarity index 97% rename from frontend/client-portal/src/sections/auth/login/LoginForm.tsx rename to frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx index 77fd65ea..89926836 100755 --- a/frontend/client-portal/src/sections/auth/login/LoginForm.tsx +++ b/frontend/client-portal/src/sections/auth/login/LoginEmailForm.tsx @@ -26,7 +26,6 @@ export default function LoginForm() { const LoginSchema = Yup.object().shape({ 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({ @@ -71,6 +70,7 @@ export default function LoginForm() { type="submit" variant="contained" loading={isSubmitting} + sx={{ marginTop: 2 }} > Login diff --git a/frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx b/frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx new file mode 100755 index 00000000..0ea7346e --- /dev/null +++ b/frontend/client-portal/src/sections/auth/login/LoginPhoneForm.tsx @@ -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({ + 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 ( + + + Masukkan akun yang telah terdaftar + {!!errors.afterSubmit && {errors.afterSubmit.message}} + + +62, + }} + /> + + + + Login + + + ); +} diff --git a/frontend/client-portal/src/sections/auth/login/index.ts b/frontend/client-portal/src/sections/auth/login/index.ts index 5c49358d..be6c6913 100755 --- a/frontend/client-portal/src/sections/auth/login/index.ts +++ b/frontend/client-portal/src/sections/auth/login/index.ts @@ -1 +1,2 @@ -export { default as LoginForm } from './LoginForm'; +export { default as LoginEmailForm } from './LoginEmailForm'; +export { default as LoginPhoneForm } from './LoginPhoneForm'; diff --git a/frontend/client-portal/yarn.lock b/frontend/client-portal/yarn.lock index f7917e52..ffe770fc 100755 --- a/frontend/client-portal/yarn.lock +++ b/frontend/client-portal/yarn.lock @@ -4205,7 +4205,7 @@ dependencies: "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==" "resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" "version" "17.0.2" @@ -4277,6 +4277,13 @@ "lodash.debounce" "^4.0.8" "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": "integrity" "sha512-KyAHvAlPjP4xLElKZJefMth91Z6FbbXRvq9OSu6xN3KBaoasLP9p+3dcxg4Ywr4tBlpMGXcPszYSAgd5CpJ45Q==" "resolved" "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0-beta.4.tgz" @@ -4316,7 +4323,7 @@ "loose-envify" "^1.4.0" "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==" "resolved" "https://registry.npmjs.org/react/-/react-17.0.2.tgz" "version" "17.0.2"