'use client'; import ScoreCard from '@/components/ScoreCard'; import ScoreCardRow from '@/components/ScoreCardRow'; import { formattedDateRangeForPhotos } from '@/photo'; import { FaArrowRight, FaCircleInfo, FaRegCalendar } from 'react-icons/fa6'; import { MdAspectRatio } from 'react-icons/md'; import { PiWarningBold } from 'react-icons/pi'; import { TbSparkles } from 'react-icons/tb'; import { BiGitBranch, BiGitCommit, BiLogoGithub } from 'react-icons/bi'; import { TEMPLATE_REPO_BRANCH, TEMPLATE_REPO_OWNER, TEMPLATE_REPO_NAME, VERCEL_GIT_COMMIT_SHA_SHORT, VERCEL_GIT_COMMIT_MESSAGE, TEMPLATE_REPO_URL_FORK, TEMPLATE_REPO_URL_README, CATEGORY_VISIBILITY, USED_DEPRECATED_ENV_VARS, } from '@/app/config'; import { getAllInsights, getGitHubMetaForCurrentApp, hasTemplateRecommendations, PhotoStats, } from '.'; import EnvVar from '@/components/EnvVar'; import { IoCheckmarkCircleOutline, IoSyncCircle } from 'react-icons/io5'; import clsx from 'clsx/lite'; import { PATH_ADMIN_PHOTOS_UPDATES } from '@/app/path'; import { LiaBroomSolid } from 'react-icons/lia'; import { IoMdGrid } from 'react-icons/io'; import { RiSpeedMiniLine } from 'react-icons/ri'; import AdminLink from '../AdminLink'; import AdminEmptyState from '../AdminEmptyState'; import { pluralize } from '@/utility/string'; import Tooltip from '@/components/Tooltip'; import { useAppState } from '@/app/AppState'; import ScoreCardContainer from '@/components/ScoreCardContainer'; import IconLens from '@/components/icons/IconLens'; import IconCamera from '@/components/icons/IconCamera'; import IconRecipe from '@/components/icons/IconRecipe'; import IconFilm from '@/components/icons/IconFilm'; 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'; import MaskedScroll from '@/components/MaskedScroll'; import IconNext from '@/components/icons/IconNext'; import Link from 'next/link'; const DEBUG_COMMIT_SHA = '4cd29ed'; const DEBUG_COMMIT_MESSAGE = 'Long commit message for debugging purposes'; const DEBUG_BEHIND_BY = 9; const DEBUG_PHOTOS_NEED_SYNC_COUNT = 7; const TEXT_COLOR_WARNING = 'text-amber-600 dark:text-amber-500'; const TEXT_COLOR_BLUE = 'text-blue-600 dark:text-blue-500'; const readmeAnchor = (anchor: string) => README/{anchor} ; const renderLabeledEnvVar = ( label: string, variable: string, value?: string, icon?: ReactNode, ) =>
{label} {icon ?
{icon}
:}
; const renderHighlightText = ( text: string, color: 'blue' | 'yellow' = 'blue', truncate = true, ) => {text} ; const renderWarningIconLarge = ; const renderWarningIconSmall = ; export default function AdminAppInsightsClient({ codeMeta, nextVersion, insights, usedDeprecatedEnvVars, photoStats: { photosCount, photosCountHidden, photosCountNeedSync, camerasCount, lensesCount, tagsCount, recipesCount, filmsCount, focalLengthsCount, dateRange, }, }: { codeMeta?: Awaited> nextVersion: string insights: ReturnType usedDeprecatedEnvVars: typeof USED_DEPRECATED_ENV_VARS photoStats: PhotoStats }) { const { shouldDebugInsights: debug } = useAppState(); const { deprecatedEnvVars, noFork, forkBehind, noAi, noRateLimiting, noConfiguredDomain, noConfiguredMetaTitle, noConfiguredMetaDescription, photosNeedSync, photoMatting, gridFirst, noStaticOptimization, } = insights; const { descriptionWithSpaces } = formattedDateRangeForPhotos(undefined, dateRange); const branchLink = {codeMeta?.branch ?? TEMPLATE_REPO_BRANCH} ; const renderTooltipContent = (content: ReactNode) => ; return ( {(codeMeta || debug) && <> {(codeMeta?.didError || debug) && } content={<> Could not analyze source code {renderTooltipContent( 'Could not connect to GitHub API. Try refreshing.', )} } />} {((!codeMeta?.didError && noFork) || debug) && } content="This template is not forked" expandContent={<> Fork original template {' '} to receive the latest fixes and features. {' '} Additional instructions in {' '} {readmeAnchor('receiving-updates')}. } />} {((!codeMeta?.didError && forkBehind) || debug) && } content={<> This fork is {' '} {renderHighlightText( pluralize(codeMeta?.behindBy ?? DEBUG_BEHIND_BY, 'commit'), 'blue', )} {' '} behind } expandContent={<> Sync your fork {' '} to receive the latest fixes and features. } />} } content={} /> } content={branchLink} /> } content={ {VERCEL_GIT_COMMIT_SHA_SHORT ?? DEBUG_COMMIT_SHA} {VERCEL_GIT_COMMIT_MESSAGE ?? DEBUG_COMMIT_MESSAGE} } /> } content={ Next.js {nextVersion} } /> } {(hasTemplateRecommendations(insights) || debug) ? <> {(deprecatedEnvVars || debug) && renderHighlightText( 'Update environment variables', 'yellow', !isExpanded, )} expandContent={
Future versions of this template may not build correctly with the following deprecated environment variables:
{usedDeprecatedEnvVars.map(({ old, replacement }) => (
{renderWarningIconSmall} {old}
))}
} />} {(noRateLimiting || debug) && renderHighlightText( 'Enable rate limiting', 'yellow', !isExpanded, )} expandContent={<> Create Upstash Redis store from storage tab on Vercel dashboard and link to this project to prevent unexpected usage by enabling rate limiting. } />} {(noConfiguredDomain || debug) && renderHighlightText( 'Configure domain', 'yellow', !isExpanded, )} expandContent={<> Not setting an explicit domain may cause certain features to behave unexpectedly. Domains are stored in {' '} } />} {( noConfiguredMetaTitle || noConfiguredMetaDescription || debug ) && } content="Configure meta" expandContent={<> Configure site title (visible in search results and browser tab) and site description (visible in search results): {' '}
{( noConfiguredMetaTitle || debug ) && renderLabeledEnvVar( 'Site title', 'NEXT_PUBLIC_META_TITLE', )} {( noConfiguredMetaDescription || debug ) && renderLabeledEnvVar( 'Site description', 'NEXT_PUBLIC_META_DESCRIPTION', )}
} />} {(noStaticOptimization || debug) && } content="Speed up page load times" expandContent={<> Improve load times by enabling static optimization:
{renderLabeledEnvVar( 'Photo pages', 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS', '1', )} {renderLabeledEnvVar( 'Photo OG images', 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES', '1', )} {renderLabeledEnvVar( 'Category pages (tags, cameras, etc.)', 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES', '1', )} {renderLabeledEnvVar( 'Category OG images', 'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORY_OG_IMAGES', '1', )} See {readmeAnchor('performance')} for cost implications.
} />} {(noAi || debug) && } content="Improve SEO + accessibility with AI" expandContent={<> Enable automatic AI text generation {' '} by setting {' '} Further instruction and cost considerations in {' '} {readmeAnchor('ai-text-generation')}. } />} {(photoMatting || debug) && } content="Vertical photos may benefit from matting" expandContent={<> Enable photo matting to make {' '} portrait and landscape photos appear more consistent {' '} } />} {(gridFirst || debug) && } content="Grid homepage" expandContent={<> Now that you have enough photos, consider switching your {' '} default view to grid by setting {' '} } />} : } includeContainer={false} > No recommendations found }
{(photosNeedSync || debug) && } content={<> {renderHighlightText( pluralize( photosCountNeedSync || DEBUG_PHOTOS_NEED_SYNC_COUNT, 'photo', ), 'blue', )} {' '} with updates {renderTooltipContent(<> Missing data or AI‑generated text )} } expandPath={PATH_ADMIN_PHOTOS_UPDATES} />} } content={<> {pluralize(photosCount, 'photo')} {photosCountHidden > 0 && ` (${photosCountHidden} hidden)`} } /> {CATEGORY_VISIBILITY.map(category => { switch (category) { case 'cameras': return } content={pluralize(camerasCount, 'camera')} />; case 'lenses': return } content={pluralize(lensesCount, 'lens', 'lenses')} />; case 'tags': return } content={pluralize(tagsCount, 'tag')} />; case 'recipes': return recipesCount > 0 ? } content={pluralize(recipesCount, 'recipe')} /> : null; case 'films': return filmsCount > 0 ? } content={pluralize(filmsCount, 'film')} /> : null; case 'focal-lengths': return } content={pluralize(focalLengthsCount, 'focal length')} />; } })} {descriptionWithSpaces && } content={descriptionWithSpaces} />}
); }