Make Checklist a special case of ScoreCard
This commit is contained in:
parent
1fa3eaccc3
commit
de8bce1bee
21
app/admin/components/page.tsx
Normal file
21
app/admin/components/page.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import SiteGrid from '@/components/SiteGrid';
|
||||||
|
import StatusIcon from '@/components/StatusIcon';
|
||||||
|
import clsx from 'clsx/lite';
|
||||||
|
|
||||||
|
export default function ComponentsPage() {
|
||||||
|
return (
|
||||||
|
<SiteGrid
|
||||||
|
contentMain={<div className={clsx(
|
||||||
|
'flex gap-0.5',
|
||||||
|
'*:inline-flex *:bg-medium',
|
||||||
|
)}>
|
||||||
|
<StatusIcon type="checked" />
|
||||||
|
<StatusIcon type="missing" />
|
||||||
|
<StatusIcon type="warning" />
|
||||||
|
<StatusIcon type="optional" />
|
||||||
|
</div>}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -114,12 +114,10 @@ export default function AdminAppConfigurationClient({
|
|||||||
const renderSubStatus = (
|
const renderSubStatus = (
|
||||||
type: ComponentProps<typeof StatusIcon>['type'],
|
type: ComponentProps<typeof StatusIcon>['type'],
|
||||||
label: ReactNode,
|
label: ReactNode,
|
||||||
iconClassName?: string,
|
iconClassName = 'translate-y-[3.5px]',
|
||||||
) =>
|
) =>
|
||||||
<div className="flex gap-2 translate-x-[-3px]">
|
<div className="flex gap-2 translate-x-[-2.5px]">
|
||||||
<span className={iconClassName}>
|
<StatusIcon {...{ type, className: iconClassName }} />
|
||||||
<StatusIcon {...{ type }} />
|
|
||||||
</span>
|
|
||||||
<span className="min-w-0">
|
<span className="min-w-0">
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
@ -132,7 +130,7 @@ export default function AdminAppConfigurationClient({
|
|||||||
renderSubStatus(
|
renderSubStatus(
|
||||||
type,
|
type,
|
||||||
renderEnvVars([variable]),
|
renderEnvVars([variable]),
|
||||||
'translate-y-[3px]',
|
'translate-y-[4.5px]',
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderError = ({
|
const renderError = ({
|
||||||
@ -165,498 +163,496 @@ export default function AdminAppConfigurationClient({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="space-y-3 -mt-3">
|
<ChecklistGroup
|
||||||
<ChecklistGroup
|
title="Storage"
|
||||||
title="Storage"
|
icon={<BiData size={16} />}
|
||||||
icon={<BiData size={16} />}
|
>
|
||||||
|
<ChecklistRow
|
||||||
|
title={hasDatabase && isAnalyzingConfiguration
|
||||||
|
? 'Testing database connection'
|
||||||
|
: 'Setup database'}
|
||||||
|
status={hasDatabase}
|
||||||
|
isPending={hasDatabase && isAnalyzingConfiguration}
|
||||||
>
|
>
|
||||||
<ChecklistRow
|
{databaseError && renderError({
|
||||||
title={hasDatabase && isAnalyzingConfiguration
|
connection: { provider: 'Database', error: databaseError},
|
||||||
? 'Testing database connection'
|
})}
|
||||||
: 'Setup database'}
|
{hasVercelPostgres
|
||||||
status={hasDatabase}
|
? renderSubStatus('checked', 'Vercel Postgres: connected')
|
||||||
isPending={hasDatabase && isAnalyzingConfiguration}
|
: renderSubStatus('optional', <>
|
||||||
>
|
Vercel Postgres:
|
||||||
{databaseError && renderError({
|
|
||||||
connection: { provider: 'Database', error: databaseError},
|
|
||||||
})}
|
|
||||||
{hasVercelPostgres
|
|
||||||
? renderSubStatus('checked', 'Vercel Postgres: connected')
|
|
||||||
: renderSubStatus('optional', <>
|
|
||||||
Vercel Postgres:
|
|
||||||
{' '}
|
|
||||||
<AdminLink
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
href="https://vercel.com/docs/storage/vercel-postgres/quickstart#create-a-postgres-database"
|
|
||||||
externalIcon
|
|
||||||
>
|
|
||||||
create store
|
|
||||||
</AdminLink>
|
|
||||||
{' '}
|
|
||||||
and connect to project
|
|
||||||
</>)}
|
|
||||||
{hasDatabase && !hasVercelPostgres &&
|
|
||||||
renderSubStatus('checked', <>
|
|
||||||
Postgres-compatible: connected
|
|
||||||
{' '}
|
|
||||||
(SSL {isPostgresSslEnabled ? 'enabled' : 'disabled'})
|
|
||||||
</>)}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title={
|
|
||||||
hasStorageProvider && isAnalyzingConfiguration
|
|
||||||
? 'Testing storage connection'
|
|
||||||
: !hasStorageProvider
|
|
||||||
? 'Setup storage (one of the following)'
|
|
||||||
: hasMultipleStorageProviders
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
? `Setup storage (new uploads go to: ${labelForStorage(currentStorage)})`
|
|
||||||
: 'Setup storage'}
|
|
||||||
status={hasStorageProvider}
|
|
||||||
isPending={hasStorageProvider && isAnalyzingConfiguration}
|
|
||||||
>
|
|
||||||
{storageError && renderError({
|
|
||||||
connection: { provider: 'Storage', error: storageError},
|
|
||||||
})}
|
|
||||||
{hasVercelBlobStorage
|
|
||||||
? renderSubStatus('checked', 'Vercel Blob: connected')
|
|
||||||
: renderSubStatus('optional', <>
|
|
||||||
{labelForStorage('vercel-blob')}:
|
|
||||||
{' '}
|
|
||||||
<AdminLink
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
href="https://vercel.com/docs/storage/vercel-blob/quickstart#create-a-blob-store"
|
|
||||||
externalIcon
|
|
||||||
>
|
|
||||||
create store
|
|
||||||
</AdminLink>
|
|
||||||
{' '}
|
|
||||||
and connect to project
|
|
||||||
</>,
|
|
||||||
)}
|
|
||||||
{hasCloudflareR2Storage
|
|
||||||
? renderSubStatus('checked', 'Cloudflare R2: connected')
|
|
||||||
: renderSubStatus('optional', <>
|
|
||||||
{labelForStorage('cloudflare-r2')}:
|
|
||||||
{' '}
|
|
||||||
<AdminLink
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
href="https://github.com/sambecker/exif-photo-blog#cloudflare-r2"
|
|
||||||
externalIcon
|
|
||||||
>
|
|
||||||
create/configure bucket
|
|
||||||
</AdminLink>
|
|
||||||
</>)}
|
|
||||||
{hasAwsS3Storage
|
|
||||||
? renderSubStatus('checked', 'AWS S3: connected')
|
|
||||||
: renderSubStatus('optional', <>
|
|
||||||
{labelForStorage('aws-s3')}:
|
|
||||||
{' '}
|
|
||||||
<AdminLink
|
|
||||||
href="https://github.com/sambecker/exif-photo-blog#aws-s3"
|
|
||||||
externalIcon
|
|
||||||
>
|
|
||||||
create/configure bucket
|
|
||||||
</AdminLink>
|
|
||||||
</>)}
|
|
||||||
</ChecklistRow>
|
|
||||||
</ChecklistGroup>
|
|
||||||
<ChecklistGroup
|
|
||||||
title="Authentication"
|
|
||||||
icon={<BiLockAlt size={16} />}
|
|
||||||
>
|
|
||||||
<ChecklistRow
|
|
||||||
title={!hasAuthSecret && isAnalyzingConfiguration
|
|
||||||
? 'Generating secret'
|
|
||||||
: 'Setup auth'}
|
|
||||||
status={hasAuthSecret}
|
|
||||||
isPending={!hasAuthSecret && isAnalyzingConfiguration}
|
|
||||||
>
|
|
||||||
Store auth secret in environment variable:
|
|
||||||
{!hasAuthSecret &&
|
|
||||||
<div className="overflow-x-auto">
|
|
||||||
<SecretGenerator />
|
|
||||||
</div>}
|
|
||||||
{renderEnvVars(['AUTH_SECRET'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Setup admin user"
|
|
||||||
status={hasAdminUser}
|
|
||||||
>
|
|
||||||
Store admin email/password
|
|
||||||
{' '}
|
|
||||||
in environment variables:
|
|
||||||
{renderEnvVars([
|
|
||||||
'ADMIN_EMAIL',
|
|
||||||
'ADMIN_PASSWORD',
|
|
||||||
])}
|
|
||||||
</ChecklistRow>
|
|
||||||
</ChecklistGroup>
|
|
||||||
<ChecklistGroup
|
|
||||||
title="Content"
|
|
||||||
icon={<BiPencil size={16} />}
|
|
||||||
>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Configure domain"
|
|
||||||
status={hasDomain}
|
|
||||||
showWarning
|
|
||||||
>
|
|
||||||
{!hasDomain &&
|
|
||||||
renderWarning({message:
|
|
||||||
'Not explicitly setting a domain may cause ' +
|
|
||||||
'certain features to behave unexpectedly',
|
|
||||||
})}
|
|
||||||
Store in environment variable (seen in top-right nav):
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_SITE_DOMAIN'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Add title"
|
|
||||||
status={hasTitle}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Store in environment variable (seen in browser tab):
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_SITE_TITLE'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Add description"
|
|
||||||
status={hasDescription}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Store in environment variable (seen in nav, under title):
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_SITE_DESCRIPTION'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Add about"
|
|
||||||
status={hasAbout}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Store in environment variable (seen in grid sidebar):
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_SITE_ABOUT'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
</ChecklistGroup>
|
|
||||||
{!simplifiedView && <>
|
|
||||||
<ChecklistGroup
|
|
||||||
title="AI text generation"
|
|
||||||
titleShort="AI"
|
|
||||||
icon={<HiSparkles />}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
<ChecklistRow
|
|
||||||
title={isAiTextGenerationEnabled && isAnalyzingConfiguration
|
|
||||||
? 'Testing OpenAI connection'
|
|
||||||
: 'Add OpenAI secret key'}
|
|
||||||
status={isAiTextGenerationEnabled}
|
|
||||||
isPending={isAiTextGenerationEnabled && isAnalyzingConfiguration}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
{aiError && renderError({
|
|
||||||
connection: { provider: 'OpenAI', error: aiError},
|
|
||||||
})}
|
|
||||||
Store your OpenAI secret key in order to add experimental support
|
|
||||||
for AI-generated text descriptions and enable an invisible field
|
|
||||||
called {'"Semantic Description"'} used to support CMD-K search:
|
|
||||||
{renderEnvVars(['OPENAI_SECRET_KEY'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title={hasRedisStorage && isAnalyzingConfiguration
|
|
||||||
? 'Testing Redis connection'
|
|
||||||
: 'Enable rate limiting'}
|
|
||||||
status={hasRedisStorage}
|
|
||||||
isPending={hasRedisStorage && isAnalyzingConfiguration}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
{redisError && renderError({
|
|
||||||
connection: { provider: 'Redis', error: redisError},
|
|
||||||
})}
|
|
||||||
Create Upstash Redis store from storage tab
|
|
||||||
on Vercel dashboard and connect to this project
|
|
||||||
to enable rate limiting
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
// eslint-disable-next-line max-len
|
|
||||||
title={`Auto-generated fields: ${aiTextAutoGeneratedFields.join(', ')}`}
|
|
||||||
status={hasAiTextAutoGeneratedFields}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Comma-separated fields to auto-generate when
|
|
||||||
uploading photos. Accepted values: title, caption,
|
|
||||||
tags, description, all, or none
|
|
||||||
{' '}
|
{' '}
|
||||||
(default: {'"title, tags, semantic"'}):
|
<AdminLink
|
||||||
{renderEnvVars(['AI_TEXT_AUTO_GENERATED_FIELDS'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
</ChecklistGroup>
|
|
||||||
<ChecklistGroup
|
|
||||||
title="Performance"
|
|
||||||
icon={<RiSpeedMiniLine size={18} />}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Static optimization"
|
|
||||||
status={isStaticallyOptimized}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to make site more responsive
|
|
||||||
by enabling static optimization
|
|
||||||
(i.e., rendering pages and images at build time):
|
|
||||||
{renderSubStatusWithEnvVar(
|
|
||||||
arePhotosStaticallyOptimized ? 'checked' : 'optional',
|
|
||||||
'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS',
|
|
||||||
)}
|
|
||||||
{renderSubStatusWithEnvVar(
|
|
||||||
arePhotoOGImagesStaticallyOptimized ? 'checked' : 'optional',
|
|
||||||
'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES',
|
|
||||||
)}
|
|
||||||
{renderSubStatusWithEnvVar(
|
|
||||||
arePhotoCategoriesStaticallyOptimized ? 'checked' : 'optional',
|
|
||||||
'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES',
|
|
||||||
)}
|
|
||||||
{renderSubStatusWithEnvVar(
|
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
arePhotoCategoryOgImagesStaticallyOptimized ? 'checked' : 'optional',
|
href="https://vercel.com/docs/storage/vercel-postgres/quickstart#create-a-postgres-database"
|
||||||
'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORY_OG_IMAGES',
|
externalIcon
|
||||||
)}
|
>
|
||||||
|
create store
|
||||||
|
</AdminLink>
|
||||||
|
{' '}
|
||||||
|
and connect to project
|
||||||
|
</>)}
|
||||||
|
{hasDatabase && !hasVercelPostgres &&
|
||||||
|
renderSubStatus('checked', <>
|
||||||
|
Postgres-compatible: connected
|
||||||
|
{' '}
|
||||||
|
(SSL {isPostgresSslEnabled ? 'enabled' : 'disabled'})
|
||||||
|
</>)}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title={
|
||||||
|
hasStorageProvider && isAnalyzingConfiguration
|
||||||
|
? 'Testing storage connection'
|
||||||
|
: !hasStorageProvider
|
||||||
|
? 'Setup storage (one of the following)'
|
||||||
|
: hasMultipleStorageProviders
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
? `Setup storage (new uploads go to: ${labelForStorage(currentStorage)})`
|
||||||
|
: 'Setup storage'}
|
||||||
|
status={hasStorageProvider}
|
||||||
|
isPending={hasStorageProvider && isAnalyzingConfiguration}
|
||||||
|
>
|
||||||
|
{storageError && renderError({
|
||||||
|
connection: { provider: 'Storage', error: storageError},
|
||||||
|
})}
|
||||||
|
{hasVercelBlobStorage
|
||||||
|
? renderSubStatus('checked', 'Vercel Blob: connected')
|
||||||
|
: renderSubStatus('optional', <>
|
||||||
|
{labelForStorage('vercel-blob')}:
|
||||||
|
{' '}
|
||||||
|
<AdminLink
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
href="https://vercel.com/docs/storage/vercel-blob/quickstart#create-a-blob-store"
|
||||||
|
externalIcon
|
||||||
|
>
|
||||||
|
create store
|
||||||
|
</AdminLink>
|
||||||
|
{' '}
|
||||||
|
and connect to project
|
||||||
|
</>,
|
||||||
|
)}
|
||||||
|
{hasCloudflareR2Storage
|
||||||
|
? renderSubStatus('checked', 'Cloudflare R2: connected')
|
||||||
|
: renderSubStatus('optional', <>
|
||||||
|
{labelForStorage('cloudflare-r2')}:
|
||||||
|
{' '}
|
||||||
|
<AdminLink
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
href="https://github.com/sambecker/exif-photo-blog#cloudflare-r2"
|
||||||
|
externalIcon
|
||||||
|
>
|
||||||
|
create/configure bucket
|
||||||
|
</AdminLink>
|
||||||
|
</>)}
|
||||||
|
{hasAwsS3Storage
|
||||||
|
? renderSubStatus('checked', 'AWS S3: connected')
|
||||||
|
: renderSubStatus('optional', <>
|
||||||
|
{labelForStorage('aws-s3')}:
|
||||||
|
{' '}
|
||||||
|
<AdminLink
|
||||||
|
href="https://github.com/sambecker/exif-photo-blog#aws-s3"
|
||||||
|
externalIcon
|
||||||
|
>
|
||||||
|
create/configure bucket
|
||||||
|
</AdminLink>
|
||||||
|
</>)}
|
||||||
|
</ChecklistRow>
|
||||||
|
</ChecklistGroup>
|
||||||
|
<ChecklistGroup
|
||||||
|
title="Authentication"
|
||||||
|
icon={<BiLockAlt size={16} />}
|
||||||
|
>
|
||||||
|
<ChecklistRow
|
||||||
|
title={!hasAuthSecret && isAnalyzingConfiguration
|
||||||
|
? 'Generating secret'
|
||||||
|
: 'Setup auth'}
|
||||||
|
status={hasAuthSecret}
|
||||||
|
isPending={!hasAuthSecret && isAnalyzingConfiguration}
|
||||||
|
>
|
||||||
|
Store auth secret in environment variable:
|
||||||
|
{!hasAuthSecret &&
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<SecretGenerator />
|
||||||
|
</div>}
|
||||||
|
{renderEnvVars(['AUTH_SECRET'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Setup admin user"
|
||||||
|
status={hasAdminUser}
|
||||||
|
>
|
||||||
|
Store admin email/password
|
||||||
|
{' '}
|
||||||
|
in environment variables:
|
||||||
|
{renderEnvVars([
|
||||||
|
'ADMIN_EMAIL',
|
||||||
|
'ADMIN_PASSWORD',
|
||||||
|
])}
|
||||||
|
</ChecklistRow>
|
||||||
|
</ChecklistGroup>
|
||||||
|
<ChecklistGroup
|
||||||
|
title="Content"
|
||||||
|
icon={<BiPencil size={16} />}
|
||||||
|
>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Configure domain"
|
||||||
|
status={hasDomain}
|
||||||
|
showWarning
|
||||||
|
>
|
||||||
|
{!hasDomain &&
|
||||||
|
renderWarning({message:
|
||||||
|
'Not explicitly setting a domain may cause ' +
|
||||||
|
'certain features to behave unexpectedly',
|
||||||
|
})}
|
||||||
|
Store in environment variable (seen in top-right nav):
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_SITE_DOMAIN'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Add title"
|
||||||
|
status={hasTitle}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Store in environment variable (seen in browser tab):
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_SITE_TITLE'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Add description"
|
||||||
|
status={hasDescription}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Store in environment variable (seen in nav, under title):
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_SITE_DESCRIPTION'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Add about"
|
||||||
|
status={hasAbout}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Store in environment variable (seen in grid sidebar):
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_SITE_ABOUT'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
</ChecklistGroup>
|
||||||
|
{!simplifiedView && <>
|
||||||
|
<ChecklistGroup
|
||||||
|
title="AI text generation"
|
||||||
|
titleShort="AI"
|
||||||
|
icon={<HiSparkles />}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
<ChecklistRow
|
||||||
|
title={isAiTextGenerationEnabled && isAnalyzingConfiguration
|
||||||
|
? 'Testing OpenAI connection'
|
||||||
|
: 'Add OpenAI secret key'}
|
||||||
|
status={isAiTextGenerationEnabled}
|
||||||
|
isPending={isAiTextGenerationEnabled && isAnalyzingConfiguration}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
{aiError && renderError({
|
||||||
|
connection: { provider: 'OpenAI', error: aiError},
|
||||||
|
})}
|
||||||
|
Store your OpenAI secret key in order to add experimental support
|
||||||
|
for AI-generated text descriptions and enable an invisible field
|
||||||
|
called {'"Semantic Description"'} used to support CMD-K search:
|
||||||
|
{renderEnvVars(['OPENAI_SECRET_KEY'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title={hasRedisStorage && isAnalyzingConfiguration
|
||||||
|
? 'Testing Redis connection'
|
||||||
|
: 'Enable rate limiting'}
|
||||||
|
status={hasRedisStorage}
|
||||||
|
isPending={hasRedisStorage && isAnalyzingConfiguration}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
{redisError && renderError({
|
||||||
|
connection: { provider: 'Redis', error: redisError},
|
||||||
|
})}
|
||||||
|
Create Upstash Redis store from storage tab
|
||||||
|
on Vercel dashboard and connect to this project
|
||||||
|
to enable rate limiting
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
title={`Auto-generated fields: ${aiTextAutoGeneratedFields.join(', ')}`}
|
||||||
|
status={hasAiTextAutoGeneratedFields}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Comma-separated fields to auto-generate when
|
||||||
|
uploading photos. Accepted values: title, caption,
|
||||||
|
tags, description, all, or none
|
||||||
|
{' '}
|
||||||
|
(default: {'"title, tags, semantic"'}):
|
||||||
|
{renderEnvVars(['AI_TEXT_AUTO_GENERATED_FIELDS'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
</ChecklistGroup>
|
||||||
|
<ChecklistGroup
|
||||||
|
title="Performance"
|
||||||
|
icon={<RiSpeedMiniLine size={18} />}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Static optimization"
|
||||||
|
status={isStaticallyOptimized}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to make site more responsive
|
||||||
|
by enabling static optimization
|
||||||
|
(i.e., rendering pages and images at build time):
|
||||||
|
{renderSubStatusWithEnvVar(
|
||||||
|
arePhotosStaticallyOptimized ? 'checked' : 'optional',
|
||||||
|
'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS',
|
||||||
|
)}
|
||||||
|
{renderSubStatusWithEnvVar(
|
||||||
|
arePhotoOGImagesStaticallyOptimized ? 'checked' : 'optional',
|
||||||
|
'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES',
|
||||||
|
)}
|
||||||
|
{renderSubStatusWithEnvVar(
|
||||||
|
arePhotoCategoriesStaticallyOptimized ? 'checked' : 'optional',
|
||||||
|
'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES',
|
||||||
|
)}
|
||||||
|
{renderSubStatusWithEnvVar(
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
arePhotoCategoryOgImagesStaticallyOptimized ? 'checked' : 'optional',
|
||||||
|
'NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORY_OG_IMAGES',
|
||||||
|
)}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Preserve original uploads"
|
||||||
|
status={areOriginalUploadsPreserved}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to prevent
|
||||||
|
image uploads being compressed before storing:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title={`Image quality: ${imageQuality}`}
|
||||||
|
status={hasImageQuality}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable from {'"1-100"'}
|
||||||
|
{' '}
|
||||||
|
to control the quality of large photos
|
||||||
|
({'"100"'} represents highest quality/largest size):
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_IMAGE_QUALITY'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Image blur"
|
||||||
|
status={isBlurEnabled}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to prevent
|
||||||
|
image blur data being stored and displayed:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_BLUR_DISABLED'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
</ChecklistGroup>
|
||||||
|
<ChecklistGroup
|
||||||
|
title="Visual"
|
||||||
|
icon={<PiPaintBrushHousehold size={19} />}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
<ChecklistRow
|
||||||
|
title={`Default theme: ${defaultTheme}`}
|
||||||
|
status={hasDefaultTheme}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
{'Set environment variable to \'light\' or \'dark\''}
|
||||||
|
{' '}
|
||||||
|
to configure initial theme
|
||||||
|
{' '}
|
||||||
|
(defaults to {'\'system\''}):
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_DEFAULT_THEME'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Photo matting"
|
||||||
|
status={arePhotosMatted}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to constrain the size
|
||||||
|
{' '}
|
||||||
|
of each photo, and display a surrounding border:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_MATTE_PHOTOS'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
</ChecklistGroup>
|
||||||
|
<ChecklistGroup
|
||||||
|
title="Display"
|
||||||
|
icon={<BiHide size={18} />}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Show EXIF data"
|
||||||
|
status={showExifInfo}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to hide EXIF data:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_HIDE_EXIF_DATA'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Show zoom controls"
|
||||||
|
status={showZoomControls}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to hide
|
||||||
|
fullscreen photo zoom controls:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_HIDE_ZOOM_CONTROLS'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Show taken at time"
|
||||||
|
status={showTakenAtTimeHidden}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to hide
|
||||||
|
taken at time from photo meta:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_HIDE_TAKEN_AT_TIME'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Show social"
|
||||||
|
status={showSocial}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to hide
|
||||||
|
{' '}
|
||||||
|
X (formerly Twitter) button from share modal:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_HIDE_SOCIAL'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Show Fujifilm simulations"
|
||||||
|
status={showFilmSimulations}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to prevent
|
||||||
|
simulations showing up in /grid sidebar and
|
||||||
|
CMD-K results:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_HIDE_FILM_SIMULATIONS'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Show repo link"
|
||||||
|
status={showRepoLink}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to hide footer link:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_HIDE_REPO_LINK'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
</ChecklistGroup>
|
||||||
|
<ChecklistGroup
|
||||||
|
title="Grid"
|
||||||
|
icon={<IoMdGrid size={17} />}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Grid homepage"
|
||||||
|
status={isGridHomepageEnabled}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to show grid layout
|
||||||
|
on homepage:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_GRID_HOMEPAGE'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title={`Grid aspect ratio: ${gridAspectRatio}`}
|
||||||
|
status={hasGridAspectRatio}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to any number to enforce aspect ratio
|
||||||
|
{' '}
|
||||||
|
(default is {'"1"'}, i.e., square)—set to {'"0"'} to disable:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_GRID_ASPECT_RATIO'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title={`Grid density: ${hasHighGridDensity ? 'high' : 'low'}`}
|
||||||
|
status={hasGridDensityPreference}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to ensure large thumbnails
|
||||||
|
on photo grid views (if not configured, density is based on
|
||||||
|
aspect ratio):
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_SHOW_LARGE_THUMBNAILS'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
</ChecklistGroup>
|
||||||
|
<ChecklistGroup
|
||||||
|
title="Settings"
|
||||||
|
icon={<HiOutlineCog size={17} className="translate-y-[0.5px]" />}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Geo privacy"
|
||||||
|
status={isGeoPrivacyEnabled}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to disable
|
||||||
|
collection/display of location-based data:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_GEO_PRIVACY'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Public downloads"
|
||||||
|
status={arePublicDownloadsEnabled}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to enable
|
||||||
|
public photo downloads for all visitors:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Public API"
|
||||||
|
status={isPublicApiEnabled}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to enable
|
||||||
|
a public API available at <code>/api</code>:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_PUBLIC_API'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Priority order"
|
||||||
|
status={isPriorityOrderEnabled}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to prevent
|
||||||
|
priority order photo field affecting photo order:
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_IGNORE_PRIORITY_ORDER'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Legacy OG text alignment"
|
||||||
|
status={isOgTextBottomAligned}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"BOTTOM"'} to
|
||||||
|
keep OG image text bottom aligned (default is {'"top"'}):
|
||||||
|
{renderEnvVars(['NEXT_PUBLIC_OG_TEXT_ALIGNMENT'])}
|
||||||
|
</ChecklistRow>
|
||||||
|
</ChecklistGroup>
|
||||||
|
{areInternalToolsEnabled &&
|
||||||
|
<ChecklistGroup
|
||||||
|
title="Internal"
|
||||||
|
icon={<CgDebug size={16} />}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
<ChecklistRow
|
||||||
|
title="Debug tools"
|
||||||
|
status={areAdminDebugToolsEnabled}
|
||||||
|
optional
|
||||||
|
>
|
||||||
|
Set environment variable to {'"1"'} to temporarily enable
|
||||||
|
features like photo matting, baseline grid, etc.:
|
||||||
|
{renderEnvVars(['ADMIN_DEBUG_TOOLS'])}
|
||||||
</ChecklistRow>
|
</ChecklistRow>
|
||||||
<ChecklistRow
|
<ChecklistRow
|
||||||
title="Preserve original uploads"
|
title="DB optimize"
|
||||||
status={areOriginalUploadsPreserved}
|
status={isAdminDbOptimizeEnabled}
|
||||||
optional
|
optional
|
||||||
>
|
>
|
||||||
Set environment variable to {'"1"'} to prevent
|
Set environment variable to {'"1"'} to prevent
|
||||||
image uploads being compressed before storing:
|
homepages from seeding infinite scroll on load:
|
||||||
{renderEnvVars(['NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS'])}
|
{renderEnvVars(['ADMIN_DB_OPTIMIZE'])}
|
||||||
</ChecklistRow>
|
</ChecklistRow>
|
||||||
<ChecklistRow
|
<ChecklistRow
|
||||||
title={`Image quality: ${imageQuality}`}
|
title="SQL debugging"
|
||||||
status={hasImageQuality}
|
status={isAdminSqlDebugEnabled}
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable from {'"1-100"'}
|
|
||||||
{' '}
|
|
||||||
to control the quality of large photos
|
|
||||||
({'"100"'} represents highest quality/largest size):
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_IMAGE_QUALITY'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Image blur"
|
|
||||||
status={isBlurEnabled}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to prevent
|
|
||||||
image blur data being stored and displayed:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_BLUR_DISABLED'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
</ChecklistGroup>
|
|
||||||
<ChecklistGroup
|
|
||||||
title="Visual"
|
|
||||||
icon={<PiPaintBrushHousehold size={19} />}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
<ChecklistRow
|
|
||||||
title={`Default theme: ${defaultTheme}`}
|
|
||||||
status={hasDefaultTheme}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
{'Set environment variable to \'light\' or \'dark\''}
|
|
||||||
{' '}
|
|
||||||
to configure initial theme
|
|
||||||
{' '}
|
|
||||||
(defaults to {'\'system\''}):
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_DEFAULT_THEME'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Photo matting"
|
|
||||||
status={arePhotosMatted}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to constrain the size
|
|
||||||
{' '}
|
|
||||||
of each photo, and display a surrounding border:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_MATTE_PHOTOS'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
</ChecklistGroup>
|
|
||||||
<ChecklistGroup
|
|
||||||
title="Display"
|
|
||||||
icon={<BiHide size={18} />}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Show EXIF data"
|
|
||||||
status={showExifInfo}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to hide EXIF data:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_EXIF_DATA'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Show zoom controls"
|
|
||||||
status={showZoomControls}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to hide
|
|
||||||
fullscreen photo zoom controls:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_ZOOM_CONTROLS'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Show taken at time"
|
|
||||||
status={showTakenAtTimeHidden}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to hide
|
|
||||||
taken at time from photo meta:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_TAKEN_AT_TIME'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Show social"
|
|
||||||
status={showSocial}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to hide
|
|
||||||
{' '}
|
|
||||||
X (formerly Twitter) button from share modal:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_SOCIAL'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Show Fujifilm simulations"
|
|
||||||
status={showFilmSimulations}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to prevent
|
|
||||||
simulations showing up in /grid sidebar and
|
|
||||||
CMD-K results:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_FILM_SIMULATIONS'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Show repo link"
|
|
||||||
status={showRepoLink}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to hide footer link:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_REPO_LINK'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
</ChecklistGroup>
|
|
||||||
<ChecklistGroup
|
|
||||||
title="Grid"
|
|
||||||
icon={<IoMdGrid size={17} />}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Grid homepage"
|
|
||||||
status={isGridHomepageEnabled}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to show grid layout
|
|
||||||
on homepage:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_GRID_HOMEPAGE'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title={`Grid aspect ratio: ${gridAspectRatio}`}
|
|
||||||
status={hasGridAspectRatio}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to any number to enforce aspect ratio
|
|
||||||
{' '}
|
|
||||||
(default is {'"1"'}, i.e., square)—set to {'"0"'} to disable:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_GRID_ASPECT_RATIO'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title={`Grid density: ${hasHighGridDensity ? 'high' : 'low'}`}
|
|
||||||
status={hasGridDensityPreference}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to ensure large thumbnails
|
|
||||||
on photo grid views (if not configured, density is based on
|
|
||||||
aspect ratio):
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_SHOW_LARGE_THUMBNAILS'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
</ChecklistGroup>
|
|
||||||
<ChecklistGroup
|
|
||||||
title="Settings"
|
|
||||||
icon={<HiOutlineCog size={17} className="translate-y-[0.5px]" />}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Geo privacy"
|
|
||||||
status={isGeoPrivacyEnabled}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to disable
|
|
||||||
collection/display of location-based data:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_GEO_PRIVACY'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Public downloads"
|
|
||||||
status={arePublicDownloadsEnabled}
|
|
||||||
optional
|
optional
|
||||||
>
|
>
|
||||||
Set environment variable to {'"1"'} to enable
|
Set environment variable to {'"1"'} to enable
|
||||||
public photo downloads for all visitors:
|
console output for all sql queries:
|
||||||
{renderEnvVars(['NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS'])}
|
{renderEnvVars(['ADMIN_SQL_DEBUG'])}
|
||||||
</ChecklistRow>
|
</ChecklistRow>
|
||||||
<ChecklistRow
|
</ChecklistGroup>}
|
||||||
title="Public API"
|
</>}
|
||||||
status={isPublicApiEnabled}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to enable
|
|
||||||
a public API available at <code>/api</code>:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_PUBLIC_API'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Priority order"
|
|
||||||
status={isPriorityOrderEnabled}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to prevent
|
|
||||||
priority order photo field affecting photo order:
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_IGNORE_PRIORITY_ORDER'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Legacy OG text alignment"
|
|
||||||
status={isOgTextBottomAligned}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"BOTTOM"'} to
|
|
||||||
keep OG image text bottom aligned (default is {'"top"'}):
|
|
||||||
{renderEnvVars(['NEXT_PUBLIC_OG_TEXT_ALIGNMENT'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
</ChecklistGroup>
|
|
||||||
{areInternalToolsEnabled &&
|
|
||||||
<ChecklistGroup
|
|
||||||
title="Internal"
|
|
||||||
icon={<CgDebug size={16} />}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
<ChecklistRow
|
|
||||||
title="Debug tools"
|
|
||||||
status={areAdminDebugToolsEnabled}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to temporarily enable
|
|
||||||
features like photo matting, baseline grid, etc.:
|
|
||||||
{renderEnvVars(['ADMIN_DEBUG_TOOLS'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="DB optimize"
|
|
||||||
status={isAdminDbOptimizeEnabled}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to prevent
|
|
||||||
homepages from seeding infinite scroll on load:
|
|
||||||
{renderEnvVars(['ADMIN_DB_OPTIMIZE'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
<ChecklistRow
|
|
||||||
title="SQL debugging"
|
|
||||||
status={isAdminSqlDebugEnabled}
|
|
||||||
optional
|
|
||||||
>
|
|
||||||
Set environment variable to {'"1"'} to enable
|
|
||||||
console output for all sql queries:
|
|
||||||
{renderEnvVars(['ADMIN_SQL_DEBUG'])}
|
|
||||||
</ChecklistRow>
|
|
||||||
</ChecklistGroup>}
|
|
||||||
</>}
|
|
||||||
</div>
|
|
||||||
<div className="pl-11 pr-2 sm:pr-11 mt-4 md:mt-7">
|
<div className="pl-11 pr-2 sm:pr-11 mt-4 md:mt-7">
|
||||||
<div>
|
<div>
|
||||||
Changes to environment variables require a redeploy
|
Changes to environment variables require a redeploy
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import Container from '@/components/Container';
|
import Container from '@/components/Container';
|
||||||
import SiteGrid from '@/components/SiteGrid';
|
import SiteGrid from '@/components/SiteGrid';
|
||||||
|
import clsx from 'clsx/lite';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
|
|
||||||
export default function AdminInfoPage({
|
export default function AdminInfoPage({
|
||||||
@ -22,7 +23,10 @@ export default function AdminInfoPage({
|
|||||||
{accessory}
|
{accessory}
|
||||||
</div>
|
</div>
|
||||||
<Container spaceChildren={false}>
|
<Container spaceChildren={false}>
|
||||||
<div className="max-w-xl w-full">
|
<div className={clsx(
|
||||||
|
'max-w-xl w-full',
|
||||||
|
'space-y-6 md:space-y-8',
|
||||||
|
)}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@ -120,7 +120,7 @@ export default function AdminAppInsightsClient({
|
|||||||
</a>;
|
</a>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-6 md:space-y-8">
|
<>
|
||||||
{(codeMeta || debug) && <>
|
{(codeMeta || debug) && <>
|
||||||
<ScoreCard title="Source code">
|
<ScoreCard title="Source code">
|
||||||
{codeMeta?.didError
|
{codeMeta?.didError
|
||||||
@ -396,6 +396,6 @@ export default function AdminAppInsightsClient({
|
|||||||
content={descriptionWithSpaces}
|
content={descriptionWithSpaces}
|
||||||
/>}
|
/>}
|
||||||
</ScoreCard>
|
</ScoreCard>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,6 +41,7 @@ export const PATH_ADMIN_TAGS = `${PATH_ADMIN}/tags`;
|
|||||||
export const PATH_ADMIN_CONFIGURATION = `${PATH_ADMIN}/configuration`;
|
export const PATH_ADMIN_CONFIGURATION = `${PATH_ADMIN}/configuration`;
|
||||||
export const PATH_ADMIN_INSIGHTS = `${PATH_ADMIN}/insights`;
|
export const PATH_ADMIN_INSIGHTS = `${PATH_ADMIN}/insights`;
|
||||||
export const PATH_ADMIN_BASELINE = `${PATH_ADMIN}/baseline`;
|
export const PATH_ADMIN_BASELINE = `${PATH_ADMIN}/baseline`;
|
||||||
|
export const PATH_ADMIN_COMPONENTS = `${PATH_ADMIN}/components`;
|
||||||
|
|
||||||
// Debug paths
|
// Debug paths
|
||||||
export const PATH_OG_ALL = `${PATH_OG}/all`;
|
export const PATH_OG_ALL = `${PATH_OG}/all`;
|
||||||
@ -60,6 +61,8 @@ export const PATHS_ADMIN = [
|
|||||||
PATH_ADMIN_UPLOADS,
|
PATH_ADMIN_UPLOADS,
|
||||||
PATH_ADMIN_TAGS,
|
PATH_ADMIN_TAGS,
|
||||||
PATH_ADMIN_CONFIGURATION,
|
PATH_ADMIN_CONFIGURATION,
|
||||||
|
PATH_ADMIN_BASELINE,
|
||||||
|
PATH_ADMIN_COMPONENTS,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const PATHS_TO_CACHE = [
|
export const PATHS_TO_CACHE = [
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import ExperimentalBadge from './ExperimentalBadge';
|
|||||||
import Badge from './Badge';
|
import Badge from './Badge';
|
||||||
import ResponsiveText from './primitives/ResponsiveText';
|
import ResponsiveText from './primitives/ResponsiveText';
|
||||||
import { parameterize } from '@/utility/string';
|
import { parameterize } from '@/utility/string';
|
||||||
|
import ScoreCard from './ScoreCard';
|
||||||
|
|
||||||
export default function ChecklistGroup({
|
export default function ChecklistGroup({
|
||||||
title,
|
title,
|
||||||
@ -23,37 +24,29 @@ export default function ChecklistGroup({
|
|||||||
const slug = parameterize(title);
|
const slug = parameterize(title);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<ScoreCard title={<a
|
||||||
<a
|
id={slug}
|
||||||
id={slug}
|
href={`#${slug}`}
|
||||||
href={`#${slug}`}
|
className={clsx(
|
||||||
className={clsx(
|
'inline-flex items-center',
|
||||||
'inline-flex items-center',
|
'text-gray-600 dark:text-gray-300',
|
||||||
'text-gray-600 dark:text-gray-300',
|
'sm:pl-1.5',
|
||||||
'pl-[18px] py-3 text-lg',
|
)}
|
||||||
)}
|
>
|
||||||
>
|
<span className="w-8 sm:w-9 shrink-0">{icon}</span>
|
||||||
<span className="w-7 shrink-0">{icon}</span>
|
<span className="inline-flex flex-wrap items-center gap-y-1 gap-x-1.5">
|
||||||
<span className="inline-flex flex-wrap items-center gap-y-1 gap-x-1.5">
|
<ResponsiveText shortText={titleShort}>
|
||||||
<ResponsiveText shortText={titleShort}>
|
{title}
|
||||||
{title}
|
</ResponsiveText>
|
||||||
</ResponsiveText>
|
{optional &&
|
||||||
{optional &&
|
<Badge type="small" className="translate-y-[0.5px]">
|
||||||
<Badge type="small" className="translate-y-[0.5px]">
|
Optional
|
||||||
Optional
|
</Badge>}
|
||||||
</Badge>}
|
{experimental &&
|
||||||
{experimental &&
|
<ExperimentalBadge className="translate-y-[0.5px]" />}
|
||||||
<ExperimentalBadge className="translate-y-[0.5px]" />}
|
</span>
|
||||||
</span>
|
</a>}>
|
||||||
</a>
|
{children}
|
||||||
<div className={clsx(
|
</ScoreCard>
|
||||||
'bg-white dark:bg-black',
|
|
||||||
'dark:text-gray-400',
|
|
||||||
'border border-gray-200 dark:border-gray-800 rounded-md',
|
|
||||||
'divide-y divide-gray-200 dark:divide-gray-800',
|
|
||||||
)}>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { ReactNode } from 'react';
|
|||||||
import { clsx } from 'clsx/lite';
|
import { clsx } from 'clsx/lite';
|
||||||
import StatusIcon from './StatusIcon';
|
import StatusIcon from './StatusIcon';
|
||||||
import ExperimentalBadge from './ExperimentalBadge';
|
import ExperimentalBadge from './ExperimentalBadge';
|
||||||
|
import ScoreCardRow from './ScoreCardRow';
|
||||||
|
|
||||||
export default function ChecklistRow({
|
export default function ChecklistRow({
|
||||||
title,
|
title,
|
||||||
@ -21,31 +22,28 @@ export default function ChecklistRow({
|
|||||||
children: ReactNode
|
children: ReactNode
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={clsx(
|
<ScoreCardRow
|
||||||
'flex gap-2.5',
|
icon={<StatusIcon
|
||||||
'px-4 pt-2 pb-2.5',
|
|
||||||
)}>
|
|
||||||
<StatusIcon
|
|
||||||
type={status
|
type={status
|
||||||
? 'checked'
|
? 'checked'
|
||||||
: showWarning
|
: showWarning
|
||||||
? 'warning'
|
? 'warning'
|
||||||
: optional ? 'optional' : 'missing'}
|
: optional ? 'optional' : 'missing'}
|
||||||
loading={isPending}
|
loading={isPending}
|
||||||
/>
|
/>}
|
||||||
<div className="flex flex-col min-w-0 grow">
|
content={<>
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
'flex flex-wrap items-center gap-2 pb-0.5',
|
'flex flex-wrap items-center gap-2 pb-0.5',
|
||||||
'font-bold dark:text-gray-300',
|
'font-bold text-main',
|
||||||
)}>
|
)}>
|
||||||
{title}
|
{title}
|
||||||
{experimental &&
|
{experimental &&
|
||||||
<ExperimentalBadge className="translate-y-[-0.5px]" />}
|
<ExperimentalBadge className="translate-y-[-0.5px]" />}
|
||||||
</div>
|
</div>
|
||||||
<div className="leading-relaxed">
|
<div className="leading-relaxed text-medium">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</>}
|
||||||
</div>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,15 +6,15 @@ export default function ScoreCard({
|
|||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
title?: string,
|
title?: ReactNode,
|
||||||
children: ReactNode,
|
children: ReactNode,
|
||||||
className?: string,
|
className?: string,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
<div className="space-y-2">
|
||||||
{title &&
|
{title &&
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
'pl-[15px]',
|
'pl-[15px] h-6',
|
||||||
'uppercase font-medium tracking-wider text-[0.8rem]',
|
'uppercase font-medium tracking-wider text-[0.8rem]',
|
||||||
'text-medium',
|
'text-medium',
|
||||||
)}>
|
)}>
|
||||||
|
|||||||
@ -44,7 +44,7 @@ export default function ScoreCardRow({
|
|||||||
<div className="grow space-y-2 py-1.5 w-full overflow-auto">
|
<div className="grow space-y-2 py-1.5 w-full overflow-auto">
|
||||||
<div className={clsx(
|
<div className={clsx(
|
||||||
'text-main pr-2',
|
'text-main pr-2',
|
||||||
!isExpanded && 'max-w-full truncate',
|
expandContent && !isExpanded && 'max-w-full truncate',
|
||||||
)}>
|
)}>
|
||||||
{typeof content === 'function'
|
{typeof content === 'function'
|
||||||
? content(isExpanded)
|
? content(isExpanded)
|
||||||
|
|||||||
@ -4,13 +4,16 @@ import {
|
|||||||
BiSolidXSquare,
|
BiSolidXSquare,
|
||||||
} from 'react-icons/bi';
|
} from 'react-icons/bi';
|
||||||
import Spinner from './Spinner';
|
import Spinner from './Spinner';
|
||||||
|
import clsx from 'clsx/lite';
|
||||||
|
|
||||||
export default function StatusIcon({
|
export default function StatusIcon({
|
||||||
type,
|
type,
|
||||||
loading,
|
loading,
|
||||||
|
className,
|
||||||
}: {
|
}: {
|
||||||
type: 'checked' | 'missing' | 'warning' | 'optional'
|
type: 'checked' | 'missing' | 'warning' | 'optional'
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const getIcon = () => {
|
const getIcon = () => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@ -21,13 +24,13 @@ export default function StatusIcon({
|
|||||||
/>;
|
/>;
|
||||||
case 'missing':
|
case 'missing':
|
||||||
return <BiSolidXSquare
|
return <BiSolidXSquare
|
||||||
size={14}
|
size={14.5}
|
||||||
className="text-red-400 translate-x-[2px] translate-y-[1.5px]"
|
className="text-red-400"
|
||||||
/>;
|
/>;
|
||||||
case 'warning':
|
case 'warning':
|
||||||
return <BiSolidXSquare
|
return <BiSolidXSquare
|
||||||
size={14}
|
size={14.5}
|
||||||
className="text-amber-500 translate-x-[2px] translate-y-[1.5px]"
|
className="text-amber-500"
|
||||||
/>;
|
/>;
|
||||||
case 'optional':
|
case 'optional':
|
||||||
return <BiSolidCheckboxMinus
|
return <BiSolidCheckboxMinus
|
||||||
@ -38,12 +41,18 @@ export default function StatusIcon({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-w-[1.2rem] pt-[1px]">
|
<span className={clsx(
|
||||||
|
'size-[16px]',
|
||||||
|
'inline-flex items-center justify-center',
|
||||||
|
className,
|
||||||
|
)}>
|
||||||
{loading
|
{loading
|
||||||
? <div className="translate-y-0.5">
|
? <span className="translate-y-[1px]">
|
||||||
<Spinner size={14} />
|
<Spinner size={12} />
|
||||||
</div>
|
</span>
|
||||||
: getIcon()}
|
: <span>
|
||||||
</div>
|
{getIcon()}
|
||||||
|
</span>}
|
||||||
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {
|
|||||||
} from 'react';
|
} from 'react';
|
||||||
import {
|
import {
|
||||||
PATH_ADMIN_BASELINE,
|
PATH_ADMIN_BASELINE,
|
||||||
|
PATH_ADMIN_COMPONENTS,
|
||||||
PATH_ADMIN_CONFIGURATION,
|
PATH_ADMIN_CONFIGURATION,
|
||||||
PATH_ADMIN_INSIGHTS,
|
PATH_ADMIN_INSIGHTS,
|
||||||
PATH_ADMIN_PHOTOS,
|
PATH_ADMIN_PHOTOS,
|
||||||
@ -376,6 +377,9 @@ export default function CommandKClient({
|
|||||||
? [{
|
? [{
|
||||||
label: 'Baseline Overview',
|
label: 'Baseline Overview',
|
||||||
path: PATH_ADMIN_BASELINE,
|
path: PATH_ADMIN_BASELINE,
|
||||||
|
}, {
|
||||||
|
label: 'Components Overview',
|
||||||
|
path: PATH_ADMIN_COMPONENTS,
|
||||||
}]
|
}]
|
||||||
: [])
|
: [])
|
||||||
.concat({
|
.concat({
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user