PPR-ify admin nav

This commit is contained in:
Sam Becker 2024-01-18 18:24:11 -06:00
parent 85813353e6
commit 0b523a1c95
6 changed files with 135 additions and 107 deletions

View File

@ -1,65 +1,52 @@
'use client';
import SiteGrid from '@/components/SiteGrid';
import {
PATH_ADMIN_CONFIGURATION,
checkPathPrefix,
isPathAdminConfiguration,
getBlobUploadUrlsNoStore,
getPhotosCountIncludingHiddenCached,
getUniqueTagsCached,
} from '@/cache';
import {
PATH_ADMIN_PHOTOS,
PATH_ADMIN_TAGS,
PATH_ADMIN_UPLOADS,
} from '@/site/paths';
import { clsx } from 'clsx/lite';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { BiCog } from 'react-icons/bi';
import AdminNavClient from './AdminNavClient';
export default function AdminNav({
items,
}: {
items: {
label: string,
href: string,
count: number,
}[]
}) {
const pathname = usePathname();
export default async function AdminNav() {
// await sleep(20_000);
const [
countPhotos,
countUploads,
countTags,
] = await Promise.all([
getPhotosCountIncludingHiddenCached(),
getBlobUploadUrlsNoStore().then(urls => urls.length),
getUniqueTagsCached().then(tags => tags.length),
]);
const navItemPhotos = {
label: 'Photos',
href: PATH_ADMIN_PHOTOS,
count: countPhotos,
};
const navItemUploads = {
label: 'Uploads',
href: PATH_ADMIN_UPLOADS,
count: countUploads,
};
const navItemTags = {
label: 'Tags',
href: PATH_ADMIN_TAGS,
count: countTags,
};
const navItems = [navItemPhotos];
if (countUploads > 0) { navItems.push(navItemUploads); }
if (countTags > 0) { navItems.push(navItemTags); }
return (
<SiteGrid
contentMain={
<div className={clsx(
'flex gap-2 md:gap-4',
'border-b border-gray-200 dark:border-gray-800 pb-3',
)}>
<div className={clsx(
'flex gap-2 md:gap-4',
'flex-grow overflow-x-scroll',
)}>
{items.map(({ label, href, count }) =>
<Link
key={label}
href={href}
className={clsx(
'flex gap-0.5',
checkPathPrefix(pathname, href) ? 'font-bold' : 'text-dim',
)}
>
<span>{label}</span>
<span>({count})</span>
</Link>)}
</div>
<Link
href={PATH_ADMIN_CONFIGURATION}
className={isPathAdminConfiguration(pathname)
? 'font-bold'
: 'text-dim'}
>
<BiCog
size={18}
className="inline-block"
aria-label="Blog Configuration"
/>
</Link>
</div>
}
/>
<AdminNavClient items={navItems} />
);
}

View File

@ -0,0 +1,66 @@
'use client';
import SiteGrid from '@/components/SiteGrid';
import {
PATH_ADMIN_CONFIGURATION,
checkPathPrefix,
isPathAdminConfiguration,
} from '@/site/paths';
import { clsx } from 'clsx/lite';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { BiCog } from 'react-icons/bi';
export default function AdminNavClient({
items,
}: {
items: {
label: string,
href: string,
count: number,
}[]
}) {
const pathname = usePathname();
return (
<SiteGrid
contentMain={
<div className={clsx(
'flex gap-2 md:gap-4',
'border-b border-gray-200 dark:border-gray-800 pb-3',
)}>
<div className={clsx(
'flex gap-2 md:gap-4',
'flex-grow overflow-x-scroll',
)}>
{items.map(({ label, href, count }) =>
<Link
key={label}
href={href}
className={clsx(
'flex gap-0.5',
checkPathPrefix(pathname, href) ? 'font-bold' : 'text-dim',
)}
>
<span>{label}</span>
{count > 0 &&
<span>({count})</span>}
</Link>)}
</div>
<Link
href={PATH_ADMIN_CONFIGURATION}
className={isPathAdminConfiguration(pathname)
? 'font-bold'
: 'text-dim'}
>
<BiCog
size={18}
className="inline-block"
aria-label="Blog Configuration"
/>
</Link>
</div>
}
/>
);
}

View File

@ -1,56 +1,21 @@
import AdminNav from '@/admin/AdminNav';
import {
getBlobUploadUrlsNoStore,
getPhotosCountIncludingHiddenCached,
getUniqueTagsCached,
} from '@/cache';
import {
PATH_ADMIN_PHOTOS,
PATH_ADMIN_TAGS,
PATH_ADMIN_UPLOADS,
} from '@/site/paths';
import AdminNavClient from '@/admin/AdminNavClient';
import { Suspense } from 'react';
export default async function AdminLayout({
children,
}: {
children: React.ReactNode
}) {
const [
countPhotos,
countUploads,
countTags,
] = await Promise.all([
getPhotosCountIncludingHiddenCached(),
getBlobUploadUrlsNoStore().then(urls => urls.length),
getUniqueTagsCached().then(tags => tags.length),
]);
const navItemPhotos = {
label: 'Photos',
href: PATH_ADMIN_PHOTOS,
count: countPhotos,
};
const navItemUploads = {
label: 'Uploads',
href: PATH_ADMIN_UPLOADS,
count: countUploads,
};
const navItemTags = {
label: 'Tags',
href: PATH_ADMIN_TAGS,
count: countTags,
};
const navItems = [navItemPhotos];
if (countUploads > 0) { navItems.push(navItemUploads); }
if (countTags > 0) { navItems.push(navItemTags); }
return (
<div className="mt-4 space-y-5">
<AdminNav items={navItems} />
<Suspense fallback={<AdminNavClient items={[{
label: 'Photos',
count: 0,
href: '/admin/photos',
}]} />}>
<AdminNav />
</Suspense>
{children}
</div>
);

View File

@ -36,15 +36,22 @@ export default function FooterClient({
key="footer"
className={clsx(
'flex items-center',
'text-dim min-h-[4rem]',
'text-dim min-h-10',
)}>
<div className={clsx(
'flex items-center flex-grow flex-wrap h-10',
'gap-x-4 min-w-0',
)}>
<div className="flex gap-x-4 gap-y-1 flex-grow flex-wrap h-4">
{isPathAdmin(pathname)
? <>
{userEmail === undefined &&
<Spinner />}
{userEmail && <>
<div>{userEmail}</div>
<div className={clsx(
'truncate max-w-full',
)}>
{userEmail}
</div>
<form action={signOutAction}>
<SubmitButtonWithStatus styleAsLink>
Sign out
@ -60,7 +67,7 @@ export default function FooterClient({
<RepoLink />}
</>}
</div>
<div className="flex items-center h-4">
<div className="flex items-center h-10">
<ThemeSwitcher />
</div>
</div>]

View File

@ -105,7 +105,7 @@
button.link {
@apply
p-0 min-h-0
border-none active:bg-transparent shadow-none
border-none bg-transparent active:bg-transparent shadow-none
}
a, .link {
@apply

3
src/utility/sleep.ts Normal file
View File

@ -0,0 +1,3 @@
export default function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}