Add progress feedback to add all photos button
This commit is contained in:
parent
959b6f0bc2
commit
25b7629a60
@ -3,7 +3,6 @@
|
||||
import ErrorNote from '@/components/ErrorNote';
|
||||
import FieldSetWithStatus from '@/components/FieldSetWithStatus';
|
||||
import Container from '@/components/Container';
|
||||
import LoaderButton from '@/components/primitives/LoaderButton';
|
||||
import { addAllUploadsAction } from '@/photo/actions';
|
||||
import { PATH_ADMIN_PHOTOS } from '@/site/paths';
|
||||
import {
|
||||
@ -21,6 +20,7 @@ import { clsx } from 'clsx/lite';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { Dispatch, SetStateAction, useRef, useState } from 'react';
|
||||
import { BiCheckCircle, BiImageAdd } from 'react-icons/bi';
|
||||
import ProgressButton from '@/components/primitives/ProgressButton';
|
||||
|
||||
const UPLOAD_BATCH_SIZE = 4;
|
||||
|
||||
@ -45,6 +45,7 @@ export default function AdminAddAllUploads({
|
||||
const [tags, setTags] = useState('');
|
||||
const [actionErrorMessage, setActionErrorMessage] = useState('');
|
||||
const [tagErrorMessage, setTagErrorMessage] = useState('');
|
||||
const [addingProgress, setAddingProgress] = useState<number>();
|
||||
const [isAddingComplete, setIsAddingComplete] = useState(false);
|
||||
|
||||
const router = useRouter();
|
||||
@ -64,6 +65,10 @@ export default function AdminAddAllUploads({
|
||||
: `Adding ${addedUploadUrls.current.length} of ${storageUrls.length}`
|
||||
);
|
||||
setButtonSubheadText(data?.subhead ?? '');
|
||||
setAddingProgress((
|
||||
addedUploadUrls.current.length /
|
||||
storageUrls.length
|
||||
) * 0.95);
|
||||
setAddedUploadUrls?.(current => {
|
||||
const urls = data?.addedUploadUrls.split(',') ?? [];
|
||||
const updatedUrls = current
|
||||
@ -76,6 +81,7 @@ export default function AdminAddAllUploads({
|
||||
} catch (e: any) {
|
||||
setIsAdding(false);
|
||||
setButtonText('Try Again');
|
||||
setAddingProgress(undefined);
|
||||
setActionErrorMessage(e);
|
||||
}
|
||||
};
|
||||
@ -131,8 +137,10 @@ export default function AdminAddAllUploads({
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<LoaderButton
|
||||
className="primary w-full justify-center"
|
||||
<ProgressButton
|
||||
primary
|
||||
className="w-full justify-center"
|
||||
progress={addingProgress}
|
||||
isLoading={isAdding}
|
||||
disabled={Boolean(tagErrorMessage) || isAddingComplete}
|
||||
icon={isAddingComplete
|
||||
@ -152,11 +160,13 @@ export default function AdminAddAllUploads({
|
||||
}
|
||||
setButtonText('Complete');
|
||||
setButtonSubheadText('All uploads added');
|
||||
setAddingProgress(1);
|
||||
setIsAdding(false);
|
||||
setIsAddingComplete(true);
|
||||
await sleep(1000).then(() =>
|
||||
router.push(PATH_ADMIN_PHOTOS));
|
||||
} catch (e: any) {
|
||||
setAddingProgress(undefined);
|
||||
setIsAdding(false);
|
||||
setButtonText('Try Again');
|
||||
setActionErrorMessage(e);
|
||||
@ -166,7 +176,7 @@ export default function AdminAddAllUploads({
|
||||
hideTextOnMobile={false}
|
||||
>
|
||||
{buttonText}
|
||||
</LoaderButton>
|
||||
</ProgressButton>
|
||||
{buttonSubheadText &&
|
||||
<div className="text-dim text-sm text-center">
|
||||
{buttonSubheadText}
|
||||
|
||||
@ -42,9 +42,9 @@ export default function AdminOutdatedClient({
|
||||
</span>
|
||||
</>}
|
||||
accessory={<LoaderButton
|
||||
primary
|
||||
icon={<IconGrSync className="translate-y-[1px]" />}
|
||||
hideTextOnMobile={false}
|
||||
className="primary"
|
||||
onClick={async () => {
|
||||
if (window.confirm(
|
||||
// eslint-disable-next-line max-len
|
||||
|
||||
@ -38,7 +38,7 @@ export default function AdminPhotosClient({
|
||||
contentMain={
|
||||
<div className="space-y-4">
|
||||
<div className="flex">
|
||||
<div className="grow">
|
||||
<div className="grow min-w-0">
|
||||
<PhotoUpload
|
||||
shouldResize={!PRO_MODE_ENABLED}
|
||||
isUploading={isUploading}
|
||||
|
||||
@ -61,7 +61,6 @@ export default function ImageInput({
|
||||
<LoaderButton
|
||||
type="button"
|
||||
isLoading={loading}
|
||||
className="primary"
|
||||
icon={<FiUploadCloud
|
||||
size={18}
|
||||
className="translate-x-[-0.5px] translate-y-[0.5px]"
|
||||
@ -69,6 +68,7 @@ export default function ImageInput({
|
||||
aria-disabled={loading}
|
||||
onClick={() => inputRef.current?.click()}
|
||||
hideTextOnMobile={false}
|
||||
primary
|
||||
>
|
||||
{loading
|
||||
? 'Uploading'
|
||||
|
||||
@ -16,14 +16,12 @@ export default function SubmitButtonWithStatus({
|
||||
children,
|
||||
disabled,
|
||||
className,
|
||||
primary,
|
||||
type: _type,
|
||||
...buttonProps
|
||||
}: {
|
||||
onFormStatusChange?: (pending: boolean) => void
|
||||
onFormSubmitToastMessage?: string
|
||||
onFormSubmit?: () => void
|
||||
primary?: boolean
|
||||
} & ComponentProps<typeof LoaderButton>) {
|
||||
const { pending } = useFormStatus();
|
||||
|
||||
@ -45,18 +43,17 @@ export default function SubmitButtonWithStatus({
|
||||
|
||||
return (
|
||||
<LoaderButton
|
||||
{...buttonProps}
|
||||
type="submit"
|
||||
disabled={disabled}
|
||||
className={clsx(
|
||||
className,
|
||||
'inline-flex items-center gap-2',
|
||||
primary && 'primary',
|
||||
className,
|
||||
)}
|
||||
icon={icon}
|
||||
spinnerColor={spinnerColor}
|
||||
styleAs={styleAs}
|
||||
isLoading={pending}
|
||||
{...buttonProps}
|
||||
>
|
||||
{children}
|
||||
</LoaderButton>
|
||||
|
||||
@ -11,6 +11,7 @@ export default function LoaderButton(props: {
|
||||
styleAs?: 'button' | 'link' | 'link-without-hover'
|
||||
hideTextOnMobile?: boolean
|
||||
shouldPreventDefault?: boolean
|
||||
primary?: boolean
|
||||
} & ButtonHTMLAttributes<HTMLButtonElement>) {
|
||||
const {
|
||||
children,
|
||||
@ -20,6 +21,7 @@ export default function LoaderButton(props: {
|
||||
styleAs = 'button',
|
||||
hideTextOnMobile = true,
|
||||
shouldPreventDefault,
|
||||
primary,
|
||||
type = 'button',
|
||||
onClick,
|
||||
disabled,
|
||||
@ -41,10 +43,12 @@ export default function LoaderButton(props: {
|
||||
'link h-4 active:text-medium',
|
||||
'disabled:!bg-transparent',
|
||||
]
|
||||
: ['h-9']),
|
||||
: ['h-9']
|
||||
),
|
||||
styleAs === 'link' && 'hover:text-dim',
|
||||
styleAs === 'link-without-hover' && 'hover:text-main',
|
||||
'inline-flex items-center gap-2 self-start whitespace-nowrap',
|
||||
primary && 'primary',
|
||||
className,
|
||||
)}
|
||||
disabled={isLoading || disabled}
|
||||
|
||||
37
src/components/primitives/ProgressButton.tsx
Normal file
37
src/components/primitives/ProgressButton.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
'use client';
|
||||
|
||||
import { ComponentProps } from 'react';
|
||||
import LoaderButton from './LoaderButton';
|
||||
import { clsx } from 'clsx/lite';
|
||||
|
||||
export default function ProgressButton({
|
||||
progress,
|
||||
isLoading,
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: {
|
||||
progress?: number
|
||||
} & ComponentProps<typeof LoaderButton>) {
|
||||
return (
|
||||
<LoaderButton
|
||||
{...props}
|
||||
isLoading={isLoading ?? ((progress ?? 1) < 1)}
|
||||
className={clsx(
|
||||
'relative overflow-hidden justify-center',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
style={{ transform: `scaleX(${progress ?? 0})`}}
|
||||
className={clsx(
|
||||
'absolute top-0 left-0 w-full',
|
||||
'transition-all duration-500 origin-left',
|
||||
'bg-invert h-[2px]',
|
||||
progress === undefined ? 'opacity-0' : 'opacity-100',
|
||||
)}
|
||||
/>
|
||||
{children}
|
||||
</LoaderButton>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user