Create admin recipe detail page
This commit is contained in:
parent
2c35587f5d
commit
769d6b64bb
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
74
src/admin/AdminRecipeForm.tsx
Normal file
74
src/admin/AdminRecipeForm.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -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,
|
||||||
}: {
|
}: {
|
||||||
Loading…
Reference in New Issue
Block a user