Add guidance around sync buttons
This commit is contained in:
parent
333f1b99d7
commit
21ed815cba
@ -6,7 +6,10 @@ import { revalidatePath } from 'next/cache';
|
|||||||
import { cookies } from 'next/headers';
|
import { cookies } from 'next/headers';
|
||||||
import { TIMEZONE_COOKIE_NAME } from '@/utility/timezone';
|
import { TIMEZONE_COOKIE_NAME } from '@/utility/timezone';
|
||||||
import { getOutdatedPhotosCount } from '@/photo/db/query';
|
import { getOutdatedPhotosCount } from '@/photo/db/query';
|
||||||
import { PRESERVE_ORIGINAL_UPLOADS } from '@/app/config';
|
import {
|
||||||
|
AI_TEXT_GENERATION_ENABLED,
|
||||||
|
PRESERVE_ORIGINAL_UPLOADS,
|
||||||
|
} from '@/app/config';
|
||||||
|
|
||||||
export const maxDuration = 60;
|
export const maxDuration = 60;
|
||||||
|
|
||||||
@ -45,6 +48,7 @@ export default async function AdminPhotosPage() {
|
|||||||
photosCount,
|
photosCount,
|
||||||
photosCountOutdated,
|
photosCountOutdated,
|
||||||
shouldResize: !PRESERVE_ORIGINAL_UPLOADS,
|
shouldResize: !PRESERVE_ORIGINAL_UPLOADS,
|
||||||
|
hasAiTextGeneration: AI_TEXT_GENERATION_ENABLED,
|
||||||
onLastUpload: async () => {
|
onLastUpload: async () => {
|
||||||
'use server';
|
'use server';
|
||||||
// Update upload count in admin nav
|
// Update upload count in admin nav
|
||||||
|
|||||||
@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
import SiteGrid from '@/components/SiteGrid';
|
import SiteGrid from '@/components/SiteGrid';
|
||||||
import {
|
|
||||||
AI_TEXT_GENERATION_ENABLED,
|
|
||||||
} from '@/app/config';
|
|
||||||
import AdminPhotosTable from '@/admin/AdminPhotosTable';
|
import AdminPhotosTable from '@/admin/AdminPhotosTable';
|
||||||
import AdminPhotosTableInfinite from '@/admin/AdminPhotosTableInfinite';
|
import AdminPhotosTableInfinite from '@/admin/AdminPhotosTableInfinite';
|
||||||
import PathLoaderButton from '@/components/primitives/PathLoaderButton';
|
import PathLoaderButton from '@/components/primitives/PathLoaderButton';
|
||||||
@ -23,6 +20,7 @@ export default function AdminPhotosClient({
|
|||||||
photosCountOutdated,
|
photosCountOutdated,
|
||||||
blobPhotoUrls,
|
blobPhotoUrls,
|
||||||
shouldResize,
|
shouldResize,
|
||||||
|
hasAiTextGeneration,
|
||||||
onLastUpload,
|
onLastUpload,
|
||||||
infiniteScrollInitial,
|
infiniteScrollInitial,
|
||||||
infiniteScrollMultiple,
|
infiniteScrollMultiple,
|
||||||
@ -33,6 +31,7 @@ export default function AdminPhotosClient({
|
|||||||
photosCountOutdated: number
|
photosCountOutdated: number
|
||||||
blobPhotoUrls: StorageListResponse
|
blobPhotoUrls: StorageListResponse
|
||||||
shouldResize: boolean
|
shouldResize: boolean
|
||||||
|
hasAiTextGeneration: boolean
|
||||||
onLastUpload: () => Promise<void>
|
onLastUpload: () => Promise<void>
|
||||||
infiniteScrollInitial: number
|
infiniteScrollInitial: number
|
||||||
infiniteScrollMultiple: number
|
infiniteScrollMultiple: number
|
||||||
@ -89,14 +88,14 @@ export default function AdminPhotosClient({
|
|||||||
<div className="space-y-[6px] sm:space-y-[10px]">
|
<div className="space-y-[6px] sm:space-y-[10px]">
|
||||||
<AdminPhotosTable
|
<AdminPhotosTable
|
||||||
photos={photos}
|
photos={photos}
|
||||||
hasAiTextGeneration={AI_TEXT_GENERATION_ENABLED}
|
hasAiTextGeneration={hasAiTextGeneration}
|
||||||
timezone={timezone}
|
timezone={timezone}
|
||||||
/>
|
/>
|
||||||
{photosCount > photos.length &&
|
{photosCount > photos.length &&
|
||||||
<AdminPhotosTableInfinite
|
<AdminPhotosTableInfinite
|
||||||
initialOffset={infiniteScrollInitial}
|
initialOffset={infiniteScrollInitial}
|
||||||
itemsPerPage={infiniteScrollMultiple}
|
itemsPerPage={infiniteScrollMultiple}
|
||||||
hasAiTextGeneration={AI_TEXT_GENERATION_ENABLED}
|
hasAiTextGeneration={hasAiTextGeneration}
|
||||||
timezone={timezone}
|
timezone={timezone}
|
||||||
/>}
|
/>}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import LoaderButton from '@/components/primitives/LoaderButton';
|
import LoaderButton from '@/components/primitives/LoaderButton';
|
||||||
import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus';
|
import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus';
|
||||||
|
import Tooltip from '@/components/Tooltip';
|
||||||
import { getExifDataAction } from '@/photo/actions';
|
import { getExifDataAction } from '@/photo/actions';
|
||||||
import { PhotoFormData } from '@/photo/form';
|
import { PhotoFormData } from '@/photo/form';
|
||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
@ -18,8 +19,11 @@ export default function ExifCaptureButton({
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Tooltip
|
||||||
|
content="Refresh form with EXIF data from original file"
|
||||||
|
supportMobile={false}
|
||||||
|
>
|
||||||
<LoaderButton
|
<LoaderButton
|
||||||
title="Update photo from original file"
|
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
@ -36,5 +40,6 @@ export default function ExifCaptureButton({
|
|||||||
>
|
>
|
||||||
EXIF
|
EXIF
|
||||||
</LoaderButton>
|
</LoaderButton>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { syncPhotoAction } from '@/photo/actions';
|
|||||||
import IconGrSync from '@/app/IconGrSync';
|
import IconGrSync from '@/app/IconGrSync';
|
||||||
import { toastSuccess } from '@/toast';
|
import { toastSuccess } from '@/toast';
|
||||||
import { ComponentProps, useState } from 'react';
|
import { ComponentProps, useState } from 'react';
|
||||||
|
import Tooltip from '@/components/Tooltip';
|
||||||
|
|
||||||
export default function PhotoSyncButton({
|
export default function PhotoSyncButton({
|
||||||
photoId,
|
photoId,
|
||||||
@ -33,8 +34,11 @@ export default function PhotoSyncButton({
|
|||||||
confirmText.push('This action cannot be undone.');
|
confirmText.push('This action cannot be undone.');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Tooltip
|
||||||
|
content="Regenerate photo data"
|
||||||
|
supportMobile={false}
|
||||||
|
>
|
||||||
<LoaderButton
|
<LoaderButton
|
||||||
title="Update photo from original file"
|
|
||||||
className={className}
|
className={className}
|
||||||
icon={<IconGrSync
|
icon={<IconGrSync
|
||||||
className="translate-y-[0.5px] translate-x-[0.5px]"
|
className="translate-y-[0.5px] translate-x-[0.5px]"
|
||||||
@ -57,5 +61,6 @@ export default function PhotoSyncButton({
|
|||||||
isLoading={isSyncing || isSyncingExternal}
|
isLoading={isSyncing || isSyncingExternal}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
/>
|
/>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,14 +10,16 @@ import useClickInsideOutside from '@/utility/useClickInsideOutside';
|
|||||||
export default function TooltipPrimitive({
|
export default function TooltipPrimitive({
|
||||||
content,
|
content,
|
||||||
className,
|
className,
|
||||||
classNameTrigger,
|
classNameTrigger: classNameTriggerProp,
|
||||||
sideOffset = 10,
|
sideOffset = 10,
|
||||||
|
supportMobile = true,
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
content?: ReactNode
|
content?: ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
classNameTrigger?: string
|
classNameTrigger?: string
|
||||||
sideOffset?: number
|
sideOffset?: number
|
||||||
|
supportMobile?: boolean
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}) {
|
}) {
|
||||||
const refTrigger = useRef<HTMLButtonElement>(null);
|
const refTrigger = useRef<HTMLButtonElement>(null);
|
||||||
@ -27,6 +29,8 @@ export default function TooltipPrimitive({
|
|||||||
|
|
||||||
const supportsHover = useSupportsHover();
|
const supportsHover = useSupportsHover();
|
||||||
|
|
||||||
|
const includeButton = supportMobile && !supportsHover;
|
||||||
|
|
||||||
useClickInsideOutside({
|
useClickInsideOutside({
|
||||||
htmlElements: [refTrigger, refContent],
|
htmlElements: [refTrigger, refContent],
|
||||||
onClickOutside: () => {
|
onClickOutside: () => {
|
||||||
@ -34,17 +38,23 @@ export default function TooltipPrimitive({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const classNameTrigger = clsx('link cursor-default', classNameTriggerProp);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip.Provider delayDuration={100}>
|
<Tooltip.Provider delayDuration={100}>
|
||||||
<Tooltip.Root open={!supportsHover ? isOpen : undefined}>
|
<Tooltip.Root open={!supportsHover ? isOpen : undefined}>
|
||||||
<Tooltip.Trigger asChild>
|
<Tooltip.Trigger asChild>
|
||||||
<button
|
{includeButton
|
||||||
|
? <button
|
||||||
ref={refTrigger}
|
ref={refTrigger}
|
||||||
onClick={!supportsHover ? () => setIsOpen(!isOpen) : undefined}
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
className={clsx('link cursor-default', classNameTrigger)}
|
className={classNameTrigger}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
|
: <span className={classNameTrigger}>
|
||||||
|
{children}
|
||||||
|
</span>}
|
||||||
</Tooltip.Trigger>
|
</Tooltip.Trigger>
|
||||||
<Tooltip.Portal >
|
<Tooltip.Portal >
|
||||||
<Tooltip.Content
|
<Tooltip.Content
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user