From cddabc6180a7c47d7abef9c7116bb08b08f2caca Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Thu, 5 Oct 2023 23:31:12 -0500 Subject: [PATCH] Add counts to admin tag management --- src/app/(auth-state)/admin/tags/page.tsx | 16 +++++++++------- src/cache/index.ts | 10 ++++++++++ src/photo/index.ts | 6 ++++-- src/services/postgres.ts | 16 +++++++++++++++- 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/app/(auth-state)/admin/tags/page.tsx b/src/app/(auth-state)/admin/tags/page.tsx index d1cbeff4..6dbeea39 100644 --- a/src/app/(auth-state)/admin/tags/page.tsx +++ b/src/app/(auth-state)/admin/tags/page.tsx @@ -1,15 +1,16 @@ import FormWithConfirm from '@/components/FormWithConfirm'; import SiteGrid from '@/components/SiteGrid'; import { deletePhotoTagGloballyAction } from '@/photo/actions'; -import { getUniqueTagsCached } from '@/cache'; import AdminGrid from '@/admin/AdminGrid'; import { Fragment } from 'react'; import DeleteButton from '@/admin/DeleteButton'; +import { photoQuantityText } from '@/photo'; +import { getUniqueTagsWithCountCached } from '@/cache'; export const runtime = 'edge'; export default async function AdminPhotosPage() { - const tags = await getUniqueTagsCached(); + const tags = await getUniqueTagsWithCountCached(); return (
- {tags.map(tag => + {tags.map(({ tag, count }) => -
+
{tag}
-
-
+
+ {photoQuantityText(count, false)} +
diff --git a/src/cache/index.ts b/src/cache/index.ts index 5c959fc8..a13094b9 100644 --- a/src/cache/index.ts +++ b/src/cache/index.ts @@ -11,6 +11,7 @@ import { getUniqueTags, getPhotosTagDateRange, getPhotosCameraDateRange, + getUniqueTagsWithCount, } from '@/services/postgres'; import { parseCachedPhotosDates, parseCachedPhotoDates } from '@/photo'; import { getBlobPhotoUrls, getBlobUploadUrls } from '@/services/blob'; @@ -181,6 +182,15 @@ export const getUniqueTagsCached: typeof getUniqueTags = (...args) => } )(); +// eslint-disable-next-line max-len +export const getUniqueTagsWithCountCached: typeof getUniqueTagsWithCount = (...args) => + unstable_cache( + () => getUniqueTagsWithCount(...args), + [KEY_PHOTOS, KEY_TAGS], { + tags: [KEY_PHOTOS, KEY_TAGS], + } + )(); + export const getUniqueCamerasCached: typeof getUniqueCameras = (...args) => unstable_cache( () => getUniqueCameras(...args), diff --git a/src/photo/index.ts b/src/photo/index.ts index c310b148..971fadbe 100644 --- a/src/photo/index.ts +++ b/src/photo/index.ts @@ -157,8 +157,10 @@ export const titleForPhoto = (photo: Photo) => const photoLabelForCount = (count: number) => count === 1 ? 'Photo' : 'Photos'; -export const photoQuantityText = (count: number) => - `(${count} ${photoLabelForCount(count)})`; +export const photoQuantityText = (count: number, includeParentheses = true) => + includeParentheses + ? `(${count} ${photoLabelForCount(count)})` + : `${count} ${photoLabelForCount(count)}`; export type PhotoDateRange = { start: string, end: string }; diff --git a/src/services/postgres.ts b/src/services/postgres.ts index 3b308026..f3c4f901 100644 --- a/src/services/postgres.ts +++ b/src/services/postgres.ts @@ -283,6 +283,17 @@ const sqlGetUniqueTags = async () => sql` ORDER BY tag ASC `.then(({ rows }) => rows.map(row => row.tag as string)); +// Include hidden photos for admin usage +const sqlGetUniqueTagsWithCount = async () => sql` + SELECT DISTINCT unnest(tags) as tag, count(distinct id) as count FROM photos + GROUP BY tag + ORDER BY count ASC +`.then(({ rows }) => rows.map(row => ({ + tag: row.tag as string, + count: parseInt(row.count, 10), + }))); + + const sqlGetUniqueCameras = async () => sql` SELECT DISTINCT make||' '||model as camera, make, model FROM photos WHERE hidden IS NOT TRUE @@ -390,6 +401,9 @@ export const getPhotosCameraDateRange = (camera: Camera) => export const getPhotosCountIncludingHidden = () => safelyQueryPhotos(sqlGetPhotosCountIncludingHidden); -export const getUniqueTags = () => safelyQueryPhotos(sqlGetUniqueTags); +export const getUniqueTags = () => + safelyQueryPhotos(sqlGetUniqueTags); +export const getUniqueTagsWithCount = () => + safelyQueryPhotos(sqlGetUniqueTagsWithCount); export const getUniqueCameras = () => safelyQueryPhotos(sqlGetUniqueCameras);