Create /admin/recipes page
This commit is contained in:
parent
3d57de3997
commit
2c35587f5d
@ -1,16 +1,16 @@
|
|||||||
import AdminTagTable from '@/admin/AdminTagTable';
|
import AdminRecipeTable from '@/admin/AdminRecipeTable';
|
||||||
import SiteGrid from '@/components/SiteGrid';
|
import SiteGrid from '@/components/SiteGrid';
|
||||||
import { getUniqueTagsHiddenCached } from '@/photo/cache';
|
import { getUniqueRecipesCached } from '@/photo/cache';
|
||||||
|
|
||||||
export default async function AdminTagsPage() {
|
export default async function AdminTagsPage() {
|
||||||
const tags = await getUniqueTagsHiddenCached().catch(() => []);
|
const recipes = await getUniqueRecipesCached().catch(() => []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SiteGrid
|
<SiteGrid
|
||||||
contentMain={
|
contentMain={
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<AdminTagTable {...{ tags }} />
|
<AdminRecipeTable {...{ recipes }} />
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div>}
|
||||||
/>
|
/>
|
||||||
|
|||||||
35
src/admin/AdminRecipeBadge.tsx
Normal file
35
src/admin/AdminRecipeBadge.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { photoLabelForCount } from '@/photo';
|
||||||
|
import { clsx } from 'clsx/lite';
|
||||||
|
import Badge from '@/components/Badge';
|
||||||
|
import PhotoRecipe from '@/recipe/PhotoRecipe';
|
||||||
|
|
||||||
|
export default function AdminRecipeBadge({
|
||||||
|
recipe,
|
||||||
|
count,
|
||||||
|
hideBadge,
|
||||||
|
}: {
|
||||||
|
recipe: string,
|
||||||
|
count: number,
|
||||||
|
hideBadge?: boolean,
|
||||||
|
}) {
|
||||||
|
const renderBadgeContent = () =>
|
||||||
|
<div className={clsx(
|
||||||
|
'inline-flex items-center gap-2',
|
||||||
|
'translate-y-[1px]',
|
||||||
|
)}>
|
||||||
|
<PhotoRecipe {...{ recipe }} />
|
||||||
|
<div className="text-dim uppercase">
|
||||||
|
<span>{count}</span>
|
||||||
|
<span className="hidden xs:inline-block">
|
||||||
|
|
||||||
|
{photoLabelForCount(count)}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
hideBadge
|
||||||
|
? renderBadgeContent()
|
||||||
|
: <Badge className="py-[3px]!">{renderBadgeContent()}</Badge>
|
||||||
|
);
|
||||||
|
}
|
||||||
43
src/admin/AdminRecipeTable.tsx
Normal file
43
src/admin/AdminRecipeTable.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import FormWithConfirm from '@/components/FormWithConfirm';
|
||||||
|
import { deletePhotoTagGloballyAction } from '@/photo/actions';
|
||||||
|
import AdminTable from '@/admin/AdminTable';
|
||||||
|
import { Fragment } from 'react';
|
||||||
|
import DeleteFormButton from '@/admin/DeleteFormButton';
|
||||||
|
import { photoQuantityText } from '@/photo';
|
||||||
|
import EditButton from '@/admin/EditButton';
|
||||||
|
import { pathForAdminRecipeEdit } from '@/app/paths';
|
||||||
|
import { clsx } from 'clsx/lite';
|
||||||
|
import { formatRecipe, Recipes, sortRecipesWithCount } from '@/recipe';
|
||||||
|
import AdminRecipeBadge from './AdminRecipeBadge';
|
||||||
|
|
||||||
|
export default function AdminRecipeTable({
|
||||||
|
recipes,
|
||||||
|
}: {
|
||||||
|
recipes: Recipes
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<AdminTable>
|
||||||
|
{sortRecipesWithCount(recipes).map(({ recipe, count }) =>
|
||||||
|
<Fragment key={recipe}>
|
||||||
|
<div className="pr-2 col-span-2">
|
||||||
|
<AdminRecipeBadge {...{ recipe, count }} />
|
||||||
|
</div>
|
||||||
|
<div className={clsx(
|
||||||
|
'flex flex-nowrap',
|
||||||
|
'gap-2 sm:gap-3 items-center',
|
||||||
|
)}>
|
||||||
|
<EditButton path={pathForAdminRecipeEdit(recipe)} />
|
||||||
|
<FormWithConfirm
|
||||||
|
action={deletePhotoTagGloballyAction}
|
||||||
|
confirmText={
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
`Are you sure you want to remove "${formatRecipe(recipe)}" from ${photoQuantityText(count, false).toLowerCase()}?`}
|
||||||
|
>
|
||||||
|
<input type="hidden" name="recipe" value={recipe} />
|
||||||
|
<DeleteFormButton clearLocalState />
|
||||||
|
</FormWithConfirm>
|
||||||
|
</div>
|
||||||
|
</Fragment>)}
|
||||||
|
</AdminTable>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -100,6 +100,9 @@ export const pathForAdminPhotoEdit = (photo: PhotoOrPhotoId) =>
|
|||||||
export const pathForAdminTagEdit = (tag: string) =>
|
export const pathForAdminTagEdit = (tag: string) =>
|
||||||
`${PATH_ADMIN_TAGS}/${tag}/${EDIT}`;
|
`${PATH_ADMIN_TAGS}/${tag}/${EDIT}`;
|
||||||
|
|
||||||
|
export const pathForAdminRecipeEdit = (recipe: string) =>
|
||||||
|
`${PATH_ADMIN_RECIPES}/${recipe}/${EDIT}`;
|
||||||
|
|
||||||
type PhotoOrPhotoId = Photo | string;
|
type PhotoOrPhotoId = Photo | string;
|
||||||
|
|
||||||
const getPhotoId = (photoOrPhotoId: PhotoOrPhotoId) =>
|
const getPhotoId = (photoOrPhotoId: PhotoOrPhotoId) =>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user