ManagementSystem/FRONTEND/src/components/Navbar/Navbar.tsx

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