diff --git a/Modules/Internal/Http/Controllers/Api/AuthController.php b/Modules/Internal/Http/Controllers/Api/AuthController.php index ec95a239..ed86e0e0 100755 --- a/Modules/Internal/Http/Controllers/Api/AuthController.php +++ b/Modules/Internal/Http/Controllers/Api/AuthController.php @@ -18,8 +18,8 @@ class AuthController extends Controller ]); $user = User::query() - ->where('email', $request->email) - ->first(); + ->where('email', $request->email) + ->first(); if (!$user) { return response(['message' => 'User Tidak Ditemukan'], 404); @@ -43,4 +43,76 @@ class AuthController extends Controller return response(['message' => 'Berhasil Logout.']); } + + public function resetPassword(Request $request) + { + $user = Auth::user(); + + $request->validate([ + 'old_password' => 'required', + 'new_password' => 'required', + 'confirm_new_password' => 'required' + ]); + + if (!Hash::check($request['old_password'], $user->password)) { + return response(['message' => 'Password Salah'], 403); + } + + if ($request["new_password"] != $request["confirm_new_password"]) { + return response([ + 'message' => "Password Tidak Sama" + ]); + } + + $user->update([ + 'password' => Hash::make($request->confirm_new_password), + ]); + return response()->json($user); + } + + public function verifyEmail(Request $request) + { + $request->validate([ + 'email' => 'required|email', + ]); + + $user = User::query() + ->where('email', $request->email) + ->first(); + + if (!$user) { + return response(['message' => 'User Tidak Ditemukan'], 404); + } + + return response()->json($user); + } + + public function forgetPassword(Request $request) + { + return $request->all(); + $request->validate([ + 'email' => 'required|email', + 'new_password' => 'required', + 'confirm_new_password' => 'required' + ]); + + $user = User::query() + ->where('email', $request->email) + ->first(); + + if (!$user) { + return response(['message' => 'User Tidak Ditemukan'], 404); + } + + if ($request["new_password"] != $request["confirm_new_password"]) { + return response([ + 'message' => "Password Tidak Sama" + ]); + } + + $user->update([ + 'password' => Hash::make($request->confirm_new_password), + ]); + return response()->json($user); + } } diff --git a/Modules/Internal/Routes/api.php b/Modules/Internal/Routes/api.php index 4457bdbb..cc27113e 100755 --- a/Modules/Internal/Routes/api.php +++ b/Modules/Internal/Routes/api.php @@ -47,6 +47,8 @@ Route::prefix('internal')->group(function () { Route::get('/user', function (Request $request) { return $request->user(); }); + Route::put('reset-password', [AuthController::class, 'resetPassword'])->name('resetPassword'); + Route::resource('corporates', CorporateController::class); Route::put('corporates/{corporate_id}/activation', [CorporateController::class, 'activation']); diff --git a/frontend/dashboard/public/image/overlay.png b/frontend/dashboard/public/image/overlay.png new file mode 100644 index 00000000..a69d15cf Binary files /dev/null and b/frontend/dashboard/public/image/overlay.png differ diff --git a/frontend/dashboard/src/layouts/dashboard/header/AccountPopover.tsx b/frontend/dashboard/src/layouts/dashboard/header/AccountPopover.tsx index fe68232e..519a9c3d 100755 --- a/frontend/dashboard/src/layouts/dashboard/header/AccountPopover.tsx +++ b/frontend/dashboard/src/layouts/dashboard/header/AccountPopover.tsx @@ -5,7 +5,7 @@ import { Box, Divider, Typography, Stack, MenuItem, Avatar } from '@mui/material // components import MenuPopover from '../../../components/MenuPopover'; import { IconButtonAnimate } from '../../../components/animate'; -import { useNavigate } from "react-router-dom"; +import { useNavigate } from 'react-router-dom'; import useAuth from '../../../hooks/useAuth'; // ---------------------------------------------------------------------- @@ -17,7 +17,7 @@ const MENU_OPTIONS = [ }, { label: 'Profile', - linkTo: '/', + linkTo: '/profile', }, { label: 'Settings', @@ -43,7 +43,7 @@ export default function AccountPopover() { const handleLogout = () => { logout(); navigate('/auth/login'); - } + }; return ( <> @@ -97,7 +97,13 @@ export default function AccountPopover() { {MENU_OPTIONS.map((option) => ( - + { + handleClose(); + navigate(option.linkTo); + }} + > {option.label} ))} @@ -105,7 +111,9 @@ export default function AccountPopover() { - Logout + + Logout + ); diff --git a/frontend/dashboard/src/pages/Profile/FormPassword.tsx b/frontend/dashboard/src/pages/Profile/FormPassword.tsx new file mode 100644 index 00000000..d0afd415 --- /dev/null +++ b/frontend/dashboard/src/pages/Profile/FormPassword.tsx @@ -0,0 +1,347 @@ +import * as Yup from 'yup'; +import { enqueueSnackbar, useSnackbar } from 'notistack'; +import { useNavigate } from 'react-router-dom'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import { yupResolver } from '@hookform/resolvers/yup'; +// @mui +import { styled } from '@mui/material/styles'; +import { LoadingButton } from '@mui/lab'; +import { + Box, + Button, + Grid, + Stack, + Typography, + Chip, + Autocomplete, + InputAdornment, + IconButton, +} from '@mui/material'; + +// components +import { FormProvider, RHFTextField, RHFSwitch } from '../../components/hook-form'; +import axios from '../../utils/axios'; +import { JWTContextType } from '../../@types/auth'; + +// @mui +import { LinearProgress, linearProgressClasses, FormControlLabel } from '@mui/material'; +import Checkbox from '@mui/material/Checkbox'; +// components +import MuiDialog from '../../components/MuiDialog'; +// React +import { ReactElement } from 'react'; +import { Users } from '../../@types/user'; +import Iconify from '../../components/Iconify'; + +// ---------------------------------------------------------------------- + +const HeaderStyle = styled('header')(({ theme }) => ({ + display: 'flex', + alignItems: 'center', + padding: theme.spacing(2), + justifyContent: 'space-between', +})); + +const LabelStyle = styled(Typography)(({ theme }) => ({ + ...theme.typography.h6, + marginBottom: theme.spacing(2), + marginTop: theme.spacing(2), +})); +type DataContent = { + info: string; + date: string; + time: string; +}; + +type MuiDialogProps = { + title?: { + name?: string; + icon?: string; + }; + openDialog: boolean; + setOpenDialog: Function; + content?: ReactElement; + data?: DataContent[]; +}; + +type FormValuesProps = { + value: string; + active: boolean; +}; + +// ---------------------------------------------------------------------- + +// ---------------------------------------------------------------------- + +const DialogFormPassword = ({ title, openDialog, setOpenDialog, data }: MuiDialogProps) => { + const [isDisabledCheckbox, setIsDisabledCheckbox] = useState(false); + const [isDisabledButton, setIsDisabledButton] = useState(true); + const navigate = useNavigate(); + + const id = data; + + const isEdit = id ? true : false; + + // eslint-disable-next-line @typescript-eslint/no-redeclare + // const [currentUsers, setCurrentUsers] = useState(); + const [currentUsers, setCurrentUsers] = useState(); + console.log('usernya masuk', currentUsers); + useEffect(() => { + if (isEdit) { + axios.get('/user/' + id).then((res) => { + setCurrentUsers(res.data); + }); + } + }, [id]); + + const Title = styled(Typography)(({ theme }) => ({ + ...theme.typography.h4, + boxShadow: 'none', + fontWeight: 700, + color: '#005B7F', + })); + + console.log('currentUsers', currentUsers); + + console.log('data Id masuk', data); + + // const NewCorporateSchema = Yup.object().shape({ + // active: Yup.boolean().required('Corporate Status is required'), + // // file: Yup.boolean().required('Corporate Status is required'), + // }); + + // const defaultValues = useMemo( + // () => ({ + // id: currentUsers?.id || '', + // name: currentUsers?.name || '', + // value: currentUsers?.value || '', + // active: currentUsers?.active === 1 ? true : false, + // }), + // // eslint-disable-next-line react-hooks/exhaustive-deps + // [currentUsers] + // ); + + interface FormValuesProps extends Partial { + taxes: boolean; + inStock: boolean; + } + + type Props = { + isEdit: boolean; + currentBanners?: Banner; + }; + + // const profileSchema = Yup.object().shape({ + // // password: Yup.string().required('katasandilama is required'), + // // katasandibaru: Yup.string().required('katasandibaru is required'), + // // konfirmasikatasandi: Yup.string().required('konfirmasikatasandi is required'), + // }); + + // const defaultValues = useMemo( + // () => ({ + // new_password: currentUsers?.new_password, + // password2: currentUsers?.password2, + // }), + // // eslint-disable-next-line react-hooks/exhaustive-deps + // [currentUsers] + // ); + + const methods = useForm({ + // resolver: yupResolver(profileSchema), + // defaultValues, + }); + + // console.log('defaultValues', defaultValues); + const { + reset, + watch, + control, + setValue, + setError, + getValues, + handleSubmit, + formState: { isSubmitting }, + } = methods; + + const values = watch(); + + // useEffect(() => { + // if (isEdit && currentUsers) { + // reset(defaultValues); + // } + // if (!isEdit) { + // reset(defaultValues); + // } + + // // eslint-disable-next-line react-hooks/exhaustive-deps + // }, [isEdit, currentUsers]); + + useEffect(() => { + if (openDialog === false) { + reset(); + } + }, [openDialog, reset]); + + const onSubmit = async (data: FormValuesProps) => { + console.log('data', data); + // const formData = new FormData(); + // formData.append('password', password); + // formData.append('passworrd') + + try { + if (!isEdit) { + const response = await axios.post('/reset-password', data); + } else { + const response = await axios.put('/reset-password/', data); + } + + reset(); + enqueueSnackbar( + !isEdit ? 'Password Created Successfully!' : 'Password Updated Successfully!', + { + variant: 'success', + } + ); + + setOpenDialog(false); + // navigate(0); + + // window.location.reload(); + // navigate('/general/bantuan/contact', { replace: true }); + } catch (error: any) { + if (error && error.response.status === 422) { + for (const [key, value] of Object.entries(error.response.data.errors)) { + setError(key, { message: value[0] }); + enqueueSnackbar(value[0] ?? 'Failed Processing Request', { variant: 'error' }); + } + } else { + enqueueSnackbar(error.message ?? 'Failed Processing Request', { variant: 'error' }); + } + } + + const ascent = document?.querySelector('ascent'); + if (ascent != null) { + ascent.innerHTML = ''; + } + }; + + const [showPasswordOld, setShowPasswordOld] = useState(false); + const [showPasswordNew, setShowPasswordNew] = useState(false); + const [showPasswordConfirmNew, setShowPasswordConfirmNew] = useState(false); + + const getContent = () => ( + + + Ubah Kata Sandi + + + + + Kata Sandi Lama + + setShowPasswordOld(!showPasswordOld)} edge="end"> + + + + ), + }} + /> + + + + Kata Sandi Baru + + setShowPasswordNew(!showPasswordNew)} edge="end"> + + + + ), + }} + /> + + + + Konfirmasi Kata Sandi + + setShowPasswordConfirmNew(!showPasswordConfirmNew)} + edge="end" + > + + + + ), + }} + /> + + + + + + + + {!isEdit ? 'Simpan' : 'Simpan'} + + + + + + + + ); + + return ( + + ); +}; + +export default DialogFormPassword; diff --git a/frontend/dashboard/src/pages/Profile/Index.tsx b/frontend/dashboard/src/pages/Profile/Index.tsx new file mode 100644 index 00000000..2603c796 --- /dev/null +++ b/frontend/dashboard/src/pages/Profile/Index.tsx @@ -0,0 +1,379 @@ +import { + Box, + Button, + Card, + Collapse, + IconButton, + InputLabel, + MenuItem, + OutlinedInput, + Paper, + Select, + SelectChangeEvent, + Table, + TableBody, + TableCell, + TableContainer, + TableHead, + TableRow, + TextField, + Typography, + Badge, + Tab, + Tabs, + CardHeader, + Stack, + Menu, + ButtonGroup, + Pagination, + Grid, + Chip, + styled, + DialogTitle, + Dialog, + DialogContent, + DialogContentText, + DialogActions, + Switch, + Avatar, + MenuItemClasses, +} from '@mui/material'; +import * as Yup from 'yup'; +import { yupResolver } from '@hookform/resolvers/yup'; + +import { + Link, + NavLink as RouterLink, + useSearchParams, + useNavigate, + useParams, +} from 'react-router-dom'; +// hooks +import React, { ChangeEvent, Component, useEffect, useMemo, useRef, useState } from 'react'; +import useSettings from '../../hooks/useSettings'; +// components +import axios from '../../utils/axios'; +import { LaravelPaginatedData } from '../../@types/paginated-data'; +import { Icd } from '../../@types/diagnosis'; +import BasePagination from '../../components/BasePagination'; +import { Users } from '../../@types/user'; +import CreateIcon from '@mui/icons-material/Create'; +import { Props } from '../../components/editor/index'; +import { red } from '@mui/material/colors'; +import { borderRadius, margin, padding } from '@mui/system'; + +import FormPassword from './FormPassword'; +import OrganizationsForm from './FormPassword'; +import DialogTopUpLimit from './DialogTopUpLimit'; +import { number } from 'yup/lib/locale'; +import AccountBoxIcon from '@mui/icons-material/AccountBox'; +import PersonIcon from '@mui/icons-material/Person'; +import BadgeIcon from '@mui/icons-material/Badge'; +import CallIcon from '@mui/icons-material/Call'; +import MailIcon from '@mui/icons-material/Mail'; +import LocationOnIcon from '@mui/icons-material/LocationOn'; +import LockIcon from '@mui/icons-material/Lock'; +import KeyIcon from '@mui/icons-material/Key'; +import useAuth from '../../hooks/useAuth'; +import LogoutTwoToneIcon from '@mui/icons-material/LogoutTwoTone'; + +export default function List() { + // Generate the every row of the table + const HeaderStyle = styled('header')(({ theme }) => ({ + padding: theme.spacing(5), + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + })); + + const Name = styled(Typography)(({ theme }) => ({ + ...theme.typography.h3, + marginBottom: theme.spacing(1), + marginTop: theme.spacing(2), + color: '#005B7F', + })); + + const LabelStyle = styled(Typography)(({ theme }) => ({ + ...theme.typography.h6, + marginBottom: theme.spacing(2), + marginTop: theme.spacing(2), + color: '#005B7F', + })); + + const navigate = useNavigate(); + const { organization_id } = useParams(); + const [searchParams, setSearchParams] = useSearchParams(); + const [importResult, setImportResult] = useState(null); + + const { id } = useParams(); + const isEdit = id ? true : false; + + const Item = styled(Paper)(({ theme }) => ({ + textAlign: 'left', + })); + + const ButtonStyle = { + color: '#005B7F', + backgroundColor: '#FFFFFF', + boxShadow: '0px 2px 4px rgba(0, 0, 0, 0.1)', + borderRadius: '8px', + flex: 1, + marginRight: 3, + padding: '13px', + border: '2px solid #005B7F', + fontSize: '15px', + lineHeight: '16px', + textTransform: 'none', + '&:hover': { + backgroundColor: '#005B7F', + color: '#FFFFFF', + }, + }; + + const ButtonStyle2 = { + color: '#CB3A31', + flex: 1, + marginRight: 3, + padding: '13px', + fontSize: '15px', + lineHeight: '16px', + textTransform: 'none', + '&:hover': { + backgroundColor: '#DFE2E1', + }, + }; + + function createData(Users: Users): Users { + return { + ...Users, + }; + } + + const headStyle = { + fontWeight: 'bold', + }; + + function DataUsers() { + const [user, setUser] = useState([]); + useEffect(() => { + axios.get('/user').then((response) => { + setUser(response.data); + }); + }, []); + + const defaultValues = useMemo( + () => ({ + id: user?.id, + email: user?.email, + }), + [user] + ); + + console.log('user', user); + return ( +
+ + Profile + + + + overlay + + + + + + + + + + Rayan Moran + + + Super Admin + + + + + + + + + + + + + + + Profil + + + + + + + + + ID + + {user?.id ? user?.id : '-'} + + + + Nama + + Perdi + + + + Telepon + + 2131231231 + + + + Email + + {user?.email ? user?.email : '-'} + + + + + + + + + + + + Keamanan + + + + + + +
+ +
+
+
+
+
+ + Logout + +
+ {/* + */} +
+
+ ); + } + + const Title = styled(Typography)(({ theme }) => ({ + boxShadow: 'none', + // paddingBottom: theme.spacing(3), + fontWeight: 700, + color: '#000000', + })); + + const TitleRole = styled(Typography)(({ theme }) => ({ + ...theme.typography.h5, + boxShadow: 'none', + fontWeight: 500, + color: '#000000', + })); + + const [openDialog, setOpenDialog] = useState(false); + const [dialogTitle, setDialogTitle] = useState(''); + const [isDialog, setIsDialog] = useState(''); + const [edit, setEdit] = useState(number); + const clickHandler = (isDialog: string) => { + switch (isDialog) { + case 'edit': + setIsDialog(isDialog); + setOpenDialog(true); + break; + default: + break; + } + }; + + const { logout } = useAuth(); + + const handleLogout = () => { + logout(); + navigate('/auth/login'); + }; + + return ( + + + {/* */} + + {/* {dataTableData.map((row) => ( + + ))} */} + + + {isDialog === 'edit' && ( + + )} + + ); +} diff --git a/frontend/dashboard/src/pages/auth/ResetPassword.tsx b/frontend/dashboard/src/pages/auth/ResetPassword.tsx index bd306e90..c10b1782 100755 --- a/frontend/dashboard/src/pages/auth/ResetPassword.tsx +++ b/frontend/dashboard/src/pages/auth/ResetPassword.tsx @@ -59,7 +59,7 @@ export default function ResetPassword() { size="large" component={RouterLink} to={PATH_AUTH.login} - sx={{ mt: 1 }} + sx={{ mt: 3 }} > Back diff --git a/frontend/dashboard/src/routes/index.tsx b/frontend/dashboard/src/routes/index.tsx index 5b5f7ce0..b5ccc843 100755 --- a/frontend/dashboard/src/routes/index.tsx +++ b/frontend/dashboard/src/routes/index.tsx @@ -8,7 +8,6 @@ import LoadingScreen from '../components/LoadingScreen'; import GuestGuard from '../guards/GuestGuard'; import { RegisterForm } from '../sections/auth/register'; import Register from '../pages/auth/Register'; -import ResetPassword from '../pages/auth/ResetPassword'; import VerifyCode from '../pages/auth/VerifyCode'; import { AuthProvider } from '../contexts/LaravelAuthContext'; import AuthGuard from '../guards/AuthGuard'; @@ -53,7 +52,7 @@ export default function Router() { }, // { path: 'login-unprotected', element: }, // { path: 'register-unprotected', element: }, - // { path: 'reset-password', element: }, + { path: 'reset-password', element: }, // { path: 'verify', element: }, ], }, @@ -229,6 +228,10 @@ export default function Router() { path: 'claims/:id', element: , }, + { + path: 'profile', + element: , + }, ], }, // { @@ -265,6 +268,7 @@ export default function Router() { } const Login = Loadable(lazy(() => import('../pages/auth/Login'))); +const ResetPassword = Loadable(lazy(() => import('../pages/auth/ResetPassword'))); // Dashboard const Dashboard = Loadable(lazy(() => import('../pages/Dashboard'))); @@ -329,5 +333,7 @@ const CorporateClaimHistories = Loadable( lazy(() => import('../pages/Corporates/ClaimHistory/Index')) ); +const Profile = Loadable(lazy(() => import('../pages/Profile/Index'))); + const Claims = Loadable(lazy(() => import('../pages/Claims/Index'))); const ClaimsCreate = Loadable(lazy(() => import('../pages/Claims/CreateUpdate'))); diff --git a/frontend/dashboard/src/sections/auth/login/LoginForm.tsx b/frontend/dashboard/src/sections/auth/login/LoginForm.tsx index 72723c23..929d871d 100755 --- a/frontend/dashboard/src/sections/auth/login/LoginForm.tsx +++ b/frontend/dashboard/src/sections/auth/login/LoginForm.tsx @@ -48,7 +48,7 @@ export default function LoginForm() { resolver: yupResolver(LoginSchema), defaultValues, }); - + const { reset, setError, @@ -58,8 +58,8 @@ export default function LoginForm() { const onSubmit = async (data: FormValuesProps) => { try { - const loginResult = await login(data.email, data.password ); - + const loginResult = await login(data.email, data.password); + navigate('/dashboard'); } catch (error) { console.error(error); @@ -75,7 +75,7 @@ export default function LoginForm() { return ( - Email : admin@linksehat.dev & Password : password + Email : admin@linksehat.dev & Password : password {!!errors.afterSubmit && {errors.afterSubmit.message}} diff --git a/frontend/dashboard/src/sections/auth/reset-password/ResetPasswordForm.tsx b/frontend/dashboard/src/sections/auth/reset-password/ResetPasswordForm.tsx index 24ae1d12..b8ddc0df 100755 --- a/frontend/dashboard/src/sections/auth/reset-password/ResetPasswordForm.tsx +++ b/frontend/dashboard/src/sections/auth/reset-password/ResetPasswordForm.tsx @@ -3,17 +3,19 @@ import * as Yup from 'yup'; import { yupResolver } from '@hookform/resolvers/yup'; import { useForm } from 'react-hook-form'; // @mui -import { Stack } from '@mui/material'; +import { Alert, Stack } from '@mui/material'; import { LoadingButton } from '@mui/lab'; // hooks import useIsMountedRef from '../../../hooks/useIsMountedRef'; // components import { FormProvider, RHFTextField } from '../../../components/hook-form'; +import axios from '../../../utils/axios'; // ---------------------------------------------------------------------- type FormValuesProps = { email: string; + afterSubmit?: string; }; type Props = { @@ -35,24 +37,33 @@ export default function ResetPasswordForm({ onSent, onGetEmail }: Props) { const { handleSubmit, - formState: { isSubmitting }, + setError, + formState: { errors, isSubmitting }, } = methods; const onSubmit = async (data: FormValuesProps) => { try { + await axios.post('/verify-email', data); + console.log(data); + + // await new Promise((resolve) => setTimeout(resolve, 500)); await new Promise((resolve) => setTimeout(resolve, 500)); if (isMountedRef.current) { onSent(); onGetEmail(data.email); } } catch (error) { - console.error(error); + console.log(error.response.data); + if (isMountedRef.current) { + setError('afterSubmit', { ...error, message: error.response.data.message }); + } } }; return ( + {!!errors.afterSubmit && {errors.afterSubmit.message}}