Add photo sync to admin menu

This commit is contained in:
Sam Becker 2025-04-20 20:44:38 -05:00
parent f4c1ebd642
commit 0309f66b10
7 changed files with 62 additions and 39 deletions

View File

@ -5,6 +5,7 @@ import {
PATH_ADMIN_CONFIGURATION,
PATH_ADMIN_INSIGHTS,
PATH_ADMIN_PHOTOS,
PATH_ADMIN_PHOTOS_SYNC,
PATH_ADMIN_RECIPES,
PATH_ADMIN_TAGS,
PATH_ADMIN_UPLOADS,
@ -26,6 +27,7 @@ import IconSignOut from '@/components/icons/IconSignOut';
import IconLock from '@/components/icons/IconLock';
import { IoMdCheckboxOutline } from 'react-icons/io';
import Spinner from '@/components/Spinner';
import IconBroom from '@/components/icons/IconBroom';
export default function AdminAppMenu({
active,
@ -38,6 +40,7 @@ export default function AdminAppMenu({
}) {
const {
photosCountTotal = 0,
photosCountNeedSync = 0,
uploadsCount = 0,
tagsCount = 0,
recipesCount = 0,
@ -71,7 +74,7 @@ export default function AdminAppMenu({
annotation: `${uploadsCount}`,
icon: <IconFolder
size={16}
className="translate-y-[0.5px]"
className="translate-x-[1px] translate-y-[0.5px]"
/>,
href: PATH_ADMIN_UPLOADS,
});
@ -121,7 +124,10 @@ export default function AdminAppMenu({
size={18}
className="translate-x-[-1px] translate-y-[0.5px]"
/>
: <IoMdCheckboxOutline size={17} className="translate-x-[-0.5px]" />,
: <IoMdCheckboxOutline
size={16}
className="translate-x-[-0.5px]"
/>,
href: PATH_GRID_INFERRED,
action: () => {
if (isSelecting) {
@ -136,6 +142,17 @@ export default function AdminAppMenu({
shouldPreventDefault: false,
});
}
if (photosCountNeedSync) {
items.push({
label: 'To Sync',
annotation: `${photosCountNeedSync}`,
icon: <IconBroom
size={17}
className="translate-y-[0.5px]"
/>,
href: PATH_ADMIN_PHOTOS_SYNC,
});
}
items.push({
label: showAppInsightsLink

View File

@ -61,7 +61,7 @@ export default function AdminPhotosClient({
/>}
tooltip={(
pluralize(photosCountNeedsSync, 'photo') +
' needs sync'
' missing data or AI-generated text'
)}
className={clsx(
'text-blue-600 dark:text-blue-400',

View File

@ -7,12 +7,16 @@ import { testDatabaseConnection } from '@/platforms/postgres';
import { testStorageConnection } from '@/platforms/storage';
import { APP_CONFIGURATION } from '@/app/config';
import { getStorageUploadUrlsNoStore } from '@/platforms/storage/cache';
import { getInsightsIndicatorStatus } from '@/admin/insights/server';
import {
getPhotosMeta,
getUniqueTags,
getUniqueRecipes,
getPhotosInNeedOfSyncCount,
} from '@/photo/db/query';
import {
getGitHubMetaForCurrentApp,
indicatorStatusForSignificantInsights,
} from './insights';
export type AdminData = Awaited<ReturnType<typeof getAdminDataAction>>;
@ -21,10 +25,11 @@ export const getAdminDataAction = async () =>
const [
photosCount,
photosCountHidden,
photosCountNeedSync,
codeMeta,
uploadsCount,
tagsCount,
recipesCount,
insightsIndicatorStatus,
] = await Promise.all([
getPhotosMeta()
.then(({ count }) => count)
@ -32,6 +37,8 @@ export const getAdminDataAction = async () =>
getPhotosMeta({ hidden: 'only' })
.then(({ count }) => count)
.catch(() => 0),
getPhotosInNeedOfSyncCount(),
getGitHubMetaForCurrentApp(),
getStorageUploadUrlsNoStore()
.then(urls => urls.length)
.catch(e => {
@ -44,9 +51,13 @@ export const getAdminDataAction = async () =>
getUniqueRecipes()
.then(recipes => recipes.length)
.catch(() => 0),
getInsightsIndicatorStatus(),
]);
const insightsIndicatorStatus = indicatorStatusForSignificantInsights({
codeMeta,
photosCountNeedSync,
});
const photosCountTotal = (
photosCount !== undefined &&
photosCountHidden !== undefined
@ -57,12 +68,13 @@ export const getAdminDataAction = async () =>
return {
photosCount,
photosCountHidden,
photosCountNeedSync,
photosCountTotal,
uploadsCount,
tagsCount,
recipesCount,
insightsIndicatorStatus,
};
} as const;
});
const scanForError = (

View File

@ -46,6 +46,7 @@ import IconFocalLength from '@/components/icons/IconFocalLength';
import IconTag from '@/components/icons/IconTag';
import IconPhoto from '@/components/icons/IconPhoto';
import { HiOutlineDocumentText } from 'react-icons/hi';
import { ReactNode } from 'react';
const DEBUG_COMMIT_SHA = '4cd29ed';
const DEBUG_COMMIT_MESSAGE = 'Long commit message for debugging purposes';
@ -131,6 +132,13 @@ export default function AdminAppInsightsClient({
{codeMeta?.branch ?? TEMPLATE_REPO_BRANCH}
</a>;
const renderTooltipContent = (content: ReactNode) =>
<Tooltip
content={content}
classNameTrigger="translate-y-[-1.5px] ml-2 h-3"
supportMobile
/>;
return (
<ScoreCardContainer>
{(codeMeta || debug) && <>
@ -143,11 +151,9 @@ export default function AdminAppInsightsClient({
/>}
content={<>
<span>Could not analyze source code</span>
<Tooltip
content="Could not connect to GitHub API. Try refreshing."
classNameTrigger="translate-y-[-1.5px] ml-2 h-3"
supportMobile
/>
{renderTooltipContent(
'Could not connect to GitHub API. Try refreshing.',
)}
</>}
/>}
{((!codeMeta?.didError && noFork) || debug) &&
@ -435,6 +441,9 @@ export default function AdminAppInsightsClient({
)}
{' '}
to sync
{renderTooltipContent(<>
Missing data or AI&#8209;generated text
</>)}
</>}
expandPath={PATH_ADMIN_PHOTOS_SYNC}
/>}

View File

@ -99,9 +99,17 @@ export const getSignificantInsights = ({
};
};
export const indicatorStatusForSignificantInsights = (
insights: Awaited<ReturnType<typeof getSignificantInsights>>,
) => {
export const indicatorStatusForSignificantInsights = ({
codeMeta,
photosCountNeedSync,
}: Parameters<typeof getSignificantInsights>[0] & {
photosCountNeedSync: number
}) => {
const insights = getSignificantInsights({
codeMeta,
photosCountNeedSync,
});
const {
forkBehind,
noAiRateLimiting,

View File

@ -1,23 +0,0 @@
import { getPhotosInNeedOfSyncCount } from '@/photo/db/query';
import {
getSignificantInsights,
indicatorStatusForSignificantInsights,
} from '.';
import { getGitHubMetaForCurrentApp } from '.';
export const getInsightsIndicatorStatus = async () => {
const [
codeMeta,
photosCountNeedSync,
] = await Promise.all([
getGitHubMetaForCurrentApp(),
getPhotosInNeedOfSyncCount(),
]);
const significantInsights = getSignificantInsights({
codeMeta,
photosCountNeedSync,
});
return indicatorStatusForSignificantInsights(significantInsights);
};

View File

@ -151,7 +151,7 @@ export default function AppStateProvider({
if (userEmail) {
storeAuthEmailCookie(userEmail);
}
}, [userEmail, adminData]);
}, [userEmail]);
const registerAdminUpdate = useCallback(() =>
setAdminUpdateTimes(updates => [...updates, new Date()])