diff --git a/src/app/(auth-state)/admin/tags/page.tsx b/src/app/(auth-state)/admin/tags/page.tsx index f15931be..36810ec7 100644 --- a/src/app/(auth-state)/admin/tags/page.tsx +++ b/src/app/(auth-state)/admin/tags/page.tsx @@ -6,6 +6,8 @@ import { Fragment } from 'react'; import DeleteButton from '@/admin/DeleteButton'; import { photoQuantityText } from '@/photo'; import { getUniqueTagsWithCountCached } from '@/cache'; +import PhotoTag from '@/tag/PhotoTag'; +import { formatTag } from '@/tag'; export const runtime = 'edge'; @@ -21,9 +23,9 @@ export default async function AdminPhotosPage() { {tags.map(({ tag, count }) =>
- {tag} +
-
+
{photoQuantityText(count, false)}
@@ -31,7 +33,7 @@ export default async function AdminPhotosPage() { action={deletePhotoTagGloballyAction} confirmText={ // eslint-disable-next-line max-len - `Are you sure you want to remove "${tag}?" from ${photoQuantityText(count, false).toLowerCase()}?`} + `Are you sure you want to remove "${formatTag(tag)}?" from ${photoQuantityText(count, false).toLowerCase()}?`} > diff --git a/src/photo/actions.ts b/src/photo/actions.ts index 2b1c862f..930c55ad 100644 --- a/src/photo/actions.ts +++ b/src/photo/actions.ts @@ -5,6 +5,7 @@ import { sqlInsertPhoto, sqlDeletePhotoTagGlobally, sqlUpdatePhoto, + sqlRenamePhotoTagGlobally, } from '@/services/postgres'; import { convertFormDataToPhoto } from './form'; import { redirect } from 'next/navigation'; @@ -71,6 +72,17 @@ export async function deletePhotoTagGloballyAction(formData: FormData) { revalidatePhotosKey(); } +export async function renamePhotoTagGloballyAction(formData: FormData) { + const tag = formData.get('tag') as string; + const newTag = formData.get('newTag') as string; + + if (tag && newTag && tag !== newTag) { + await sqlRenamePhotoTagGlobally(tag, newTag); + + revalidatePhotosKey(); + } +} + export async function deleteBlobPhotoAction(formData: FormData) { await deleteBlobPhoto(formData.get('url') as string); diff --git a/src/services/postgres.ts b/src/services/postgres.ts index aa8967be..2a30424d 100644 --- a/src/services/postgres.ts +++ b/src/services/postgres.ts @@ -135,7 +135,14 @@ export const sqlUpdatePhoto = (photo: PhotoDbInsert) => export const sqlDeletePhotoTagGlobally = (tag: string) => sql` UPDATE photos - SET tags=array_remove(tags, ${tag}) + SET tags=ARRAY_REMOVE(tags, ${tag}) + WHERE ${tag}=ANY(tags) + `; + +export const sqlRenamePhotoTagGlobally = (tag: string, newTag: string) => + sql` + UPDATE photos + SET tags=ARRAY_REPLACE(tags, ${tag}, ${newTag}) WHERE ${tag}=ANY(tags) `; diff --git a/src/tag/PhotoTag.tsx b/src/tag/PhotoTag.tsx index 6d016a74..6526ab05 100644 --- a/src/tag/PhotoTag.tsx +++ b/src/tag/PhotoTag.tsx @@ -2,6 +2,7 @@ import Link from 'next/link'; import { pathForTag } from '@/site/paths'; import { FaTag } from 'react-icons/fa'; import { cc } from '@/utility/css'; +import { formatTag } from '.'; export default function PhotoTag({ tag, @@ -27,7 +28,7 @@ export default function PhotoTag({ )} />} - {tag.replaceAll('-', ' ')} + {formatTag(tag)} ); diff --git a/src/tag/index.ts b/src/tag/index.ts index a3e96f85..8b2d7322 100644 --- a/src/tag/index.ts +++ b/src/tag/index.ts @@ -7,12 +7,15 @@ import { import { absolutePathForTag, absolutePathForTagImage } from '@/site/paths'; import { capitalizeWords } from '@/utility/string'; +export const formatTag = (tag: string) => + capitalizeWords(tag.replaceAll('-', ' ')); + export const titleForTag = ( tag: string, photos:Photo[], explicitCount?: number, ) => [ - capitalizeWords(tag.replaceAll('-', ' ')), + formatTag(tag), photoQuantityText(explicitCount ?? photos.length), ].join(' ');