386 lines
11 KiB
TypeScript
Executable File
386 lines
11 KiB
TypeScript
Executable File
import { changePassword } from '@/api/Auth'
|
|
import { logout } from '@/rtk/dispatches/auth'
|
|
import { post } from '@/rtk/helpers/apiService'
|
|
import { requirementsPassword } from '@/rtk/helpers/variables'
|
|
import { useAppDispatch, useAppSelector } from '@/rtk/hooks'
|
|
import {
|
|
Box,
|
|
Button,
|
|
Code,
|
|
Group,
|
|
Modal,
|
|
PasswordInput,
|
|
Text,
|
|
TextInput,
|
|
useComputedColorScheme,
|
|
useMantineColorScheme,
|
|
} from '@mantine/core'
|
|
import { notifications } from '@mantine/notifications'
|
|
import {
|
|
IconCalendar,
|
|
IconCalendarClock,
|
|
IconLayoutSidebarLeftExpand,
|
|
IconLayoutSidebarRightExpand,
|
|
IconLogout,
|
|
// IconMail,
|
|
IconMoon,
|
|
IconPasswordUser,
|
|
IconReport,
|
|
IconScan,
|
|
IconSun,
|
|
IconTicket,
|
|
IconDevices,
|
|
} from '@tabler/icons-react'
|
|
import { useCallback, useEffect, useState } from 'react'
|
|
import { useLocation, useNavigate } from 'react-router-dom'
|
|
import PasswordRequirementInput from '../PasswordRequirementInput/PasswordRequirementInput'
|
|
import classes from './NavbarSimpleColored.module.css'
|
|
|
|
const data = [
|
|
// { link: '/dashboard', label: 'Dashboard', icon: IconHome },
|
|
{ link: '/timekeeping', label: 'Timekeeping', icon: IconCalendar },
|
|
{ link: '/tracking', label: 'Check in/out', icon: IconScan },
|
|
{ link: '/worklogs', label: 'Worklogs', icon: IconReport },
|
|
{
|
|
link: '/leave-management',
|
|
label: 'Leave Management',
|
|
icon: IconCalendarClock,
|
|
},
|
|
{ link: '/tickets', label: 'Tickets', icon: IconTicket },
|
|
{ link: '/tickets-management', label: 'Tickets Management', icon: IconDevices },
|
|
// { link: '/jira', label: 'Jira', icon: IconSubtask },
|
|
// { link: '/custom-theme', label: 'Custom Theme', icon: IconBrush },
|
|
// { link: '/general-setting', label: 'General Setting', icon: IconSettings },
|
|
// { link: '/packages', label: 'Packages', icon: IconPackages },
|
|
// { link: '/discounts', label: 'Discounts', icon: IconDiscount2 },
|
|
// { link: '/client', label: 'Clients', icon: IconUsersGroup },
|
|
// { link: '/banner', label: 'Banners', icon: IconPanoramaHorizontal },
|
|
// { link: '/order', label: 'Orders', icon: IconReceipt2 },
|
|
// { link: '/sn-check-history', label: 'Check History', icon: IconListSearch },
|
|
// { link: '/contacts', label: 'Contacts', icon: IconAddressBook },
|
|
]
|
|
|
|
type TProps = {
|
|
isCompactMenu: boolean
|
|
handleSetCompactMenu: () => void
|
|
}
|
|
|
|
const Navbar = ({
|
|
isCompactMenu = false,
|
|
handleSetCompactMenu = () => {},
|
|
}: TProps) => {
|
|
const user = useAppSelector((state) => state.authentication.user)
|
|
const [active, setActive] = useState<string>('')
|
|
|
|
//state from dashboard
|
|
const location = useLocation()
|
|
useEffect(() => {
|
|
if (location.pathname) {
|
|
const activeMenu = data.filter((i) => i.link === location.pathname)[0]
|
|
.label
|
|
setActive(activeMenu)
|
|
window.history.replaceState({}, document.title)
|
|
document.title = activeMenu + ' - Admin'
|
|
}
|
|
}, [location?.pathname])
|
|
|
|
const [opened, setOpened] = useState(false)
|
|
const [loading, setLoading] = useState(false)
|
|
const [dataChange, setDataChange] = useState({
|
|
password: '',
|
|
new_password: '',
|
|
confirm_password: '',
|
|
})
|
|
const [countSpam, setCountSpam] = useState(0)
|
|
const navigate = useNavigate()
|
|
const dispatch = useAppDispatch()
|
|
const passwordRegex =
|
|
/^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
|
|
|
|
const { setColorScheme } = useMantineColorScheme()
|
|
const computedColorScheme = useComputedColorScheme('light', {
|
|
getInitialValueInEffect: true,
|
|
})
|
|
|
|
const links = data.map((item) => (
|
|
<a
|
|
className={classes.link}
|
|
data-active={item.label === active || undefined}
|
|
key={item.label}
|
|
onClick={() => {
|
|
// setHeader(item.label);
|
|
setActive(active)
|
|
if (active !== item.label) {
|
|
navigate(item.link)
|
|
}
|
|
}}
|
|
>
|
|
<item.icon className={classes.linkIcon} stroke={1.5} />
|
|
<span
|
|
className={`${classes.itemLabel} ${
|
|
isCompactMenu ? classes.labelCompactMenu : ''
|
|
} `}
|
|
>
|
|
{item.label}
|
|
</span>
|
|
</a>
|
|
))
|
|
|
|
const handleLogout = useCallback(() => {
|
|
dispatch(logout(navigate))
|
|
}, [dispatch, navigate])
|
|
|
|
const handleChangePassword = async () => {
|
|
try {
|
|
if (countSpam > 5) {
|
|
setLoading(true)
|
|
notifications.show({
|
|
title: 'Error',
|
|
message: 'Password error more than 5 times. Logout after 3s',
|
|
color: 'red',
|
|
})
|
|
|
|
setTimeout(() => {
|
|
localStorage.clear()
|
|
window.location.reload()
|
|
}, 3000)
|
|
} else {
|
|
setLoading(true)
|
|
const res = await post(
|
|
changePassword,
|
|
{
|
|
email: user.user.email,
|
|
password: dataChange.password,
|
|
new_password: dataChange.new_password,
|
|
confirm_password: dataChange.confirm_password,
|
|
},
|
|
undefined,
|
|
)
|
|
|
|
if (res.status) {
|
|
notifications.show({
|
|
title: 'Success',
|
|
message: 'Reset password success',
|
|
color: 'green',
|
|
})
|
|
|
|
setOpened(false)
|
|
setDataChange({
|
|
password: '',
|
|
new_password: '',
|
|
confirm_password: '',
|
|
})
|
|
handleLogout()
|
|
} else {
|
|
notifications.show({
|
|
title: 'Error',
|
|
message: res.errors.password,
|
|
color: 'red',
|
|
})
|
|
}
|
|
setCountSpam(countSpam + 1)
|
|
setLoading(false)
|
|
}
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
} catch (error: any) {
|
|
console.log(error)
|
|
if (error.response.status === 422) {
|
|
const errorMess = error.response.data.message
|
|
notifications.show({
|
|
title: 'Error',
|
|
message: errorMess,
|
|
color: 'red',
|
|
})
|
|
}
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<nav
|
|
className={`${classes.navbar} ${
|
|
isCompactMenu ? classes.compactMenu : ''
|
|
}`}
|
|
>
|
|
<div className={classes.navbarMain}>
|
|
<Group className={classes.header} justify="space-between">
|
|
{/* <Image
|
|
src="https://littlepay.com/wp-content/uploads/2021/11/animation-elements-2.svg"
|
|
w={isCompactMenu ? '120%' : '50%'}
|
|
></Image> */}
|
|
<Code fw={700} className={classes.version}>
|
|
v1.0.1
|
|
</Code>
|
|
</Group>
|
|
{links}
|
|
</div>
|
|
|
|
<div className={classes.footer}>
|
|
<a
|
|
href="#"
|
|
className={classes.link}
|
|
onClick={() =>
|
|
setColorScheme(computedColorScheme === 'light' ? 'dark' : 'light')
|
|
}
|
|
>
|
|
{computedColorScheme === 'light' ? (
|
|
<IconSun className={classes.linkIcon} stroke={1.5} />
|
|
) : (
|
|
<IconMoon className={classes.linkIcon} stroke={1.5} />
|
|
)}
|
|
<span
|
|
className={`${classes.itemLabel} ${
|
|
isCompactMenu ? classes.labelCompactMenu : ''
|
|
}`}
|
|
>
|
|
Change mode
|
|
</span>
|
|
</a>
|
|
<a href="#" className={classes.link} onClick={() => setOpened(true)}>
|
|
<IconPasswordUser className={classes.linkIcon} stroke={1.5} />
|
|
<span
|
|
className={`${classes.itemLabel} ${
|
|
isCompactMenu ? classes.labelCompactMenu : ''
|
|
}`}
|
|
>
|
|
Change password
|
|
</span>
|
|
</a>
|
|
<a href="#" className={classes.link} onClick={handleLogout}>
|
|
<IconLogout className={classes.linkIcon} stroke={1.5} />
|
|
<span
|
|
className={`${classes.itemLabel} ${
|
|
isCompactMenu ? classes.labelCompactMenu : ''
|
|
}`}
|
|
>
|
|
Logout
|
|
</span>
|
|
</a>
|
|
<div className={classes.footer}></div>
|
|
<a href="#" className={classes.link} onClick={handleSetCompactMenu}>
|
|
{isCompactMenu ? (
|
|
<IconLayoutSidebarLeftExpand
|
|
className={classes.linkIcon}
|
|
stroke={1.5}
|
|
/>
|
|
) : (
|
|
<IconLayoutSidebarRightExpand
|
|
className={classes.linkIcon}
|
|
stroke={1.5}
|
|
/>
|
|
)}
|
|
|
|
<span
|
|
className={`${classes.itemLabel} ${
|
|
isCompactMenu ? classes.labelCompactMenu : ''
|
|
}`}
|
|
>
|
|
Collapse Sidebar
|
|
</span>
|
|
</a>
|
|
</div>
|
|
</nav>
|
|
|
|
<Modal
|
|
opened={opened}
|
|
onClose={() => {
|
|
setOpened(false)
|
|
setDataChange({
|
|
password: '',
|
|
new_password: '',
|
|
confirm_password: '',
|
|
})
|
|
}}
|
|
title={
|
|
<Text
|
|
fw={700}
|
|
fz={'1.2rem'}
|
|
className={`${classes.itemLabel} ${
|
|
isCompactMenu ? classes.labelCompactMenu : ''
|
|
}`}
|
|
>
|
|
Change password
|
|
</Text>
|
|
}
|
|
>
|
|
<Box p="sm">
|
|
<TextInput
|
|
label="E-mail"
|
|
value={user.user.email}
|
|
disabled
|
|
mb={'md'}
|
|
></TextInput>
|
|
|
|
<PasswordInput
|
|
label="Current password"
|
|
required
|
|
placeholder="Current password"
|
|
maxLength={32}
|
|
value={dataChange.password}
|
|
onChange={(e) => {
|
|
setDataChange({ ...dataChange, password: e.target.value })
|
|
}}
|
|
error={
|
|
dataChange.password.length < 8 &&
|
|
dataChange.password !== '' &&
|
|
'Length 8 characters or more'
|
|
}
|
|
/>
|
|
|
|
<PasswordRequirementInput
|
|
requirements={requirementsPassword}
|
|
value={dataChange}
|
|
setValue={setDataChange}
|
|
label="New password"
|
|
placeholder="New password"
|
|
name="new_password"
|
|
mb="md"
|
|
/>
|
|
|
|
<PasswordInput
|
|
label="Confirm password"
|
|
required
|
|
placeholder="Confirm password"
|
|
maxLength={32}
|
|
value={dataChange.confirm_password}
|
|
onChange={(e) => {
|
|
setDataChange({
|
|
...dataChange,
|
|
confirm_password: e.target.value,
|
|
})
|
|
}}
|
|
error={
|
|
dataChange.new_password !== dataChange.confirm_password &&
|
|
dataChange.confirm_password !== '' &&
|
|
'Password do not match'
|
|
}
|
|
></PasswordInput>
|
|
|
|
<Button
|
|
style={{ float: 'right' }}
|
|
mb={'lg'}
|
|
mt={'lg'}
|
|
loading={loading}
|
|
disabled={
|
|
loading === false &&
|
|
dataChange.password !== '' &&
|
|
passwordRegex.test(dataChange.new_password) &&
|
|
dataChange.new_password === dataChange.confirm_password &&
|
|
dataChange.new_password !== ''
|
|
? false
|
|
: true
|
|
}
|
|
onClick={() => handleChangePassword()}
|
|
>
|
|
Update
|
|
</Button>
|
|
</Box>
|
|
</Modal>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default Navbar
|