From c9599120d290c417c7b711858a8debbcead4b821 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Thu, 5 Oct 2023 22:01:23 -0500 Subject: [PATCH] Add admin sub-nav --- src/admin/AdminNav.tsx | 40 +++++ src/app/(auth-state)/admin/layout.tsx | 43 ++++++ src/app/(auth-state)/admin/photos/page.tsx | 168 ++++++++++----------- src/components/FooterAuth.tsx | 2 +- src/components/FooterStatic.tsx | 2 +- src/components/HeaderList.tsx | 2 +- src/components/ShareButton.tsx | 2 +- src/components/StatusIcon.tsx | 2 +- src/photo/PhotoHeader.tsx | 4 +- src/services/blob.ts | 4 +- src/site/globals.css | 8 + src/site/paths.ts | 9 +- 12 files changed, 188 insertions(+), 98 deletions(-) create mode 100644 src/admin/AdminNav.tsx create mode 100644 src/app/(auth-state)/admin/layout.tsx diff --git a/src/admin/AdminNav.tsx b/src/admin/AdminNav.tsx new file mode 100644 index 00000000..8282a8e7 --- /dev/null +++ b/src/admin/AdminNav.tsx @@ -0,0 +1,40 @@ +'use client'; + +import { cc } from '@/utility/css'; +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; + +export default function AdminNav({ + items, +}: { + items: { + label: string, + href: string, + count: number, + }[] +}) { + const pathname = usePathname(); + + return ( +
+
+ {items.map(({ label, href, count }) => + + {label} + ({count}) + )} +
+
+ ); +} diff --git a/src/app/(auth-state)/admin/layout.tsx b/src/app/(auth-state)/admin/layout.tsx new file mode 100644 index 00000000..ef916ea5 --- /dev/null +++ b/src/app/(auth-state)/admin/layout.tsx @@ -0,0 +1,43 @@ +import AdminNav from '@/admin/AdminNav'; +import { + getPhotosCountIncludingHiddenCached, + getUniqueTagsCached, +} from '@/cache'; +import { PATH_ADMIN_PHOTOS, PATH_ADMIN_TAGS } from '@/site/paths'; + +export default async function AdminLayout({ + children, +}: { + children: React.ReactNode +}) { + const [ + photosCount, + tagsCount, + ] = await Promise.all([ + getPhotosCountIncludingHiddenCached(), + getUniqueTagsCached().then(tags => tags.length), + ]); + + const navItemPhotos = { + label: 'Photos', + href: PATH_ADMIN_PHOTOS, + count: photosCount, + }; + + const navItemTags = { + label: 'Tags', + href: PATH_ADMIN_TAGS, + count: tagsCount, + }; + + const navItems = tagsCount > 0 + ? [navItemPhotos, navItemTags] + : [navItemPhotos]; + + return ( +
+ + {children} +
+ ); +} diff --git a/src/app/(auth-state)/admin/photos/page.tsx b/src/app/(auth-state)/admin/photos/page.tsx index c2abb6e8..3555ec3e 100644 --- a/src/app/(auth-state)/admin/photos/page.tsx +++ b/src/app/(auth-state)/admin/photos/page.tsx @@ -58,93 +58,91 @@ export default async function AdminPage({ searchParams }: PaginationParams) { return ( -
-
-
- -
-
+
+
+ +
+ + } > - } - > - Clear Cache - - -
- {blobUploadUrls.length > 0 && - } - {blobPhotoUrls.length > 0 && - } -
- - {photos.map(photo => - - -
- - - {photo.title || 'Untitled'} - {photo.hidden && - } - - {photo.priorityOrder !== null && - - {photo.priorityOrder} - } - -
- {photo.takenAtNaive} -
-
- - + +
+ {blobUploadUrls.length > 0 && + } + {blobPhotoUrls.length > 0 && + } +
+ + {photos.map(photo => + + +
+ - - - - - )} - - {showMorePhotos && - } -
+ + {photo.title || 'Untitled'} + {photo.hidden && + } + + {photo.priorityOrder !== null && + + {photo.priorityOrder} + } + +
+ {photo.takenAtNaive} +
+
+ + + + + + + )} + + {showMorePhotos && + }
} /> diff --git a/src/components/FooterAuth.tsx b/src/components/FooterAuth.tsx index 9aed1910..17f7e585 100644 --- a/src/components/FooterAuth.tsx +++ b/src/components/FooterAuth.tsx @@ -25,7 +25,7 @@ export default function FooterAuth() { contentMain={
{hasState diff --git a/src/components/FooterStatic.tsx b/src/components/FooterStatic.tsx index e4881d8d..e28ee553 100644 --- a/src/components/FooterStatic.tsx +++ b/src/components/FooterStatic.tsx @@ -18,7 +18,7 @@ export default function FooterStatic({ contentMain={
, ].concat(items)} - classNameItem="text-gray-400 dark:text-gray-500" + classNameItem="text-dim" /> ); } diff --git a/src/components/ShareButton.tsx b/src/components/ShareButton.tsx index 08ec476a..a3d4388a 100644 --- a/src/components/ShareButton.tsx +++ b/src/components/ShareButton.tsx @@ -16,7 +16,7 @@ export default function ShareButton({ , prefetch, shouldScroll, diff --git a/src/components/StatusIcon.tsx b/src/components/StatusIcon.tsx index ffd5579b..46dc94de 100644 --- a/src/components/StatusIcon.tsx +++ b/src/components/StatusIcon.tsx @@ -27,7 +27,7 @@ export default function StatusIcon({ case 'optional': return ; } }; diff --git a/src/photo/PhotoHeader.tsx b/src/photo/PhotoHeader.tsx index 30c0d53a..d725451b 100644 --- a/src/photo/PhotoHeader.tsx +++ b/src/photo/PhotoHeader.tsx @@ -35,7 +35,7 @@ export default function PhotoHeader({ {entity} {selectedPhotoIndex !== undefined @@ -48,7 +48,7 @@ export default function PhotoHeader({ {start === end ? start diff --git a/src/services/blob.ts b/src/services/blob.ts index 05a2e488..a34d83a0 100644 --- a/src/services/blob.ts +++ b/src/services/blob.ts @@ -1,4 +1,4 @@ -import { PATH_ADMIN_UPLOAD_BLOB_HANDLER } from '@/site/paths'; +import { PATH_ADMIN_UPLOAD_BLOB } from '@/site/paths'; import { del, list, put } from '@vercel/blob'; import { upload } from '@vercel/blob/client'; @@ -41,7 +41,7 @@ export const uploadPhotoFromClient = async ( file, { access: 'public', - handleUploadUrl: PATH_ADMIN_UPLOAD_BLOB_HANDLER, + handleUploadUrl: PATH_ADMIN_UPLOAD_BLOB, }, ); diff --git a/src/site/globals.css b/src/site/globals.css index f0f5cf7d..89efa7a3 100644 --- a/src/site/globals.css +++ b/src/site/globals.css @@ -3,12 +3,14 @@ @tailwind utilities; @layer base { + /* Core */ body { @apply font-mono text-sm md:text-base bg-white dark:bg-black text-gray-900 dark:text-gray-100 } + /* Forms */ label { @apply font-sans font-medium block uppercase text-xs @@ -70,9 +72,15 @@ disabled:bg-transparent dark:disabled:bg-transparent disabled:border-gray-100 dark:disabled:border-gray-900 } + /* Toasts */ .toaster [data-sonner-toast] { @apply font-mono !border-gray-200 dark:!border-gray-800 } + /* Common Utilities */ + .text-dim { + @apply + text-gray-400 dark:text-gray-500 + } } diff --git a/src/site/paths.ts b/src/site/paths.ts index e8529434..87480fe7 100644 --- a/src/site/paths.ts +++ b/src/site/paths.ts @@ -19,10 +19,11 @@ export const PATH_SIGN_IN = '/sign-in'; export const PATH_OG = '/og'; export const PATH_CHECKLIST = '/checklist'; -// Extended paths -export const PATH_ADMIN_PHOTOS = `${PATH_ADMIN}/photos`; -export const PATH_ADMIN_UPLOAD = `${PATH_ADMIN}/uploads`; -export const PATH_ADMIN_UPLOAD_BLOB_HANDLER = `${PATH_ADMIN_UPLOAD}/blob`; +// Admin paths +export const PATH_ADMIN_PHOTOS = `${PATH_ADMIN}/photos`; +export const PATH_ADMIN_TAGS = `${PATH_ADMIN}/tags`; +export const PATH_ADMIN_UPLOAD = `${PATH_ADMIN}/uploads`; +export const PATH_ADMIN_UPLOAD_BLOB = `${PATH_ADMIN_UPLOAD}/blob`; // Modifiers const SHARE = 'share';