Add hidden, favorite options to bulk uploads
This commit is contained in:
parent
7127bdf354
commit
0f0d9a32e3
@ -46,14 +46,12 @@ export default function ComponentsPage() {
|
|||||||
'*:inline-flex *:gap-1 [&_input]:-translate-y-0.5',
|
'*:inline-flex *:gap-1 [&_input]:-translate-y-0.5',
|
||||||
)}>
|
)}>
|
||||||
<FieldSetWithStatus
|
<FieldSetWithStatus
|
||||||
id="grid"
|
|
||||||
label="Grid"
|
label="Grid"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
value={shouldShowBaselineGrid ? 'true' : 'false'}
|
value={shouldShowBaselineGrid ? 'true' : 'false'}
|
||||||
onChange={e => setShouldShowBaselineGrid?.(e === 'true')}
|
onChange={e => setShouldShowBaselineGrid?.(e === 'true')}
|
||||||
/>
|
/>
|
||||||
<FieldSetWithStatus
|
<FieldSetWithStatus
|
||||||
id="components"
|
|
||||||
label="Components"
|
label="Components"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
value={debugComponents ? 'true' : 'false'}
|
value={debugComponents ? 'true' : 'false'}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { getStorageUploadUrlsNoStore } from '@/platforms/storage/cache';
|
import { getStorageUploadUrlsNoStore } from '@/platforms/storage/cache';
|
||||||
import SiteGrid from '@/components/SiteGrid';
|
import SiteGrid from '@/components/SiteGrid';
|
||||||
import { getUniqueTagsCached, getUniqueRecipesCached } from '@/photo/cache';
|
import { getUniqueTagsCached } from '@/photo/cache';
|
||||||
import AdminUploadsClient from '@/admin/AdminUploadsClient';
|
import AdminUploadsClient from '@/admin/AdminUploadsClient';
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
import { PATH_ADMIN_PHOTOS } from '@/app/paths';
|
import { PATH_ADMIN_PHOTOS } from '@/app/paths';
|
||||||
@ -10,7 +10,6 @@ export const maxDuration = 60;
|
|||||||
export default async function AdminUploadsPage() {
|
export default async function AdminUploadsPage() {
|
||||||
const urls = await getStorageUploadUrlsNoStore();
|
const urls = await getStorageUploadUrlsNoStore();
|
||||||
const uniqueTags = await getUniqueTagsCached();
|
const uniqueTags = await getUniqueTagsCached();
|
||||||
const uniqueRecipes = await getUniqueRecipesCached();
|
|
||||||
|
|
||||||
if (urls.length === 0) {
|
if (urls.length === 0) {
|
||||||
redirect(PATH_ADMIN_PHOTOS);
|
redirect(PATH_ADMIN_PHOTOS);
|
||||||
@ -21,7 +20,6 @@ export default async function AdminUploadsPage() {
|
|||||||
<AdminUploadsClient {...{
|
<AdminUploadsClient {...{
|
||||||
urls,
|
urls,
|
||||||
uniqueTags,
|
uniqueTags,
|
||||||
uniqueRecipes,
|
|
||||||
}} />}
|
}} />}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -43,11 +43,14 @@ export default function AdminBatchUploadActions({
|
|||||||
}) {
|
}) {
|
||||||
const { updateAdminData } = useAppState();
|
const { updateAdminData } = useAppState();
|
||||||
|
|
||||||
const [buttonText, setButtonText] = useState('Add All Uploads');
|
const [showBulkSettings, setShowBulkSettings] = useState(false);
|
||||||
const [showTags, setShowTags] = useState(false);
|
|
||||||
const [tags, setTags] = useState('');
|
const [tags, setTags] = useState('');
|
||||||
const [actionErrorMessage, setActionErrorMessage] = useState('');
|
const [favorite, setFavorite] = useState('false');
|
||||||
|
const [hidden, setHidden] = useState('false');
|
||||||
const [tagErrorMessage, setTagErrorMessage] = useState('');
|
const [tagErrorMessage, setTagErrorMessage] = useState('');
|
||||||
|
|
||||||
|
const [buttonText, setButtonText] = useState('Add All Uploads');
|
||||||
|
const [actionErrorMessage, setActionErrorMessage] = useState('');
|
||||||
const [addingProgress, setAddingProgress] = useState<number>();
|
const [addingProgress, setAddingProgress] = useState<number>();
|
||||||
const [isAddingComplete, setIsAddingComplete] = useState(false);
|
const [isAddingComplete, setIsAddingComplete] = useState(false);
|
||||||
|
|
||||||
@ -58,7 +61,11 @@ export default function AdminBatchUploadActions({
|
|||||||
try {
|
try {
|
||||||
const stream = await addAllUploadsAction({
|
const stream = await addAllUploadsAction({
|
||||||
uploadUrls,
|
uploadUrls,
|
||||||
tags: showTags ? tags : undefined,
|
...showBulkSettings && {
|
||||||
|
tags,
|
||||||
|
favorite,
|
||||||
|
hidden,
|
||||||
|
},
|
||||||
takenAtLocal: generateLocalPostgresString(),
|
takenAtLocal: generateLocalPostgresString(),
|
||||||
takenAtNaiveLocal: generateLocalNaivePostgresString(),
|
takenAtNaiveLocal: generateLocalNaivePostgresString(),
|
||||||
shouldRevalidateAllKeysAndPaths: isFinalBatch,
|
shouldRevalidateAllKeysAndPaths: isFinalBatch,
|
||||||
@ -116,29 +123,43 @@ export default function AdminBatchUploadActions({
|
|||||||
'grow',
|
'grow',
|
||||||
tagErrorMessage ? 'text-error' : 'text-main',
|
tagErrorMessage ? 'text-error' : 'text-main',
|
||||||
)}>
|
)}>
|
||||||
{showTags
|
{showBulkSettings
|
||||||
? tagErrorMessage || 'Add tags to all uploads'
|
? tagErrorMessage || 'Apply to all uploads'
|
||||||
: `Found ${storageUrls.length} uploads`}
|
: `Found ${storageUrls.length} uploads`}
|
||||||
</div>
|
</div>
|
||||||
<FieldSetWithStatus
|
<FieldSetWithStatus
|
||||||
id="show-tags"
|
label="Apply Bulk Settings"
|
||||||
label="Apply tags"
|
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
value={showTags ? 'true' : 'false'}
|
value={showBulkSettings ? 'true' : 'false'}
|
||||||
onChange={value => setShowTags(value === 'true')}
|
onChange={value => setShowBulkSettings(value === 'true')}
|
||||||
readOnly={isAdding}
|
readOnly={isAdding}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{showTags && !actionErrorMessage &&
|
{showBulkSettings && !actionErrorMessage &&
|
||||||
<PhotoTagFieldset
|
<div className="space-y-3">
|
||||||
tags={tags}
|
<PhotoTagFieldset
|
||||||
tagOptions={uniqueTags}
|
label="Tags"
|
||||||
onChange={setTags}
|
tags={tags}
|
||||||
onError={setTagErrorMessage}
|
tagOptions={uniqueTags}
|
||||||
readOnly={isAdding}
|
onChange={setTags}
|
||||||
openOnLoad
|
onError={setTagErrorMessage}
|
||||||
hideLabel
|
readOnly={isAdding}
|
||||||
/>}
|
/>
|
||||||
|
<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">
|
<div className="space-y-2">
|
||||||
<ProgressButton
|
<ProgressButton
|
||||||
primary
|
primary
|
||||||
|
|||||||
@ -35,7 +35,6 @@ export default function AdminRecipeForm({
|
|||||||
className="space-y-8"
|
className="space-y-8"
|
||||||
>
|
>
|
||||||
<FieldSetWithStatus
|
<FieldSetWithStatus
|
||||||
id="updatedRecipeRaw"
|
|
||||||
label="New Recipe Name"
|
label="New Recipe Name"
|
||||||
value={updatedRecipeRaw}
|
value={updatedRecipeRaw}
|
||||||
onChange={setUpdatedRecipeRaw}
|
onChange={setUpdatedRecipeRaw}
|
||||||
|
|||||||
@ -35,7 +35,6 @@ export default function AdminTagForm({
|
|||||||
className="space-y-8"
|
className="space-y-8"
|
||||||
>
|
>
|
||||||
<FieldSetWithStatus
|
<FieldSetWithStatus
|
||||||
id="updatedTagRaw"
|
|
||||||
label="New Tag Name"
|
label="New Tag Name"
|
||||||
value={updatedTagRaw}
|
value={updatedTagRaw}
|
||||||
onChange={setUpdatedTagRaw}
|
onChange={setUpdatedTagRaw}
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import AdminBatchUploadActions from './AdminBatchUploadActions';
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { Tags } from '@/tag';
|
import { Tags } from '@/tag';
|
||||||
import AdminUploadsTable from './AdminUploadsTable';
|
import AdminUploadsTable from './AdminUploadsTable';
|
||||||
import { Recipes } from '@/recipe';
|
|
||||||
|
|
||||||
export type UrlAddStatus = StorageListItem & {
|
export type UrlAddStatus = StorageListItem & {
|
||||||
status?: 'waiting' | 'adding' | 'added'
|
status?: 'waiting' | 'adding' | 'added'
|
||||||
@ -19,7 +18,6 @@ export default function AdminUploadsClient({
|
|||||||
}: {
|
}: {
|
||||||
urls: StorageListResponse
|
urls: StorageListResponse
|
||||||
uniqueTags?: Tags
|
uniqueTags?: Tags
|
||||||
uniqueRecipes?: Recipes
|
|
||||||
}) {
|
}) {
|
||||||
const [isAdding, setIsAdding] = useState(false);
|
const [isAdding, setIsAdding] = useState(false);
|
||||||
const [urlAddStatuses, setUrlAddStatuses] = useState<UrlAddStatus[]>(urls);
|
const [urlAddStatuses, setUrlAddStatuses] = useState<UrlAddStatus[]>(urls);
|
||||||
|
|||||||
@ -15,7 +15,6 @@ export default function PhotoTagFieldset(props: {
|
|||||||
'tagOptions'
|
'tagOptions'
|
||||||
>>) {
|
>>) {
|
||||||
const {
|
const {
|
||||||
id,
|
|
||||||
tags,
|
tags,
|
||||||
tagOptions,
|
tagOptions,
|
||||||
onChange,
|
onChange,
|
||||||
@ -42,7 +41,7 @@ export default function PhotoTagFieldset(props: {
|
|||||||
<FieldSetWithStatus
|
<FieldSetWithStatus
|
||||||
{...rest}
|
{...rest}
|
||||||
inputRef={ref}
|
inputRef={ref}
|
||||||
id={id ?? 'tags'}
|
label="Tags"
|
||||||
value={tags}
|
value={tags}
|
||||||
tagOptions={convertTagsForForm(tagOptions)}
|
tagOptions={convertTagsForForm(tagOptions)}
|
||||||
onChange={tags => {
|
onChange={tags => {
|
||||||
|
|||||||
@ -7,9 +7,10 @@ import { clsx } from 'clsx/lite';
|
|||||||
import { FieldSetType, AnnotatedTag } from '@/photo/form';
|
import { FieldSetType, AnnotatedTag } from '@/photo/form';
|
||||||
import TagInput from './TagInput';
|
import TagInput from './TagInput';
|
||||||
import { FiChevronDown } from 'react-icons/fi';
|
import { FiChevronDown } from 'react-icons/fi';
|
||||||
|
import { parameterize } from '@/utility/string';
|
||||||
|
|
||||||
export default function FieldSetWithStatus({
|
export default function FieldSetWithStatus({
|
||||||
id,
|
id: _id,
|
||||||
label,
|
label,
|
||||||
note,
|
note,
|
||||||
error,
|
error,
|
||||||
@ -34,8 +35,8 @@ export default function FieldSetWithStatus({
|
|||||||
hideLabel,
|
hideLabel,
|
||||||
checkboxAccessory,
|
checkboxAccessory,
|
||||||
}: {
|
}: {
|
||||||
id: string
|
id?: string
|
||||||
label?: string
|
label: string
|
||||||
note?: string
|
note?: string
|
||||||
error?: string
|
error?: string
|
||||||
value: string
|
value: string
|
||||||
@ -59,6 +60,8 @@ export default function FieldSetWithStatus({
|
|||||||
hideLabel?: boolean
|
hideLabel?: boolean
|
||||||
checkboxAccessory?: React.ReactNode
|
checkboxAccessory?: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
|
const id = _id || parameterize(label);
|
||||||
|
|
||||||
const { pending } = useFormStatus();
|
const { pending } = useFormStatus();
|
||||||
|
|
||||||
const renderInput =
|
const renderInput =
|
||||||
@ -66,13 +69,13 @@ export default function FieldSetWithStatus({
|
|||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
id={id}
|
id={id}
|
||||||
name={id}
|
name={id}
|
||||||
|
type={type}
|
||||||
value={value}
|
value={value}
|
||||||
checked={type === 'checkbox' ? value === 'true' : undefined}
|
checked={type === 'checkbox' ? value === 'true' : undefined}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
onChange={e => onChange?.(type === 'checkbox'
|
onChange={e => onChange?.(type === 'checkbox'
|
||||||
? e.target.value === 'true' ? 'false' : 'true'
|
? e.target.value === 'true' ? 'false' : 'true'
|
||||||
: e.target.value)}
|
: e.target.value)}
|
||||||
type={type}
|
|
||||||
spellCheck={spellCheck}
|
spellCheck={spellCheck}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
autoCapitalize={!capitalize ? 'off' : undefined}
|
autoCapitalize={!capitalize ? 'off' : undefined}
|
||||||
|
|||||||
@ -89,12 +89,16 @@ export const createPhotoAction = async (formData: FormData) =>
|
|||||||
export const addAllUploadsAction = async ({
|
export const addAllUploadsAction = async ({
|
||||||
uploadUrls,
|
uploadUrls,
|
||||||
tags,
|
tags,
|
||||||
|
favorite,
|
||||||
|
hidden,
|
||||||
takenAtLocal,
|
takenAtLocal,
|
||||||
takenAtNaiveLocal,
|
takenAtNaiveLocal,
|
||||||
shouldRevalidateAllKeysAndPaths = true,
|
shouldRevalidateAllKeysAndPaths = true,
|
||||||
}: {
|
}: {
|
||||||
uploadUrls: string[]
|
uploadUrls: string[]
|
||||||
tags?: string
|
tags?: string
|
||||||
|
favorite?: string
|
||||||
|
hidden?: string
|
||||||
takenAtLocal: string
|
takenAtLocal: string
|
||||||
takenAtNaiveLocal: string
|
takenAtNaiveLocal: string
|
||||||
shouldRevalidateAllKeysAndPaths?: boolean
|
shouldRevalidateAllKeysAndPaths?: boolean
|
||||||
@ -106,7 +110,7 @@ export const addAllUploadsAction = async ({
|
|||||||
let currentUploadUrl = '';
|
let currentUploadUrl = '';
|
||||||
let progress = 0;
|
let progress = 0;
|
||||||
|
|
||||||
const stream = createStreamableValue<UrlAddStatus>();
|
const stream = createStreamableValue<Omit<UrlAddStatus, 'fileName'>>();
|
||||||
|
|
||||||
const streamUpdate = (
|
const streamUpdate = (
|
||||||
statusMessage: string,
|
statusMessage: string,
|
||||||
@ -157,6 +161,8 @@ export const addAllUploadsAction = async ({
|
|||||||
title,
|
title,
|
||||||
caption,
|
caption,
|
||||||
tags: tags || aiTags,
|
tags: tags || aiTags,
|
||||||
|
hidden,
|
||||||
|
favorite,
|
||||||
semanticDescription,
|
semanticDescription,
|
||||||
takenAt: formDataFromExif.takenAt || takenAtLocal,
|
takenAt: formDataFromExif.takenAt || takenAtLocal,
|
||||||
takenAtNaive: formDataFromExif.takenAtNaive || takenAtNaiveLocal,
|
takenAtNaive: formDataFromExif.takenAtNaive || takenAtNaiveLocal,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user