Add admin tags page with global delete function

This commit is contained in:
Sam Becker 2023-10-05 23:06:12 -05:00
parent 35af0057c2
commit 74bc870b3d
6 changed files with 134 additions and 77 deletions

26
src/admin/AdminGrid.tsx Normal file
View File

@ -0,0 +1,26 @@
import { cc } from '@/utility/css';
import { ReactNode } from 'react';
export default function AdminGrid ({
title,
children,
}: {
title?: string,
children: ReactNode,
}) {
return <div className="space-y-4">
{title &&
<div className="font-bold">
{title}
</div>}
<div className="min-w-[14rem] overflow-x-scroll">
<div className={cc(
'w-full',
'grid grid-cols-[auto_1fr_auto_auto] ',
'gap-2 sm:gap-3 items-center',
)}>
{children}
</div>
</div>
</div>;
}

View File

@ -1,5 +1,6 @@
'use client';
import SiteGrid from '@/components/SiteGrid';
import { cc } from '@/utility/css';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
@ -16,6 +17,8 @@ export default function AdminNav({
const pathname = usePathname();
return (
<SiteGrid
contentMain={
<div className={cc(
'border-b border-gray-900 pb-2',
)}>
@ -36,5 +39,7 @@ export default function AdminNav({
</Link>)}
</div>
</div>
}
/>
);
}

View File

@ -0,0 +1,10 @@
import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus';
export default function DeleteButton () {
return <SubmitButtonWithStatus
title="Delete"
icon={<span className="inline-flex text-[18px]">×</span>}
>
Delete
</SubmitButtonWithStatus>;
}

23
src/admin/EditButton.tsx Normal file
View File

@ -0,0 +1,23 @@
import Link from 'next/link';
import { FaRegEdit } from 'react-icons/fa';
export default function EditButton ({
href,
label = 'Edit',
}: {
href: string,
label?: string,
}) {
return (
<Link
title="Edit"
href={href}
className="button"
>
<FaRegEdit className="translate-y-[-0.5px]" />
<span className="hidden sm:inline-block">
{label}
</span>
</Link>
);
}

View File

@ -1,4 +1,4 @@
import { Fragment, ReactNode } from 'react';
import { Fragment } from 'react';
import PhotoUploadInput from '@/photo/PhotoUploadInput';
import Link from 'next/link';
import PhotoTiny from '@/photo/PhotoTiny';
@ -11,7 +11,6 @@ import {
deleteBlobPhotoAction,
syncCacheAction,
} from '@/photo/actions';
import { FaRegEdit } from 'react-icons/fa';
import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus';
import { pathForBlobUrl } from '@/services/blob';
import {
@ -33,12 +32,17 @@ import {
PaginationParams,
getPaginationForSearchParams,
} from '@/site/pagination';
import AdminGrid from '@/admin/AdminGrid';
import DeleteButton from '@/admin/DeleteButton';
import EditButton from '@/admin/EditButton';
export const runtime = 'edge';
const DEBUG_PHOTO_BLOBS = false;
export default async function AdminPage({ searchParams }: PaginationParams) {
export default async function AdminTagsPage({
searchParams,
}: PaginationParams) {
const { offset, limit } = getPaginationForSearchParams(searchParams);
const [
@ -85,7 +89,7 @@ export default async function AdminPage({ searchParams }: PaginationParams) {
label={`Photos Files (${blobPhotoUrls.length})`}
/>}
<div className="space-y-4">
<AdminGrid title={`Photos (${count})`}>
<AdminGrid>
{photos.map(photo =>
<Fragment key={photo.id}>
<PhotoTiny
@ -149,59 +153,6 @@ export default async function AdminPage({ searchParams }: PaginationParams) {
);
}
function AdminGrid ({
title,
children,
}: {
title: string,
children: ReactNode,
}) {
return <div className="space-y-4">
<div className="font-bold">
{title}
</div>
<div className="min-w-[14rem] overflow-x-scroll">
<div className={cc(
'w-full',
'grid grid-cols-[auto_1fr_auto_auto] ',
'gap-2 sm:gap-3 items-center',
)}>
{children}
</div>
</div>
</div>;
}
function EditButton ({
href,
label = 'Edit',
}: {
href: string,
label?: string,
}) {
return (
<Link
title="Edit"
href={href}
className="button"
>
<FaRegEdit className="translate-y-[-0.5px]" />
<span className="hidden sm:inline-block">
{label}
</span>
</Link>
);
}
function DeleteButton () {
return <SubmitButtonWithStatus
title="Delete"
icon={<span className="inline-flex text-[18px]">×</span>}
>
Delete
</SubmitButtonWithStatus>;
}
function BlobUrls ({
blobUrls,
label,

View File

@ -0,0 +1,42 @@
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';
export const runtime = 'edge';
export default async function AdminPhotosPage() {
const tags = await getUniqueTagsCached();
return (
<SiteGrid
contentMain={
<div className="space-y-6">
<div className="space-y-4">
<AdminGrid>
{tags.map(tag =>
<Fragment key={tag}>
<div className="flex-grow w-full">
{tag}
</div>
<div />
<div />
<FormWithConfirm
action={deletePhotoTagGloballyAction}
confirmText={
// eslint-disable-next-line max-len
`Are you sure you want to remove "${tag}?" from all photos?`}
>
<input type="hidden" name="tag" value={tag} />
<DeleteButton />
</FormWithConfirm>
</Fragment>)}
</AdminGrid>
</div>
</div>}
/>
);
}