Add multi-tag query to batch edit
This commit is contained in:
parent
dc8dedd806
commit
38d372dd72
@ -12,6 +12,7 @@ import { Tags } from '@/tag';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { PATH_GRID_INFERRED } from '@/site/paths';
|
||||
import PhotoTagFieldset from './PhotoTagFieldset';
|
||||
import { tagMultiplePhotosAction } from '@/photo/actions';
|
||||
|
||||
export default function AdminBatchEditPanelClient({
|
||||
uniqueTags,
|
||||
@ -26,6 +27,8 @@ export default function AdminBatchEditPanelClient({
|
||||
setSelectedPhotoIds,
|
||||
} = useAppState();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const [tags, setTags] = useState<string>();
|
||||
const [tagErrorMessage, setTagErrorMessage] = useState('');
|
||||
const isTagging = tags !== undefined;
|
||||
@ -44,6 +47,7 @@ export default function AdminBatchEditPanelClient({
|
||||
setTags(undefined);
|
||||
setTagErrorMessage('');
|
||||
}}
|
||||
disabled={isLoading}
|
||||
>
|
||||
Cancel
|
||||
</LoaderButton>
|
||||
@ -51,7 +55,15 @@ export default function AdminBatchEditPanelClient({
|
||||
className="min-h-[2.5rem]"
|
||||
// eslint-disable-next-line max-len
|
||||
confirmText={`Are you sure you want to apply tags to ${selectedPhotoIds?.length} ${photosPlural}? This action cannot be undone.`}
|
||||
disabled={!tags || Boolean(tagErrorMessage)}
|
||||
onClick={() => {
|
||||
setIsLoading(true);
|
||||
tagMultiplePhotosAction(
|
||||
tags,
|
||||
selectedPhotoIds ?? [],
|
||||
)
|
||||
.finally(() => setIsLoading(false));
|
||||
}}
|
||||
disabled={!tags || Boolean(tagErrorMessage) || isLoading}
|
||||
primary
|
||||
>
|
||||
Apply Tags
|
||||
@ -60,10 +72,13 @@ export default function AdminBatchEditPanelClient({
|
||||
: <>
|
||||
{(selectedPhotoIds?.length ?? 0) > 0 &&
|
||||
<>
|
||||
<LoaderButton onClick={() => setTags('')}>
|
||||
<LoaderButton
|
||||
onClick={() => setTags('')}
|
||||
isLoading={isLoading}
|
||||
>
|
||||
Tag ...
|
||||
</LoaderButton>
|
||||
<DeleteButton />
|
||||
<DeleteButton disabled={isLoading} />
|
||||
</>}
|
||||
<LoaderButton
|
||||
icon={<IoCloseSharp size={20} className="translate-y-[-1.5px]" />}
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
renamePhotoTagGlobally,
|
||||
getPhoto,
|
||||
getPhotos,
|
||||
addTagsToPhotos,
|
||||
} from '@/photo/db/query';
|
||||
import { GetPhotosOptions, areOptionsSensitive } from './db';
|
||||
import {
|
||||
@ -47,6 +48,7 @@ import { generateAiImageQueries } from './ai/server';
|
||||
import { createStreamableValue } from 'ai/rsc';
|
||||
import { convertUploadToPhoto } from './storage';
|
||||
import { UrlAddStatus } from '@/admin/AdminUploadsClient';
|
||||
import { convertStringToArray } from '@/utility/string';
|
||||
|
||||
// Private actions
|
||||
|
||||
@ -203,6 +205,18 @@ export const updatePhotoAction = async (formData: FormData) =>
|
||||
redirect(PATH_ADMIN_PHOTOS);
|
||||
});
|
||||
|
||||
export const tagMultiplePhotosAction = (
|
||||
tags: string,
|
||||
photoIds: string[],
|
||||
) =>
|
||||
runAuthenticatedAdminServerAction(async () => {
|
||||
await addTagsToPhotos(
|
||||
convertStringToArray(tags, false) ?? [],
|
||||
photoIds,
|
||||
);
|
||||
revalidateAllKeysAndPaths();
|
||||
});
|
||||
|
||||
export const toggleFavoritePhotoAction = async (
|
||||
photoId: string,
|
||||
shouldRedirect?: boolean,
|
||||
|
||||
@ -244,17 +244,19 @@ export const renamePhotoTagGlobally = (tag: string, updatedTag: string) =>
|
||||
`, 'renamePhotoTagGlobally');
|
||||
|
||||
export const addTagsToPhotos = (tags: string[], photoIds: string[]) =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
safelyQueryPhotos(() => query(`
|
||||
UPDATE photos
|
||||
SET tags = (
|
||||
SELECT array_agg(DISTINCT elem)
|
||||
FROM unnest(
|
||||
array_cat(tags, ARRAY${convertArrayToPostgresString(tags, 'brackets')})
|
||||
array_cat(tags, $1)
|
||||
) AS elem
|
||||
)
|
||||
WHERE id IN ${convertArrayToPostgresString(photoIds, 'brackets')}
|
||||
LIMIT ${photoIds.length}
|
||||
`, 'addTagsToPhotos');
|
||||
WHERE id = ANY($2)
|
||||
`, [
|
||||
convertArrayToPostgresString(tags),
|
||||
convertArrayToPostgresString(photoIds),
|
||||
]), 'addTagsToPhotos');
|
||||
|
||||
export const deletePhoto = (id: string) =>
|
||||
safelyQueryPhotos(() => sql`
|
||||
|
||||
@ -43,11 +43,13 @@ export const sql = <T extends QueryResultRow>(
|
||||
|
||||
export const convertArrayToPostgresString = (
|
||||
array?: string[],
|
||||
type: 'braces' | 'brackets' = 'braces',
|
||||
type: 'braces' | 'brackets' | 'parentheses' = 'braces',
|
||||
) => array
|
||||
? type === 'braces'
|
||||
? `{${array.join(',')}}`
|
||||
: `[${array.map(i => `'${i}'`).join(',')}]`
|
||||
: type === 'brackets'
|
||||
? `[${array.map(i => `'${i}'`).join(',')}]`
|
||||
: `(${array.map(i => `'${i}'`).join(',')})`
|
||||
: null;
|
||||
|
||||
const isTemplateStringsArray = (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user