243 lines
6.5 KiB
TypeScript
243 lines
6.5 KiB
TypeScript
'use client';
|
|
|
|
import MoreMenu from '@/components/more/MoreMenu';
|
|
import {
|
|
PATH_ADMIN_CONFIGURATION,
|
|
PATH_ADMIN_INSIGHTS,
|
|
PATH_ADMIN_PHOTOS,
|
|
PATH_ADMIN_PHOTOS_UPDATES,
|
|
PATH_ADMIN_RECIPES,
|
|
PATH_ADMIN_TAGS,
|
|
PATH_ADMIN_UPLOADS,
|
|
PATH_GRID_INFERRED,
|
|
} from '@/app/paths';
|
|
import { useAppState } from '@/state/AppState';
|
|
import { IoArrowDown, IoArrowUp, IoCloseSharp } from 'react-icons/io5';
|
|
import { clsx } from 'clsx/lite';
|
|
import AdminAppInfoIcon from './AdminAppInfoIcon';
|
|
import { signOutAction } from '@/auth/actions';
|
|
import { ComponentProps } from 'react';
|
|
import useIsKeyBeingPressed from '@/utility/useIsKeyBeingPressed';
|
|
import IconPhoto from '@/components/icons/IconPhoto';
|
|
import IconUpload from '@/components/icons/IconUpload';
|
|
import IconRecipe from '@/components/icons/IconRecipe';
|
|
import IconTag from '@/components/icons/IconTag';
|
|
import IconFolder from '@/components/icons/IconFolder';
|
|
import IconSignOut from '@/components/icons/IconSignOut';
|
|
import IconLock from '@/components/icons/IconLock';
|
|
import { IoMdCheckboxOutline } from 'react-icons/io';
|
|
import Spinner from '@/components/Spinner';
|
|
import IconBroom from '@/components/icons/IconBroom';
|
|
import InsightsIndicatorDot from './insights/InsightsIndicatorDot';
|
|
|
|
export default function AdminAppMenu({
|
|
active,
|
|
animateMenuClose,
|
|
className,
|
|
}: {
|
|
active?: boolean
|
|
animateMenuClose?: boolean
|
|
className?: string
|
|
}) {
|
|
const {
|
|
photosCountTotal = 0,
|
|
photosCountNeedSync = 0,
|
|
uploadsCount = 0,
|
|
tagsCount = 0,
|
|
recipesCount = 0,
|
|
hasAdminData,
|
|
isLoadingAdminData,
|
|
selectedPhotoIds,
|
|
startUpload,
|
|
setSelectedPhotoIds,
|
|
refreshAdminData,
|
|
clearAuthStateAndRedirectIfNecessary,
|
|
} = useAppState();
|
|
|
|
const isSelecting = selectedPhotoIds !== undefined;
|
|
|
|
const isAltPressed = useIsKeyBeingPressed('alt');
|
|
|
|
const showAppInsightsLink = photosCountTotal > 0 && !isAltPressed;
|
|
|
|
const items: ComponentProps<typeof MoreMenu>['items'] = [{
|
|
label: 'Upload Photos',
|
|
icon: <IconUpload
|
|
size={15}
|
|
className="translate-x-[0.5px] translate-y-[0.5px]"
|
|
/>,
|
|
action: startUpload,
|
|
}];
|
|
|
|
if (uploadsCount) {
|
|
items.push({
|
|
label: 'Uploads',
|
|
annotation: `${uploadsCount}`,
|
|
icon: <IconFolder
|
|
size={16}
|
|
className="translate-x-[1px] translate-y-[0.5px]"
|
|
/>,
|
|
href: PATH_ADMIN_UPLOADS,
|
|
});
|
|
}
|
|
if (photosCountNeedSync) {
|
|
items.push({
|
|
label: 'Updates',
|
|
annotation: <>
|
|
<span className="mr-3">
|
|
{photosCountNeedSync}
|
|
</span>
|
|
<InsightsIndicatorDot
|
|
className="inline-block translate-y-[-0.5px]"
|
|
size="small"
|
|
/>
|
|
</>,
|
|
icon: <IconBroom
|
|
size={17}
|
|
className="translate-y-[0.5px]"
|
|
/>,
|
|
href: PATH_ADMIN_PHOTOS_UPDATES,
|
|
});
|
|
}
|
|
if (photosCountTotal) {
|
|
items.push({
|
|
label: 'Manage Photos',
|
|
...photosCountTotal && {
|
|
annotation: `${photosCountTotal}`,
|
|
},
|
|
icon: <IconPhoto
|
|
size={15}
|
|
className="translate-x-[-0.5px] translate-y-[0.5px]"
|
|
/>,
|
|
href: PATH_ADMIN_PHOTOS,
|
|
});
|
|
}
|
|
if (tagsCount) {
|
|
items.push({
|
|
label: 'Manage Tags',
|
|
annotation: `${tagsCount}`,
|
|
icon: <IconTag
|
|
size={15}
|
|
className="translate-y-[0.5px]"
|
|
/>,
|
|
href: PATH_ADMIN_TAGS,
|
|
});
|
|
}
|
|
if (recipesCount) {
|
|
items.push({
|
|
label: 'Manage Recipes',
|
|
annotation: `${recipesCount}`,
|
|
icon: <IconRecipe
|
|
size={17}
|
|
className="translate-x-[-0.5px] translate-y-[0.5px]"
|
|
/>,
|
|
href: PATH_ADMIN_RECIPES,
|
|
});
|
|
}
|
|
if (photosCountTotal) {
|
|
items.push({
|
|
label: isSelecting
|
|
? 'Exit Batch Edit'
|
|
: 'Batch Edit ...',
|
|
icon: isSelecting
|
|
? <IoCloseSharp
|
|
size={18}
|
|
className="translate-x-[-1px] translate-y-[0.5px]"
|
|
/>
|
|
: <IoMdCheckboxOutline
|
|
size={16}
|
|
className="translate-x-[-0.5px]"
|
|
/>,
|
|
href: PATH_GRID_INFERRED,
|
|
action: () => {
|
|
if (isSelecting) {
|
|
setSelectedPhotoIds?.(undefined);
|
|
} else {
|
|
setSelectedPhotoIds?.([]);
|
|
}
|
|
if (document.activeElement instanceof HTMLElement) {
|
|
document.activeElement.blur();
|
|
}
|
|
},
|
|
shouldPreventDefault: false,
|
|
});
|
|
}
|
|
|
|
items.push({
|
|
label: showAppInsightsLink
|
|
? 'App Insights'
|
|
: 'App Configuration',
|
|
icon: <AdminAppInfoIcon
|
|
size="small"
|
|
className="translate-x-[-0.5px] translate-y-[-0.5px]"
|
|
/>,
|
|
href: showAppInsightsLink
|
|
? PATH_ADMIN_INSIGHTS
|
|
: PATH_ADMIN_CONFIGURATION,
|
|
}, {
|
|
label: 'Sign Out',
|
|
icon: <IconSignOut size={15} />,
|
|
action: () => signOutAction().then(clearAuthStateAndRedirectIfNecessary),
|
|
});
|
|
|
|
return (
|
|
<MoreMenu
|
|
header={<div className="flex items-center select-none">
|
|
<span className="inline-flex items-center justify-center w-5 mr-2">
|
|
{!hasAdminData && isLoadingAdminData
|
|
? <Spinner
|
|
className="translate-x-[1px] translate-y-[1px]"
|
|
size={13}
|
|
/>
|
|
:<IconLock
|
|
size={16}
|
|
className="translate-x-[1px] translate-y-[0.5px]"
|
|
narrow
|
|
/>}
|
|
</span>
|
|
<span className="grow">Admin menu</span>
|
|
</div>}
|
|
icon={<div className={clsx(
|
|
'w-[28px] h-[28px]',
|
|
'overflow-hidden',
|
|
)}>
|
|
<div className={clsx(
|
|
'flex flex-col items-center justify-center gap-2',
|
|
'relative transition-transform',
|
|
animateMenuClose ? 'duration-300' : 'duration-0',
|
|
'translate-y-[-18px]',
|
|
)}>
|
|
<IoArrowDown size={16} className="shrink-0" />
|
|
<IoArrowUp size={16} className="shrink-0" />
|
|
</div>
|
|
</div>}
|
|
align="start"
|
|
sideOffset={12}
|
|
alignOffset={-85}
|
|
onOpen={refreshAdminData}
|
|
className={clsx(
|
|
'border-medium',
|
|
className,
|
|
)}
|
|
buttonClassName={clsx(
|
|
'p-0!',
|
|
'w-full h-full',
|
|
'flex items-center justify-center',
|
|
'hover:bg-transparent dark:hover:bg-transparent',
|
|
'active:bg-transparent dark:active:bg-transparent',
|
|
'rounded-none focus:outline-none',
|
|
active
|
|
? 'text-black dark:text-white'
|
|
: 'text-gray-400 dark:text-gray-600',
|
|
)}
|
|
buttonClassNameOpen={clsx(
|
|
'bg-dim text-main!',
|
|
'[&>*>*]:translate-y-[6px]',
|
|
!animateMenuClose && '[&>*>*]:duration-300',
|
|
)}
|
|
items={items}
|
|
ariaLabel="Admin Menu"
|
|
/>
|
|
);
|
|
}
|