Refine admin tag editor
This commit is contained in:
parent
1fd41462e7
commit
02fbf0a2e0
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -6,6 +6,7 @@
|
||||
"exif",
|
||||
"hgetall",
|
||||
"hset",
|
||||
"Lightbox",
|
||||
"nanoids",
|
||||
"nextjs",
|
||||
"qaub",
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
import AdminChildPage from '@/components/AdminChildPage';
|
||||
import { redirect } from 'next/navigation';
|
||||
import { getPhotosCached } from '@/cache';
|
||||
import { getPhotosCached, getPhotosTagCountCached } from '@/cache';
|
||||
import TagForm from '@/tag/TagForm';
|
||||
import { PATH_ADMIN, PATH_ADMIN_TAGS } from '@/site/paths';
|
||||
import { getPhotosTagCount } from '@/services/postgres';
|
||||
import { PATH_ADMIN, PATH_ADMIN_TAGS, pathForTag } from '@/site/paths';
|
||||
import PhotoTag from '@/tag/PhotoTag';
|
||||
import { photoQuantityText } from '@/photo';
|
||||
import PhotoGrid from '@/photo/PhotoGrid';
|
||||
import { photoLabelForCount } from '@/photo';
|
||||
import PhotoLightbox from '@/photo/PhotoLightbox';
|
||||
|
||||
const MAX_PHOTO_TO_SHOW = 6;
|
||||
|
||||
export const runtime = 'edge';
|
||||
|
||||
@ -19,8 +20,8 @@ export default async function PhotoPageEdit({ params: { tag } }: Props) {
|
||||
count,
|
||||
photos,
|
||||
] = await Promise.all([
|
||||
getPhotosTagCount(tag),
|
||||
getPhotosCached({ tag }),
|
||||
getPhotosTagCountCached(tag),
|
||||
getPhotosCached({ tag, limit: MAX_PHOTO_TO_SHOW }),
|
||||
]);
|
||||
|
||||
if (count === 0) { redirect(PATH_ADMIN); }
|
||||
@ -32,18 +33,21 @@ export default async function PhotoPageEdit({ params: { tag } }: Props) {
|
||||
breadcrumb={<div className="flex item gap-2">
|
||||
<PhotoTag {...{ tag }} />
|
||||
<div className="text-dim uppercase">
|
||||
{photoQuantityText(count, false)}
|
||||
<span>{count}</span>
|
||||
<span className="hidden xs:inline-block">
|
||||
|
||||
{photoLabelForCount(count)}
|
||||
</span>
|
||||
</div>
|
||||
</div>}
|
||||
>
|
||||
<div className="space-y-8">
|
||||
<PhotoGrid
|
||||
photos={photos.slice(0, 12)}
|
||||
animate={false}
|
||||
small
|
||||
<TagForm {...{ tag, photos }}>
|
||||
<PhotoLightbox
|
||||
{...{ count, photos }}
|
||||
maxPhotosToShow={MAX_PHOTO_TO_SHOW}
|
||||
moreLink={pathForTag(tag)}
|
||||
/>
|
||||
<TagForm {...{ tag, photos }} />
|
||||
</div>
|
||||
</TagForm>
|
||||
</AdminChildPage>
|
||||
);
|
||||
};
|
||||
|
||||
@ -20,7 +20,10 @@ function AdminChildPage({
|
||||
contentMain={
|
||||
<div className="space-y-6">
|
||||
{backPath &&
|
||||
<div className="flex gap-3 items-center h-9">
|
||||
<div className={cc(
|
||||
'flex flex-wrap items-center gap-x-1.5 sm:gap-x-3 gap-y-1',
|
||||
'h-9',
|
||||
)}>
|
||||
<Link
|
||||
href={backPath}
|
||||
className="flex gap-1.5 items-center"
|
||||
|
||||
@ -15,6 +15,7 @@ export default function PhotoGrid({
|
||||
animateOnFirstLoadOnly,
|
||||
staggerOnFirstLoadOnly = true,
|
||||
showMorePath,
|
||||
additionalTile,
|
||||
small,
|
||||
}: {
|
||||
photos: Photo[]
|
||||
@ -26,6 +27,7 @@ export default function PhotoGrid({
|
||||
animateOnFirstLoadOnly?: boolean
|
||||
staggerOnFirstLoadOnly?: boolean
|
||||
showMorePath?: string
|
||||
additionalTile?: JSX.Element
|
||||
small?: boolean
|
||||
}) {
|
||||
return (
|
||||
@ -34,7 +36,7 @@ export default function PhotoGrid({
|
||||
className={cc(
|
||||
'grid gap-1',
|
||||
small
|
||||
? 'grid-cols-4 xs:grid-cols-6'
|
||||
? 'grid-cols-3 xs:grid-cols-6'
|
||||
: 'grid-cols-2 sm:grid-cols-4 md:grid-cols-3 lg:grid-cols-4',
|
||||
'items-center',
|
||||
)}
|
||||
@ -51,7 +53,7 @@ export default function PhotoGrid({
|
||||
tag={tag}
|
||||
camera={camera}
|
||||
selected={photo.id === selectedPhoto?.id}
|
||||
/>)}
|
||||
/>).concat(additionalTile ?? [])}
|
||||
/>
|
||||
{showMorePath &&
|
||||
<MorePhotos path={showMorePath} />}
|
||||
|
||||
51
src/photo/PhotoLightbox.tsx
Normal file
51
src/photo/PhotoLightbox.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import { cc } from '@/utility/css';
|
||||
import { Photo } from '.';
|
||||
import PhotoGrid from './PhotoGrid';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function PhotoLightbox({
|
||||
count,
|
||||
photos,
|
||||
maxPhotosToShow = 6,
|
||||
moreLink,
|
||||
}: {
|
||||
count: number
|
||||
photos: Photo[]
|
||||
maxPhotosToShow?: number
|
||||
moreLink: string
|
||||
}) {
|
||||
const photoCountToShow = maxPhotosToShow < count
|
||||
? maxPhotosToShow - 1
|
||||
: maxPhotosToShow;
|
||||
|
||||
const countNotShown = count - photoCountToShow;
|
||||
|
||||
const showOverageTile = countNotShown > 0;
|
||||
|
||||
return (
|
||||
<div className={cc(
|
||||
'border dark:border-gray-800 p-1.5 lg:p-2 rounded-md',
|
||||
'bg-gray-50 dark:bg-gray-950',
|
||||
)}>
|
||||
<PhotoGrid
|
||||
photos={photos.slice(0, photoCountToShow)}
|
||||
animate={false}
|
||||
additionalTile={showOverageTile
|
||||
? <Link
|
||||
href={moreLink}
|
||||
className={cc(
|
||||
'flex flex-col items-center justify-center',
|
||||
'gap-0.5 lg:gap-1',
|
||||
)}
|
||||
>
|
||||
<div className="text-[1.1rem] lg:text-[1.5rem]">
|
||||
+{countNotShown}
|
||||
</div>
|
||||
<div className="text-dim">More</div>
|
||||
</Link>
|
||||
: undefined}
|
||||
small
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -154,7 +154,7 @@ export const translatePhotoId = (id: string) =>
|
||||
export const titleForPhoto = (photo: Photo) =>
|
||||
photo.title || 'Untitled';
|
||||
|
||||
const photoLabelForCount = (count: number) =>
|
||||
export const photoLabelForCount = (count: number) =>
|
||||
count === 1 ? 'Photo' : 'Photos';
|
||||
|
||||
export const photoQuantityText = (count: number, includeParentheses = true) =>
|
||||
|
||||
@ -4,14 +4,16 @@ import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus';
|
||||
import Link from 'next/link';
|
||||
import { PATH_ADMIN_TAGS } from '@/site/paths';
|
||||
import FieldSetWithStatus from '@/components/FieldSetWithStatus';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { ReactNode, useMemo, useState } from 'react';
|
||||
import { renamePhotoTagGloballyAction } from '@/photo/actions';
|
||||
import { parameterize } from '@/utility/string';
|
||||
|
||||
export default function TagForm({
|
||||
tag,
|
||||
children,
|
||||
}: {
|
||||
tag: string
|
||||
children?: ReactNode
|
||||
}) {
|
||||
const [updatedTagRaw, setUpdatedTagRaw] = useState(tag);
|
||||
|
||||
@ -49,6 +51,7 @@ export default function TagForm({
|
||||
hidden
|
||||
readOnly
|
||||
/>
|
||||
{children}
|
||||
<div className="flex gap-3">
|
||||
<Link
|
||||
className="button"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user