Improve add/edit upload functionality
This commit is contained in:
parent
6ec4bfbfe8
commit
144e68b965
@ -1,4 +1,4 @@
|
||||
import { PATH_ADMIN } from '@/app/paths';
|
||||
import { PARAM_UPLOAD_TITLE, PATH_ADMIN } from '@/app/paths';
|
||||
import { extractImageDataFromBlobPath } from '@/photo/server';
|
||||
import { redirect } from 'next/navigation';
|
||||
import {
|
||||
@ -19,10 +19,12 @@ export const maxDuration = 60;
|
||||
|
||||
interface Params {
|
||||
params: Promise<{ uploadPath: string }>
|
||||
searchParams: Promise<Record<string, string | string[] | undefined>>
|
||||
}
|
||||
|
||||
export default async function UploadPage({ params }: Params) {
|
||||
const { uploadPath } = await params;
|
||||
export default async function UploadPage({ params, searchParams }: Params) {
|
||||
const uploadPath = (await params).uploadPath;
|
||||
const title = (await searchParams)[PARAM_UPLOAD_TITLE];
|
||||
|
||||
const {
|
||||
blobId,
|
||||
@ -62,13 +64,19 @@ export default async function UploadPage({ params }: Params) {
|
||||
: undefined,
|
||||
]);
|
||||
|
||||
if (formDataFromExif && recipeTitle) {
|
||||
const hasAiTextGeneration = AI_TEXT_GENERATION_ENABLED;
|
||||
let textFieldsToAutoGenerate = AI_TEXT_AUTO_GENERATED_FIELDS;
|
||||
|
||||
if (formDataFromExif) {
|
||||
if (recipeTitle) {
|
||||
formDataFromExif.recipeTitle = recipeTitle;
|
||||
}
|
||||
|
||||
const hasAiTextGeneration = AI_TEXT_GENERATION_ENABLED;
|
||||
|
||||
const textFieldsToAutoGenerate = AI_TEXT_AUTO_GENERATED_FIELDS;
|
||||
if (typeof title === 'string') {
|
||||
formDataFromExif.title = title;
|
||||
textFieldsToAutoGenerate = textFieldsToAutoGenerate
|
||||
.filter(field => field !== 'title');
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
!isDataMissing
|
||||
|
||||
66
src/admin/AddUploadButton.tsx
Normal file
66
src/admin/AddUploadButton.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import LoaderButton from '@/components/primitives/LoaderButton';
|
||||
import { addUploadAction } from '@/photo/actions';
|
||||
import {
|
||||
generateLocalNaivePostgresString,
|
||||
generateLocalPostgresString,
|
||||
} from '@/utility/date';
|
||||
import { pathForAdminUploadUrl } from '@/app/paths';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { BiImageAdd } from 'react-icons/bi';
|
||||
import { ComponentProps, useState } from 'react';
|
||||
|
||||
export default function AddUploadButton({
|
||||
url,
|
||||
title,
|
||||
onAddStart,
|
||||
onAddFinish,
|
||||
shouldRedirectToAdminPhotos,
|
||||
...props
|
||||
}: {
|
||||
url: string
|
||||
title?: string
|
||||
onAddStart?: () => void
|
||||
onAddFinish?: (success: boolean) => void
|
||||
shouldRedirectToAdminPhotos: boolean
|
||||
} & ComponentProps<typeof LoaderButton>) {
|
||||
const router = useRouter();
|
||||
|
||||
const [isAddingLocal, setIsAddingLocal] = useState(false);
|
||||
|
||||
return (
|
||||
<LoaderButton
|
||||
{...props}
|
||||
icon={<BiImageAdd
|
||||
size={18}
|
||||
className="translate-x-[1px] translate-y-[1px]"
|
||||
/>}
|
||||
onClick={() => {
|
||||
onAddStart?.();
|
||||
setIsAddingLocal(true);
|
||||
addUploadAction({
|
||||
url,
|
||||
title,
|
||||
takenAtLocal: generateLocalPostgresString(),
|
||||
takenAtNaiveLocal: generateLocalNaivePostgresString(),
|
||||
})
|
||||
.then(() => {
|
||||
if (shouldRedirectToAdminPhotos) {
|
||||
router.push(pathForAdminUploadUrl(url));
|
||||
} else {
|
||||
onAddFinish?.(true);
|
||||
setIsAddingLocal(false);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
onAddFinish?.(false);
|
||||
setIsAddingLocal(false);
|
||||
});
|
||||
}}
|
||||
isLoading={isAddingLocal}
|
||||
tooltip="Add directly"
|
||||
hideText="never"
|
||||
>
|
||||
Add
|
||||
</LoaderButton>
|
||||
);
|
||||
}
|
||||
@ -26,7 +26,7 @@ export default function AdminUploadsTable({
|
||||
{...{
|
||||
...status,
|
||||
tabIndex: index + 1,
|
||||
shouldRedirectToAdminPhotosOnDelete: urlAddStatuses.length <= 1,
|
||||
shouldRedirectAfterAction: urlAddStatuses.length <= 1,
|
||||
isAdding,
|
||||
isDeleting,
|
||||
isComplete,
|
||||
|
||||
@ -9,13 +9,12 @@ import ResponsiveDate from '@/components/ResponsiveDate';
|
||||
import Spinner from '@/components/Spinner';
|
||||
import { FaRegCircleCheck } from 'react-icons/fa6';
|
||||
import { pathForAdminUploadUrl } from '@/app/paths';
|
||||
import DeleteBlobButton from './DeleteUploadButton';
|
||||
import DeleteUploadButton from './DeleteUploadButton';
|
||||
import { Dispatch, SetStateAction, useEffect, useRef } from 'react';
|
||||
import { isElementEntirelyInViewport } from '@/utility/dom';
|
||||
import FieldSetWithStatus from '@/components/FieldSetWithStatus';
|
||||
import EditButton from './EditButton';
|
||||
import LoaderButton from '@/components/primitives/LoaderButton';
|
||||
import { BiImageAdd } from 'react-icons/bi';
|
||||
import AddUploadButton from './AddUploadButton';
|
||||
|
||||
export default function AdminUploadsTableRow({
|
||||
url,
|
||||
@ -25,7 +24,7 @@ export default function AdminUploadsTableRow({
|
||||
uploadedAt,
|
||||
size,
|
||||
tabIndex,
|
||||
shouldRedirectToAdminPhotosOnDelete,
|
||||
shouldRedirectAfterAction,
|
||||
isAdding,
|
||||
isDeleting,
|
||||
isComplete,
|
||||
@ -33,7 +32,7 @@ export default function AdminUploadsTableRow({
|
||||
setUrlAddStatuses,
|
||||
}: UrlAddStatus & {
|
||||
tabIndex: number
|
||||
shouldRedirectToAdminPhotosOnDelete: boolean
|
||||
shouldRedirectAfterAction: boolean
|
||||
isAdding?: boolean
|
||||
isDeleting?: boolean
|
||||
isComplete?: boolean
|
||||
@ -58,6 +57,18 @@ export default function AdminUploadsTableRow({
|
||||
|
||||
const isRowLoading = isAdding || isDeleting || isComplete || Boolean(status);
|
||||
|
||||
const updateStatus = (updatedStatus: Partial<UrlAddStatus>) => {
|
||||
setUrlAddStatuses?.(statuses => statuses.map(status => status.url === url
|
||||
? {
|
||||
...status,
|
||||
...updatedStatus,
|
||||
}
|
||||
: status));
|
||||
};
|
||||
|
||||
const removeRow = () => setUrlAddStatuses?.(statuses => statuses
|
||||
.filter(({ url: urlToRemove }) => urlToRemove !== url));
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
@ -90,14 +101,8 @@ export default function AdminUploadsTableRow({
|
||||
<FieldSetWithStatus
|
||||
label="Title"
|
||||
value={draftTitle}
|
||||
onChange={titleUpdated => {
|
||||
setUrlAddStatuses?.(statuses => statuses.map(status => ({
|
||||
...status,
|
||||
draftTitle: status.url === url
|
||||
? titleUpdated
|
||||
: status.draftTitle,
|
||||
})));
|
||||
}}
|
||||
onChange={titleUpdated =>
|
||||
updateStatus({ draftTitle: titleUpdated })}
|
||||
placeholder="Title (optional)"
|
||||
tabIndex={tabIndex}
|
||||
readOnly={isRowLoading}
|
||||
@ -115,32 +120,29 @@ export default function AdminUploadsTableRow({
|
||||
/>}
|
||||
</>
|
||||
: <>
|
||||
<LoaderButton
|
||||
icon={<BiImageAdd
|
||||
size={18}
|
||||
className="translate-x-[1px] translate-y-[1px]"
|
||||
/>}
|
||||
<AddUploadButton
|
||||
url={url}
|
||||
onAddStart={() => updateStatus({
|
||||
status: 'adding',
|
||||
statusMessage: 'Adding ...',
|
||||
})}
|
||||
onAddFinish={removeRow}
|
||||
shouldRedirectToAdminPhotos={shouldRedirectAfterAction}
|
||||
disabled={isRowLoading}
|
||||
tooltip="Add directly"
|
||||
hideText="never"
|
||||
>
|
||||
Add
|
||||
</LoaderButton>
|
||||
/>
|
||||
<EditButton
|
||||
path={pathForAdminUploadUrl(url)}
|
||||
path={pathForAdminUploadUrl(url, draftTitle)}
|
||||
disabled={isRowLoading}
|
||||
tooltip="Review photo details"
|
||||
hideText="always"
|
||||
/>
|
||||
<DeleteBlobButton
|
||||
<DeleteUploadButton
|
||||
urls={[url]}
|
||||
shouldRedirectToAdminPhotos={
|
||||
shouldRedirectToAdminPhotosOnDelete}
|
||||
shouldRedirectToAdminPhotos={shouldRedirectAfterAction}
|
||||
onDeleteStart={() => setIsDeleting?.(true)}
|
||||
onDelete={() => {
|
||||
setIsDeleting?.(false);
|
||||
setUrlAddStatuses?.(statuses => statuses
|
||||
.filter(({ url: urlToRemove }) => urlToRemove !== url));
|
||||
removeRow();
|
||||
}}
|
||||
disabled={isRowLoading}
|
||||
tooltip="Delete upload"
|
||||
|
||||
@ -67,6 +67,7 @@ export const PATH_API_PRESIGNED_URL = `${PATH_API_STORAGE}/presigned-url`;
|
||||
|
||||
// Modifiers
|
||||
const EDIT = 'edit';
|
||||
export const PARAM_UPLOAD_TITLE = 'title';
|
||||
|
||||
// Special characters
|
||||
export const MISSING_FIELD = '-';
|
||||
@ -103,8 +104,9 @@ type PhotoPathParams = { photo: PhotoOrPhotoId } & PhotoSetCategory & {
|
||||
showRecipe?: boolean
|
||||
};
|
||||
|
||||
export const pathForAdminUploadUrl = (url: string) =>
|
||||
`${PATH_ADMIN_UPLOADS}/${encodeURIComponent(url)}`;
|
||||
export const pathForAdminUploadUrl = (url: string, title?: string) =>
|
||||
// eslint-disable-next-line max-len
|
||||
`${PATH_ADMIN_UPLOADS}/${encodeURIComponent(url)}${title ? `?${PARAM_UPLOAD_TITLE}=${encodeURIComponent(title)}` : ''}`;
|
||||
|
||||
export const pathForAdminPhotoEdit = (photo: PhotoOrPhotoId) =>
|
||||
`${PATH_ADMIN_PHOTOS}/${getPhotoId(photo)}/${EDIT}`;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user