Create admin recipe detail page

This commit is contained in:
Sam Becker 2025-03-12 18:11:16 -05:00
parent 2c35587f5d
commit 769d6b64bb
4 changed files with 92 additions and 18 deletions

View File

@ -1,31 +1,31 @@
import AdminChildPage from '@/components/AdminChildPage'; import AdminChildPage from '@/components/AdminChildPage';
import { redirect } from 'next/navigation'; import { redirect } from 'next/navigation';
import { getPhotosCached } from '@/photo/cache'; import { getPhotosCached } from '@/photo/cache';
import TagForm from '@/tag/TagForm'; import { PATH_ADMIN, PATH_ADMIN_TAGS, pathForRecipe } from '@/app/paths';
import { PATH_ADMIN, PATH_ADMIN_TAGS, pathForTag } from '@/app/paths';
import PhotoLightbox from '@/photo/PhotoLightbox'; import PhotoLightbox from '@/photo/PhotoLightbox';
import { getPhotosMeta } from '@/photo/db/query'; import { getPhotosMeta } from '@/photo/db/query';
import AdminTagBadge from '@/admin/AdminTagBadge'; import AdminRecipeBadge from '@/admin/AdminRecipeBadge';
import AdminRecipeForm from '@/admin/AdminRecipeForm';
const MAX_PHOTO_TO_SHOW = 6; const MAX_PHOTO_TO_SHOW = 6;
interface Props { interface Props {
params: Promise<{ tag: string }> params: Promise<{ recipe: string }>
} }
export default async function PhotoPageEdit({ export default async function RecipePageEdit({
params, params,
}: Props) { }: Props) {
const { tag: tagFromParams } = await params; const { recipe: recipeFromParams } = await params;
const tag = decodeURIComponent(tagFromParams); const recipe = decodeURIComponent(recipeFromParams);
const [ const [
{ count }, { count },
photos, photos,
] = await Promise.all([ ] = await Promise.all([
getPhotosMeta({ tag }), getPhotosMeta({ recipe }),
getPhotosCached({ tag, limit: MAX_PHOTO_TO_SHOW }), getPhotosCached({ recipe, limit: MAX_PHOTO_TO_SHOW }),
]); ]);
if (count === 0) { redirect(PATH_ADMIN); } if (count === 0) { redirect(PATH_ADMIN); }
@ -34,15 +34,15 @@ export default async function PhotoPageEdit({
<AdminChildPage <AdminChildPage
backPath={PATH_ADMIN_TAGS} backPath={PATH_ADMIN_TAGS}
backLabel="Tags" backLabel="Tags"
breadcrumb={<AdminTagBadge {...{ tag, count, hideBadge: true }} />} breadcrumb={<AdminRecipeBadge {...{ recipe, count, hideBadge: true }} />}
> >
<TagForm {...{ tag, photos }}> <AdminRecipeForm {...{ recipe, photos }}>
<PhotoLightbox <PhotoLightbox
{...{ count, photos, tag }} {...{ count, photos, recipe }}
maxPhotosToShow={MAX_PHOTO_TO_SHOW} maxPhotosToShow={MAX_PHOTO_TO_SHOW}
moreLink={pathForTag(tag)} moreLink={pathForRecipe(recipe)}
/> />
</TagForm> </AdminRecipeForm>
</AdminChildPage> </AdminChildPage>
); );
}; };

View File

@ -1,7 +1,7 @@
import AdminChildPage from '@/components/AdminChildPage'; import AdminChildPage from '@/components/AdminChildPage';
import { redirect } from 'next/navigation'; import { redirect } from 'next/navigation';
import { getPhotosCached } from '@/photo/cache'; import { getPhotosCached } from '@/photo/cache';
import TagForm from '@/tag/TagForm'; import AdminTagForm from '@/admin/AdminTagForm';
import { PATH_ADMIN, PATH_ADMIN_TAGS, pathForTag } from '@/app/paths'; import { PATH_ADMIN, PATH_ADMIN_TAGS, pathForTag } from '@/app/paths';
import PhotoLightbox from '@/photo/PhotoLightbox'; import PhotoLightbox from '@/photo/PhotoLightbox';
import { getPhotosMeta } from '@/photo/db/query'; import { getPhotosMeta } from '@/photo/db/query';
@ -36,13 +36,13 @@ export default async function PhotoPageEdit({
backLabel="Tags" backLabel="Tags"
breadcrumb={<AdminTagBadge {...{ tag, count, hideBadge: true }} />} breadcrumb={<AdminTagBadge {...{ tag, count, hideBadge: true }} />}
> >
<TagForm {...{ tag, photos }}> <AdminTagForm {...{ tag, photos }}>
<PhotoLightbox <PhotoLightbox
{...{ count, photos, tag }} {...{ count, photos, tag }}
maxPhotosToShow={MAX_PHOTO_TO_SHOW} maxPhotosToShow={MAX_PHOTO_TO_SHOW}
moreLink={pathForTag(tag)} moreLink={pathForTag(tag)}
/> />
</TagForm> </AdminTagForm>
</AdminChildPage> </AdminChildPage>
); );
}; };

View File

@ -0,0 +1,74 @@
'use client';
import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus';
import Link from 'next/link';
import { PATH_ADMIN_RECIPES } from '@/app/paths';
import FieldSetWithStatus from '@/components/FieldSetWithStatus';
import { ReactNode, useMemo, useState } from 'react';
import { renamePhotoTagGloballyAction } from '@/photo/actions';
import { parameterize } from '@/utility/string';
import { useAppState } from '@/state/AppState';
export default function AdminRecipeForm({
recipe,
children,
}: {
recipe: string
children?: ReactNode
}) {
const { invalidateSwr } = useAppState();
const [updatedRecipeRaw, setUpdatedRecipeRaw] = useState(recipe);
const updatedRecipe = useMemo(() =>
parameterize(updatedRecipeRaw)
, [updatedRecipeRaw]);
const isFormValid = (
updatedRecipe &&
updatedRecipe !== recipe
);
return (
<form
action={renamePhotoTagGloballyAction}
className="space-y-8"
>
<FieldSetWithStatus
id="updatedTagRaw"
label="New Tag Name"
value={updatedRecipeRaw}
onChange={setUpdatedRecipeRaw}
/>
{/* Form data: tag to be replaced */}
<input
name="recipe"
value={recipe}
hidden
readOnly
/>
{/* Form data: updated tag */}
<input
name="updatedRecipe"
value={updatedRecipe}
hidden
readOnly
/>
{children}
<div className="flex gap-3">
<Link
className="button"
href={PATH_ADMIN_RECIPES}
>
Cancel
</Link>
<SubmitButtonWithStatus
disabled={!isFormValid}
onFormSubmit={invalidateSwr}
>
Update
</SubmitButtonWithStatus>
</div>
</form>
);
}

View File

@ -9,7 +9,7 @@ import { renamePhotoTagGloballyAction } from '@/photo/actions';
import { parameterize } from '@/utility/string'; import { parameterize } from '@/utility/string';
import { useAppState } from '@/state/AppState'; import { useAppState } from '@/state/AppState';
export default function TagForm({ export default function AdminTagForm({
tag, tag,
children, children,
}: { }: {