import Badge from '@app/components/Common/Badge'; import { menuMessages } from '@app/components/Layout/Sidebar'; import useClickOutside from '@app/hooks/useClickOutside'; import { Permission, useUser } from '@app/hooks/useUser'; import { Transition } from '@headlessui/react'; import { ClockIcon, CogIcon, EllipsisHorizontalIcon, ExclamationTriangleIcon, EyeSlashIcon, FilmIcon, SparklesIcon, TvIcon, UsersIcon, } from '@heroicons/react/24/outline'; import { ClockIcon as FilledClockIcon, CogIcon as FilledCogIcon, ExclamationTriangleIcon as FilledExclamationTriangleIcon, EyeSlashIcon as FilledEyeSlashIcon, FilmIcon as FilledFilmIcon, SparklesIcon as FilledSparklesIcon, TvIcon as FilledTvIcon, UsersIcon as FilledUsersIcon, XMarkIcon, } from '@heroicons/react/24/solid'; import Link from 'next/link'; import { useRouter } from 'next/router'; import { cloneElement, useEffect, useRef, useState } from 'react'; import { useIntl } from 'react-intl'; interface MobileMenuProps { pendingRequestsCount: number; openIssuesCount: number; revalidateIssueCount: () => void; revalidateRequestsCount: () => void; } interface MenuLink { href: string; svgIcon: JSX.Element; svgIconSelected: JSX.Element; content: React.ReactNode; activeRegExp: RegExp; as?: string; requiredPermission?: Permission | Permission[]; permissionType?: 'and' | 'or'; dataTestId?: string; } const MobileMenu = ({ pendingRequestsCount, openIssuesCount, revalidateIssueCount, revalidateRequestsCount, }: MobileMenuProps) => { const ref = useRef(null); const intl = useIntl(); const [isOpen, setIsOpen] = useState(false); const { hasPermission } = useUser(); const router = useRouter(); useClickOutside(ref, () => { setTimeout(() => { if (isOpen) { setIsOpen(false); } }, 150); }); const toggle = () => setIsOpen(!isOpen); const menuLinks: MenuLink[] = [ { href: '/', content: intl.formatMessage(menuMessages.dashboard), svgIcon: , svgIconSelected: , activeRegExp: /^\/(discover\/?)?$/, }, { href: '/discover/movies', content: intl.formatMessage(menuMessages.browsemovies), svgIcon: , svgIconSelected: , activeRegExp: /^\/discover\/movies$/, }, { href: '/discover/tv', content: intl.formatMessage(menuMessages.browsetv), svgIcon: , svgIconSelected: , activeRegExp: /^\/discover\/tv$/, }, { href: '/requests', content: intl.formatMessage(menuMessages.requests), svgIcon: , svgIconSelected: , activeRegExp: /^\/requests/, }, { href: '/blocklist', content: intl.formatMessage(menuMessages.blocklist), svgIcon: , svgIconSelected: , activeRegExp: /^\/blocklist/, requiredPermission: [ Permission.MANAGE_BLOCKLIST, Permission.VIEW_BLOCKLIST, ], permissionType: 'or', }, { href: '/issues', content: intl.formatMessage(menuMessages.issues), svgIcon: , svgIconSelected: , activeRegExp: /^\/issues/, requiredPermission: [ Permission.MANAGE_ISSUES, Permission.CREATE_ISSUES, Permission.VIEW_ISSUES, ], permissionType: 'or', }, { href: '/users', content: intl.formatMessage(menuMessages.users), svgIcon: , svgIconSelected: , activeRegExp: /^\/users/, requiredPermission: Permission.MANAGE_USERS, dataTestId: 'sidebar-menu-users', }, { href: '/settings', content: intl.formatMessage(menuMessages.settings), svgIcon: , svgIconSelected: , activeRegExp: /^\/settings/, requiredPermission: Permission.ADMIN, dataTestId: 'sidebar-menu-settings', }, ]; const filteredLinks = menuLinks.filter( (link) => !link.requiredPermission || hasPermission(link.requiredPermission, { type: link.permissionType ?? 'and', }) ); useEffect(() => { if (openIssuesCount) { revalidateIssueCount(); } if (pendingRequestsCount) { revalidateRequestsCount(); } }, [ revalidateIssueCount, revalidateRequestsCount, pendingRequestsCount, openIssuesCount, ]); return (
{filteredLinks.map((link) => { const isActive = router.pathname.match(link.activeRegExp); return ( { if (e.key === 'Enter') { setIsOpen(false); } }} onClick={() => setIsOpen(false)} role="button" tabIndex={0} > {cloneElement(isActive ? link.svgIconSelected : link.svgIcon, { className: 'h-5 w-5', })} {link.content} {link.href === '/requests' && pendingRequestsCount > 0 && hasPermission(Permission.MANAGE_REQUESTS) && (
{pendingRequestsCount}
)} {link.href === '/issues' && openIssuesCount > 0 && hasPermission(Permission.MANAGE_ISSUES) && (
{openIssuesCount}
)} ); })}
{filteredLinks .slice(0, filteredLinks.length === 5 ? 5 : 4) .map((link) => { const isActive = router.pathname.match(link.activeRegExp) && !isOpen; return ( {cloneElement( isActive ? link.svgIconSelected : link.svgIcon, { className: 'h-6 w-6', } )} {link.href === '/requests' && pendingRequestsCount > 0 && hasPermission(Permission.MANAGE_REQUESTS) && (
99 ? 'w-6' : 'w-4' } h-4 items-center justify-center !px-[5px] !py-[7px] text-[8px]`} > {pendingRequestsCount > 99 ? '99+' : pendingRequestsCount}
)} ); })} {filteredLinks.length > 4 && filteredLinks.length !== 5 && ( )}
); }; export default MobileMenu;