Add hidden, favorite options to bulk uploads

This commit is contained in:
Sam Becker 2025-03-15 00:20:36 -05:00
parent 7127bdf354
commit 0f0d9a32e3
9 changed files with 57 additions and 36 deletions

View File

@ -46,14 +46,12 @@ export default function ComponentsPage() {
'*:inline-flex *:gap-1 [&_input]:-translate-y-0.5',
)}>
<FieldSetWithStatus
id="grid"
label="Grid"
type="checkbox"
value={shouldShowBaselineGrid ? 'true' : 'false'}
onChange={e => setShouldShowBaselineGrid?.(e === 'true')}
/>
<FieldSetWithStatus
id="components"
label="Components"
type="checkbox"
value={debugComponents ? 'true' : 'false'}

View File

@ -1,6 +1,6 @@
import { getStorageUploadUrlsNoStore } from '@/platforms/storage/cache';
import SiteGrid from '@/components/SiteGrid';
import { getUniqueTagsCached, getUniqueRecipesCached } from '@/photo/cache';
import { getUniqueTagsCached } from '@/photo/cache';
import AdminUploadsClient from '@/admin/AdminUploadsClient';
import { redirect } from 'next/navigation';
import { PATH_ADMIN_PHOTOS } from '@/app/paths';
@ -10,7 +10,6 @@ export const maxDuration = 60;
export default async function AdminUploadsPage() {
const urls = await getStorageUploadUrlsNoStore();
const uniqueTags = await getUniqueTagsCached();
const uniqueRecipes = await getUniqueRecipesCached();
if (urls.length === 0) {
redirect(PATH_ADMIN_PHOTOS);
@ -21,7 +20,6 @@ export default async function AdminUploadsPage() {
<AdminUploadsClient {...{
urls,
uniqueTags,
uniqueRecipes,
}} />}
/>
);

View File

@ -43,11 +43,14 @@ export default function AdminBatchUploadActions({
}) {
const { updateAdminData } = useAppState();
const [buttonText, setButtonText] = useState('Add All Uploads');
const [showTags, setShowTags] = useState(false);
const [showBulkSettings, setShowBulkSettings] = useState(false);
const [tags, setTags] = useState('');
const [actionErrorMessage, setActionErrorMessage] = useState('');
const [favorite, setFavorite] = useState('false');
const [hidden, setHidden] = useState('false');
const [tagErrorMessage, setTagErrorMessage] = useState('');
const [buttonText, setButtonText] = useState('Add All Uploads');
const [actionErrorMessage, setActionErrorMessage] = useState('');
const [addingProgress, setAddingProgress] = useState<number>();
const [isAddingComplete, setIsAddingComplete] = useState(false);
@ -58,7 +61,11 @@ export default function AdminBatchUploadActions({
try {
const stream = await addAllUploadsAction({
uploadUrls,
tags: showTags ? tags : undefined,
...showBulkSettings && {
tags,
favorite,
hidden,
},
takenAtLocal: generateLocalPostgresString(),
takenAtNaiveLocal: generateLocalNaivePostgresString(),
shouldRevalidateAllKeysAndPaths: isFinalBatch,
@ -116,29 +123,43 @@ export default function AdminBatchUploadActions({
'grow',
tagErrorMessage ? 'text-error' : 'text-main',
)}>
{showTags
? tagErrorMessage || 'Add tags to all uploads'
{showBulkSettings
? tagErrorMessage || 'Apply to all uploads'
: `Found ${storageUrls.length} uploads`}
</div>
<FieldSetWithStatus
id="show-tags"
label="Apply tags"
label="Apply Bulk Settings"
type="checkbox"
value={showTags ? 'true' : 'false'}
onChange={value => setShowTags(value === 'true')}
value={showBulkSettings ? 'true' : 'false'}
onChange={value => setShowBulkSettings(value === 'true')}
readOnly={isAdding}
/>
</div>
{showTags && !actionErrorMessage &&
{showBulkSettings && !actionErrorMessage &&
<div className="space-y-3">
<PhotoTagFieldset
label="Tags"
tags={tags}
tagOptions={uniqueTags}
onChange={setTags}
onError={setTagErrorMessage}
readOnly={isAdding}
openOnLoad
hideLabel
/>}
/>
<FieldSetWithStatus
label="Favorite"
type="checkbox"
value={favorite}
onChange={setFavorite}
readOnly={isAdding}
/>
<FieldSetWithStatus
label="Hidden"
type="checkbox"
value={hidden}
onChange={setHidden}
readOnly={isAdding}
/>
</div>}
<div className="space-y-2">
<ProgressButton
primary

View File

@ -35,7 +35,6 @@ export default function AdminRecipeForm({
className="space-y-8"
>
<FieldSetWithStatus
id="updatedRecipeRaw"
label="New Recipe Name"
value={updatedRecipeRaw}
onChange={setUpdatedRecipeRaw}

View File

@ -35,7 +35,6 @@ export default function AdminTagForm({
className="space-y-8"
>
<FieldSetWithStatus
id="updatedTagRaw"
label="New Tag Name"
value={updatedTagRaw}
onChange={setUpdatedTagRaw}

View File

@ -5,7 +5,6 @@ import AdminBatchUploadActions from './AdminBatchUploadActions';
import { useMemo, useState } from 'react';
import { Tags } from '@/tag';
import AdminUploadsTable from './AdminUploadsTable';
import { Recipes } from '@/recipe';
export type UrlAddStatus = StorageListItem & {
status?: 'waiting' | 'adding' | 'added'
@ -19,7 +18,6 @@ export default function AdminUploadsClient({
}: {
urls: StorageListResponse
uniqueTags?: Tags
uniqueRecipes?: Recipes
}) {
const [isAdding, setIsAdding] = useState(false);
const [urlAddStatuses, setUrlAddStatuses] = useState<UrlAddStatus[]>(urls);

View File

@ -15,7 +15,6 @@ export default function PhotoTagFieldset(props: {
'tagOptions'
>>) {
const {
id,
tags,
tagOptions,
onChange,
@ -42,7 +41,7 @@ export default function PhotoTagFieldset(props: {
<FieldSetWithStatus
{...rest}
inputRef={ref}
id={id ?? 'tags'}
label="Tags"
value={tags}
tagOptions={convertTagsForForm(tagOptions)}
onChange={tags => {

View File

@ -7,9 +7,10 @@ import { clsx } from 'clsx/lite';
import { FieldSetType, AnnotatedTag } from '@/photo/form';
import TagInput from './TagInput';
import { FiChevronDown } from 'react-icons/fi';
import { parameterize } from '@/utility/string';
export default function FieldSetWithStatus({
id,
id: _id,
label,
note,
error,
@ -34,8 +35,8 @@ export default function FieldSetWithStatus({
hideLabel,
checkboxAccessory,
}: {
id: string
label?: string
id?: string
label: string
note?: string
error?: string
value: string
@ -59,6 +60,8 @@ export default function FieldSetWithStatus({
hideLabel?: boolean
checkboxAccessory?: React.ReactNode
}) {
const id = _id || parameterize(label);
const { pending } = useFormStatus();
const renderInput =
@ -66,13 +69,13 @@ export default function FieldSetWithStatus({
ref={inputRef}
id={id}
name={id}
type={type}
value={value}
checked={type === 'checkbox' ? value === 'true' : undefined}
placeholder={placeholder}
onChange={e => onChange?.(type === 'checkbox'
? e.target.value === 'true' ? 'false' : 'true'
: e.target.value)}
type={type}
spellCheck={spellCheck}
autoComplete="off"
autoCapitalize={!capitalize ? 'off' : undefined}

View File

@ -89,12 +89,16 @@ export const createPhotoAction = async (formData: FormData) =>
export const addAllUploadsAction = async ({
uploadUrls,
tags,
favorite,
hidden,
takenAtLocal,
takenAtNaiveLocal,
shouldRevalidateAllKeysAndPaths = true,
}: {
uploadUrls: string[]
tags?: string
favorite?: string
hidden?: string
takenAtLocal: string
takenAtNaiveLocal: string
shouldRevalidateAllKeysAndPaths?: boolean
@ -106,7 +110,7 @@ export const addAllUploadsAction = async ({
let currentUploadUrl = '';
let progress = 0;
const stream = createStreamableValue<UrlAddStatus>();
const stream = createStreamableValue<Omit<UrlAddStatus, 'fileName'>>();
const streamUpdate = (
statusMessage: string,
@ -157,6 +161,8 @@ export const addAllUploadsAction = async ({
title,
caption,
tags: tags || aiTags,
hidden,
favorite,
semanticDescription,
takenAt: formDataFromExif.takenAt || takenAtLocal,
takenAtNaive: formDataFromExif.takenAtNaive || takenAtNaiveLocal,