Vercel/src/admin/AdminPhotosClient.tsx
Sam Becker 59f5c74269
Chromatic sorting (#284)
* Test color palette extraction

* Fix import

* Add hex <> oklch conversions

* Add 'hue' storage to photos

* Consolidate color modules

* Add chromatic config, track missing color data

* Bump deps

* Fix lens text test

* Finalize color storage

* Refactor color imports

* Hide form color data when disabled

* Store all average oklch color components

* Finalize color-config language

* Optimize photo syncing for color data

* Only update color data when syncing if possible

* Build out all color sorts

* Debug image colors

* Improve color debugging

* Improve color logging

* Simplify color sorting

* Bump deps

* Fix color sync logic

* Switch to sort params: ascending, descending

* Fix commandk sort menu

* Update tr-tr sorting language

* Add color capture to all photo extractions

* Add color visualization to photo form

* Standardize photo update language

* Create global debug color update function

* Improve color data capture logging

* Update maximum function duration for admin photos

* Add note to remove maxDuration

* Use AI to generate sorting color

* Conditionally use AI to analyze colors

* Manage AI color analysis batched requests

* Fix color reporting in admin photo table

* Only update color where AI fields are missing

* Temporarily upgrade admin/photos timeout

* Fix pro-based max duration

* Standardize color sorting foundations

* Update color sorting language

* Refactor color calculations

* Restore max duration time

* Update color-based sort menu labels

* Finalize color documentation

* Clean up color test actions

* Round color sort values before submitting to db

* Consolidate color server actions
2025-08-03 19:31:02 -05:00

131 lines
4.5 KiB
TypeScript

'use client';
import { clsx } from 'clsx/lite';
import AppGrid from '@/components/AppGrid';
import AdminPhotosTable from '@/admin/AdminPhotosTable';
import AdminPhotosTableInfinite from '@/admin/AdminPhotosTableInfinite';
import PathLoaderButton from '@/components/primitives/PathLoaderButton';
import { PATH_ADMIN_PHOTOS_UPDATES } from '@/app/path';
import { Photo } from '@/photo';
import { StorageListResponse } from '@/platforms/storage';
import AdminUploadsTable from './AdminUploadsTable';
import { Timezone } from '@/utility/timezone';
import { useAppState } from '@/app/AppState';
import PhotoUploadWithStatus from '@/photo/PhotoUploadWithStatus';
import { pluralize } from '@/utility/string';
import IconBroom from '@/components/icons/IconBroom';
import ResponsiveText from '@/components/primitives/ResponsiveText';
import { useAppText } from '@/i18n/state/client';
import SyncColorButton from '@/photo/color/SyncColorButton';
export default function AdminPhotosClient({
photos,
photosCount,
photosCountNeedsSync,
blobPhotoUrls,
shouldResize,
hasAiTextGeneration,
onLastUpload,
infiniteScrollInitial,
infiniteScrollMultiple,
timezone,
debugColorData,
}: {
photos: Photo[]
photosCount: number
photosCountNeedsSync: number
blobPhotoUrls: StorageListResponse
shouldResize: boolean
hasAiTextGeneration: boolean
onLastUpload: () => Promise<void>
infiniteScrollInitial: number
infiniteScrollMultiple: number
timezone: Timezone
debugColorData?: boolean
}) {
const { uploadState: { isUploading } } = useAppState();
const appText = useAppText();
return (
<AppGrid
contentMain={
<div className="space-y-4">
<div className="flex gap-4">
<div className="grow min-w-0">
<PhotoUploadWithStatus
inputId="admin-photos"
shouldResize={shouldResize}
onLastUpload={onLastUpload}
/>
</div>
{debugColorData &&
<SyncColorButton />}
{photosCountNeedsSync > 0 &&
<PathLoaderButton
path={PATH_ADMIN_PHOTOS_UPDATES}
icon={<IconBroom
size={18}
className="translate-x-[-1px]"
/>}
tooltip={(
pluralize(
photosCountNeedsSync,
appText.photo.photo,
appText.photo.photoPlural.toLocaleLowerCase(),
) +
' missing data or AI-generated text'
)}
className={clsx(
'text-blue-600 dark:text-blue-400',
'border border-blue-200 dark:border-blue-800/60',
'active:bg-blue-50 dark:active:bg-blue-950/50',
'disabled:bg-blue-50 dark:disabled:bg-blue-950/50',
isUploading && 'hidden md:inline-flex',
)}
spinnerColor="text"
spinnerClassName="text-blue-200 dark:text-blue-600/40"
hideText="never"
>
<ResponsiveText shortText={photosCountNeedsSync}>
{pluralize(
photosCountNeedsSync,
appText.admin.update,
appText.admin.updatePlural,
)}
</ResponsiveText>
</PathLoaderButton>}
</div>
{blobPhotoUrls.length > 0 &&
<div className={clsx(
'border-b pb-6',
'border-gray-200 dark:border-gray-700',
'space-y-4',
)}>
<div className="font-bold">
Photo Blobs ({blobPhotoUrls.length})
</div>
<AdminUploadsTable urlAddStatuses={blobPhotoUrls} />
</div>}
{/* Use custom spacing to address gap/space-y compatibility quirks */}
<div className="space-y-[6px] sm:space-y-[10px]">
<AdminPhotosTable
photos={photos}
hasAiTextGeneration={hasAiTextGeneration}
timezone={timezone}
debugColorData={debugColorData}
/>
{photosCount > photos.length &&
<AdminPhotosTableInfinite
initialOffset={infiniteScrollInitial}
itemsPerPage={infiniteScrollMultiple}
hasAiTextGeneration={hasAiTextGeneration}
timezone={timezone}
debugColorData={debugColorData}
/>}
</div>
</div>}
/>
);
}