Finalize core insights UX
This commit is contained in:
parent
cc02829849
commit
e1082a8a3d
@ -67,6 +67,7 @@ export default function AdminOutdatedClient({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
isLoading={arePhotoIdsSyncing}
|
isLoading={arePhotoIdsSyncing}
|
||||||
|
disabled={!updateBatchSize}
|
||||||
>
|
>
|
||||||
{arePhotoIdsSyncing
|
{arePhotoIdsSyncing
|
||||||
? 'Syncing'
|
? 'Syncing'
|
||||||
|
|||||||
@ -19,6 +19,7 @@ import {
|
|||||||
VERCEL_GIT_REPO_SLUG,
|
VERCEL_GIT_REPO_SLUG,
|
||||||
} from '@/app-core/config';
|
} from '@/app-core/config';
|
||||||
import { getGitHubMetaWithFallback } from '../github';
|
import { getGitHubMetaWithFallback } from '../github';
|
||||||
|
import { OUTDATED_THRESHOLD } from '@/photo';
|
||||||
|
|
||||||
const BASIC_PHOTO_INSTALLATION_COUNT = 32;
|
const BASIC_PHOTO_INSTALLATION_COUNT = 32;
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ export default async function AdminAppInsights() {
|
|||||||
const [
|
const [
|
||||||
{ count: photosCount, dateRange },
|
{ count: photosCount, dateRange },
|
||||||
{ count: photosCountHidden },
|
{ count: photosCountHidden },
|
||||||
|
{ count: photosCountOutdated },
|
||||||
{ count: photosCountPortrait },
|
{ count: photosCountPortrait },
|
||||||
tags,
|
tags,
|
||||||
cameras,
|
cameras,
|
||||||
@ -39,6 +41,7 @@ export default async function AdminAppInsights() {
|
|||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
getPhotosMeta({ hidden: 'include' }),
|
getPhotosMeta({ hidden: 'include' }),
|
||||||
getPhotosMeta({ hidden: 'only' }),
|
getPhotosMeta({ hidden: 'only' }),
|
||||||
|
getPhotosMeta({ hidden: 'include', updatedBefore: OUTDATED_THRESHOLD }),
|
||||||
getPhotosMeta({ maximumAspectRatio: 0.9 }),
|
getPhotosMeta({ maximumAspectRatio: 0.9 }),
|
||||||
getUniqueTags(),
|
getUniqueTags(),
|
||||||
getUniqueCameras(),
|
getUniqueCameras(),
|
||||||
@ -63,11 +66,12 @@ export default async function AdminAppInsights() {
|
|||||||
return (
|
return (
|
||||||
<AdminAppInsightsClient
|
<AdminAppInsightsClient
|
||||||
codeMeta={codeMeta}
|
codeMeta={codeMeta}
|
||||||
recommendations={{
|
insights={{
|
||||||
noFork: !codeMeta?.isForkedFromBase && !codeMeta?.isBaseRepo,
|
noFork: !codeMeta?.isForkedFromBase && !codeMeta?.isBaseRepo,
|
||||||
forkBehind: Boolean(codeMeta?.isBehind),
|
forkBehind: Boolean(codeMeta?.isBehind),
|
||||||
noAi: !isAiTextGenerationEnabled,
|
noAi: !isAiTextGenerationEnabled,
|
||||||
noAiRateLimiting: isAiTextGenerationEnabled && !hasVercelBlobStorage,
|
noAiRateLimiting: isAiTextGenerationEnabled && !hasVercelBlobStorage,
|
||||||
|
outdatedPhotos: Boolean(photosCountOutdated),
|
||||||
photoMatting: photosCountPortrait > 0 && !MATTE_PHOTOS,
|
photoMatting: photosCountPortrait > 0 && !MATTE_PHOTOS,
|
||||||
gridFirst: (
|
gridFirst: (
|
||||||
photosCount >= BASIC_PHOTO_INSTALLATION_COUNT &&
|
photosCount >= BASIC_PHOTO_INSTALLATION_COUNT &&
|
||||||
@ -78,6 +82,7 @@ export default async function AdminAppInsights() {
|
|||||||
photoStats={{
|
photoStats={{
|
||||||
photosCount,
|
photosCount,
|
||||||
photosCountHidden,
|
photosCountHidden,
|
||||||
|
photosCountOutdated,
|
||||||
tagsCount: tags.length,
|
tagsCount: tags.length,
|
||||||
camerasCount: cameras.length,
|
camerasCount: cameras.length,
|
||||||
filmSimulationsCount: filmSimulations.length,
|
filmSimulationsCount: filmSimulations.length,
|
||||||
|
|||||||
@ -1,20 +1,16 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import IconGrSync from '@/app-core/IconGrSync';
|
|
||||||
import ScoreCard from '@/components/ScoreCard';
|
import ScoreCard from '@/components/ScoreCard';
|
||||||
import ScoreCardRow from '@/components/ScoreCardRow';
|
import ScoreCardRow from '@/components/ScoreCardRow';
|
||||||
import { dateRangeForPhotos, PhotoDateRange } from '@/photo';
|
import { dateRangeForPhotos } from '@/photo';
|
||||||
import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon';
|
import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon';
|
||||||
import { FaCamera } from 'react-icons/fa';
|
import { FaCamera } from 'react-icons/fa';
|
||||||
import { FaTag } from 'react-icons/fa';
|
import { FaTag } from 'react-icons/fa';
|
||||||
import { FaRegCalendar } from 'react-icons/fa6';
|
import { FaCircleInfo, FaRegCalendar } from 'react-icons/fa6';
|
||||||
import {
|
import { HiOutlinePhotograph } from 'react-icons/hi';
|
||||||
HiOutlinePhotograph,
|
import { MdAspectRatio } from 'react-icons/md';
|
||||||
HiSparkles,
|
|
||||||
} from 'react-icons/hi';
|
|
||||||
import { MdLightbulbOutline } from 'react-icons/md';
|
|
||||||
import { PiWarningBold } from 'react-icons/pi';
|
import { PiWarningBold } from 'react-icons/pi';
|
||||||
import { TbCone } from 'react-icons/tb';
|
import { TbCone, TbSparkles } from 'react-icons/tb';
|
||||||
import { getGitHubMetaWithFallback } from '../github';
|
import { getGitHubMetaWithFallback } from '../github';
|
||||||
import { BiGitBranch, BiGitCommit, BiLogoGithub } from 'react-icons/bi';
|
import { BiGitBranch, BiGitCommit, BiLogoGithub } from 'react-icons/bi';
|
||||||
import {
|
import {
|
||||||
@ -23,24 +19,40 @@ import {
|
|||||||
TEMPLATE_REPO_NAME,
|
TEMPLATE_REPO_NAME,
|
||||||
VERCEL_GIT_COMMIT_SHA_SHORT,
|
VERCEL_GIT_COMMIT_SHA_SHORT,
|
||||||
VERCEL_GIT_COMMIT_MESSAGE,
|
VERCEL_GIT_COMMIT_MESSAGE,
|
||||||
|
TEMPLATE_REPO_URL_FORK,
|
||||||
|
TEMPLATE_REPO_URL_README,
|
||||||
} from '@/app-core/config';
|
} from '@/app-core/config';
|
||||||
import { AdminAppInsight } from '.';
|
import { AdminAppInsights, hasTemplateRecommendations, PhotoStats } from '.';
|
||||||
import EnvVar from '@/components/EnvVar';
|
import EnvVar from '@/components/EnvVar';
|
||||||
import { IoSyncCircle } from 'react-icons/io5';
|
import { IoSyncCircle } from 'react-icons/io5';
|
||||||
import clsx from 'clsx/lite';
|
import clsx from 'clsx/lite';
|
||||||
|
import { PATH_ADMIN_OUTDATED } from '@/app-core/paths';
|
||||||
|
import { LiaBroomSolid } from 'react-icons/lia';
|
||||||
|
import { IoMdGrid } from 'react-icons/io';
|
||||||
|
import { RiSpeedMiniLine } from 'react-icons/ri';
|
||||||
|
import LinkWithStatus from '@/components/LinkWithStatus';
|
||||||
|
|
||||||
|
const readmeAnchor = (anchor: string, text: string) =>
|
||||||
|
<a
|
||||||
|
href={`${TEMPLATE_REPO_URL_README}#${anchor}}`}
|
||||||
|
target="blank"
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</a>;
|
||||||
|
|
||||||
const DEBUG_COMMIT_SHA = '4cd29ed';
|
const DEBUG_COMMIT_SHA = '4cd29ed';
|
||||||
const DEBUG_COMMIT_MESSAGE = 'Long commit message for debugging purposes';
|
const DEBUG_COMMIT_MESSAGE = 'Long commit message for debugging purposes';
|
||||||
|
const DEBUG_BEHIND_BY = 9;
|
||||||
|
const DEBUG_PHOTOS_COUNT_OUTDATED = 7;
|
||||||
|
|
||||||
export default function AdminAppInsightsClient({
|
export default function AdminAppInsightsClient({
|
||||||
codeMeta,
|
codeMeta,
|
||||||
recommendations: {
|
insights,
|
||||||
noAi,
|
|
||||||
noAiRateLimiting,
|
|
||||||
},
|
|
||||||
photoStats: {
|
photoStats: {
|
||||||
photosCount,
|
photosCount,
|
||||||
photosCountHidden,
|
photosCountHidden,
|
||||||
|
photosCountOutdated,
|
||||||
tagsCount,
|
tagsCount,
|
||||||
camerasCount,
|
camerasCount,
|
||||||
filmSimulationsCount,
|
filmSimulationsCount,
|
||||||
@ -50,18 +62,20 @@ export default function AdminAppInsightsClient({
|
|||||||
debug,
|
debug,
|
||||||
}: {
|
}: {
|
||||||
codeMeta?: Awaited<ReturnType<typeof getGitHubMetaWithFallback>>
|
codeMeta?: Awaited<ReturnType<typeof getGitHubMetaWithFallback>>
|
||||||
recommendations: Record<AdminAppInsight, boolean>
|
insights: AdminAppInsights
|
||||||
photoStats: {
|
photoStats: PhotoStats
|
||||||
photosCount: number
|
|
||||||
photosCountHidden: number
|
|
||||||
tagsCount: number
|
|
||||||
camerasCount: number
|
|
||||||
filmSimulationsCount: number
|
|
||||||
lensesCount: number
|
|
||||||
dateRange?: PhotoDateRange
|
|
||||||
},
|
|
||||||
debug?: boolean
|
debug?: boolean
|
||||||
}) {
|
}) {
|
||||||
|
const {
|
||||||
|
noFork,
|
||||||
|
forkBehind,
|
||||||
|
noAi,
|
||||||
|
noAiRateLimiting,
|
||||||
|
outdatedPhotos,
|
||||||
|
photoMatting,
|
||||||
|
gridFirst,
|
||||||
|
noStaticOptimization,
|
||||||
|
} = insights;
|
||||||
|
|
||||||
const { descriptionWithSpaces } = dateRangeForPhotos(undefined, dateRange);
|
const { descriptionWithSpaces } = dateRangeForPhotos(undefined, dateRange);
|
||||||
|
|
||||||
@ -69,37 +83,63 @@ export default function AdminAppInsightsClient({
|
|||||||
<div className="space-y-6 md:space-y-8">
|
<div className="space-y-6 md:space-y-8">
|
||||||
{(codeMeta?.isBaseRepo || codeMeta?.isForkedFromBase || debug) && <>
|
{(codeMeta?.isBaseRepo || codeMeta?.isForkedFromBase || debug) && <>
|
||||||
<ScoreCard title="Build details">
|
<ScoreCard title="Build details">
|
||||||
{(codeMeta?.behindBy || debug) &&
|
{(noFork || debug) &&
|
||||||
<ScoreCardRow
|
<ScoreCardRow
|
||||||
icon={<IoSyncCircle
|
icon={<FaCircleInfo
|
||||||
size={18}
|
size={15}
|
||||||
className="text-blue-500"
|
className="text-blue-500 translate-y-[1px]"
|
||||||
/>}
|
/>}
|
||||||
content={<>
|
content="This template is not forked"
|
||||||
This fork is
|
expandContent={<>
|
||||||
{' '}
|
|
||||||
<span className={clsx(
|
|
||||||
'text-blue-600 bg-blue-100/60',
|
|
||||||
'dark:text-blue-400 dark:bg-blue-900/50',
|
|
||||||
'px-1.5 pt-[1px] pb-0.5 rounded-md',
|
|
||||||
)}>
|
|
||||||
{codeMeta?.behindBy ?? 9} commits
|
|
||||||
</span>
|
|
||||||
{' '}
|
|
||||||
behind
|
|
||||||
</>}
|
|
||||||
additionalContent={<>
|
|
||||||
<a
|
<a
|
||||||
href={codeMeta?.urlRepo}
|
href={TEMPLATE_REPO_URL_FORK}
|
||||||
target="blank"
|
target="blank"
|
||||||
className="underline"
|
className="underline"
|
||||||
>
|
>
|
||||||
Sync your fork
|
Fork
|
||||||
</a>
|
</a>
|
||||||
{' '}
|
{' '}
|
||||||
to receive the latest fixes and features
|
original template to receive the latest fixes and features.
|
||||||
|
{' '}
|
||||||
|
{readmeAnchor('receiving-updates', 'Additional instructions')}
|
||||||
|
{' '}
|
||||||
|
in README.
|
||||||
</>}
|
</>}
|
||||||
/>}
|
/>}
|
||||||
|
{(forkBehind || debug) && <ScoreCardRow
|
||||||
|
icon={<IoSyncCircle
|
||||||
|
size={18}
|
||||||
|
className="text-blue-500"
|
||||||
|
/>}
|
||||||
|
content={<>
|
||||||
|
This fork is
|
||||||
|
{' '}
|
||||||
|
<span className={clsx(
|
||||||
|
'text-blue-600 bg-blue-100/60',
|
||||||
|
'dark:text-blue-400 dark:bg-blue-900/50',
|
||||||
|
'px-1.5 pt-[1px] pb-0.5 rounded-md',
|
||||||
|
)}>
|
||||||
|
{codeMeta?.behindBy ?? DEBUG_BEHIND_BY}
|
||||||
|
{' '}
|
||||||
|
{(codeMeta?.behindBy ?? DEBUG_BEHIND_BY) === 1
|
||||||
|
? 'commit'
|
||||||
|
: 'commits'}
|
||||||
|
</span>
|
||||||
|
{' '}
|
||||||
|
behind
|
||||||
|
</>}
|
||||||
|
expandContent={<>
|
||||||
|
<a
|
||||||
|
href={codeMeta?.urlRepo}
|
||||||
|
target="blank"
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
Sync your fork
|
||||||
|
</a>
|
||||||
|
{' '}
|
||||||
|
to receive the latest fixes and features
|
||||||
|
</>}
|
||||||
|
/>}
|
||||||
<ScoreCardRow
|
<ScoreCardRow
|
||||||
icon={<BiLogoGithub size={17} />}
|
icon={<BiLogoGithub size={17} />}
|
||||||
content={<div
|
content={<div
|
||||||
@ -152,42 +192,104 @@ export default function AdminAppInsightsClient({
|
|||||||
/>
|
/>
|
||||||
</ScoreCard>
|
</ScoreCard>
|
||||||
</>}
|
</>}
|
||||||
<ScoreCard title="Template recommendations">
|
{(hasTemplateRecommendations(insights) || debug) &&
|
||||||
{(noAiRateLimiting || debug) && <ScoreCardRow
|
<ScoreCard title="Template recommendations">
|
||||||
icon={<PiWarningBold
|
{(noAiRateLimiting || debug) && <ScoreCardRow
|
||||||
size={17}
|
icon={<PiWarningBold
|
||||||
className="translate-x-[0.5px] text-amber-600"
|
size={17}
|
||||||
|
className="translate-x-[0.5px] text-amber-600"
|
||||||
|
/>}
|
||||||
|
content="AI enabled without rate limiting"
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
expandContent="Create Vercel KV store and link o this project in order to enable rate limiting."
|
||||||
/>}
|
/>}
|
||||||
content="AI enabled without rate limiting"
|
{(noAi || debug) && <ScoreCardRow
|
||||||
// eslint-disable-next-line max-len
|
icon={<TbSparkles size={17} />}
|
||||||
additionalContent="Create Vercel KV store and link o this project in order to enable rate limiting."
|
content="Improve SEO + accessibility with AI"
|
||||||
/>}
|
expandContent={<>
|
||||||
{(noAi || debug) && <ScoreCardRow
|
<div>
|
||||||
icon={<MdLightbulbOutline size={19} />}
|
Enable automatic AI text generation
|
||||||
content="Enable AI text generation to improve photo descriptions"
|
{' '}
|
||||||
// eslint-disable-next-line max-len
|
by setting environment variable
|
||||||
additionalContent="Create Vercel KV store and link it to this project in order to enable rate limiting."
|
{' '}
|
||||||
/>}
|
<EnvVar variable="OPENAI_SECRET_KEY" />.
|
||||||
<ScoreCardRow
|
</div>
|
||||||
icon={<MdLightbulbOutline size={19} />}
|
<div>
|
||||||
// eslint-disable-next-line max-len
|
Further instruction in
|
||||||
content="You seem to have several vertical photos—consider enabling matting to make portrait and landscape photos appear more consistent"
|
{' '}
|
||||||
additionalContent={<>
|
{readmeAnchor('ai-text-generation', 'README')}.
|
||||||
Enabled photo matting by setting
|
</div>
|
||||||
<EnvVar variable="NEXT_PUBLIC_MATTE_PHOTOS" value="1" />
|
</>}
|
||||||
</>}
|
/>}
|
||||||
/>
|
{(photoMatting || debug) && <ScoreCardRow
|
||||||
<ScoreCardRow
|
// eslint-disable-next-line max-len
|
||||||
icon={<IconGrSync />}
|
icon={<MdAspectRatio size={17} className="rotate-90 translate-x-[-1px]" />}
|
||||||
// eslint-disable-next-line max-len
|
content="Vertical photos may benefit from matting"
|
||||||
content="Consider forking this repository to receive new features and fixes"
|
expandContent={<>
|
||||||
/>
|
{/* eslint-disable-next-line max-len */}
|
||||||
<ScoreCardRow
|
Enable photo matting to make portrait and landscape photos appear more consistent
|
||||||
icon={<HiSparkles />}
|
<EnvVar variable="NEXT_PUBLIC_MATTE_PHOTOS" value="1" />
|
||||||
content="Enable AI text generation in the app configuration"
|
</>}
|
||||||
/>
|
/>}
|
||||||
</ScoreCard>
|
{(gridFirst || debug) && <ScoreCardRow
|
||||||
|
icon={<IoMdGrid size={18} className="translate-y-[-1px]" />}
|
||||||
|
content="Grid homepage"
|
||||||
|
expandContent={<>
|
||||||
|
Enable grid homepage by setting environment variable
|
||||||
|
{' '}
|
||||||
|
<EnvVar variable="NEXT_PUBLIC_GRID_HOMEPAGE_ENABLED" value="1" />
|
||||||
|
</>}
|
||||||
|
/>}
|
||||||
|
{(noStaticOptimization || debug) && <ScoreCardRow
|
||||||
|
icon={<RiSpeedMiniLine
|
||||||
|
size={19}
|
||||||
|
className="translate-x-[1px] translate-y-[-1.5px]"
|
||||||
|
/>}
|
||||||
|
content="Static optimization"
|
||||||
|
expandContent={<>
|
||||||
|
{/* eslint-disable-next-line max-len */}
|
||||||
|
Enable static optimization by setting any of the following environment variables:
|
||||||
|
<div className="flex flex-col gap-y-1 mt-3">
|
||||||
|
<EnvVar
|
||||||
|
variable="NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS"
|
||||||
|
value="1"
|
||||||
|
/>
|
||||||
|
<EnvVar
|
||||||
|
variable="NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES"
|
||||||
|
value="1"
|
||||||
|
/>
|
||||||
|
<EnvVar
|
||||||
|
variable="NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES"
|
||||||
|
value="1"
|
||||||
|
/>
|
||||||
|
<EnvVar
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
variable="NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORY_OG_IMAGES"
|
||||||
|
value="1"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>}
|
||||||
|
/>}
|
||||||
|
</ScoreCard>}
|
||||||
<ScoreCard title="Library Stats">
|
<ScoreCard title="Library Stats">
|
||||||
|
{(outdatedPhotos || debug) && <ScoreCardRow
|
||||||
|
icon={<LiaBroomSolid
|
||||||
|
size={19}
|
||||||
|
className="translate-y-[-2px] text-amber-600"
|
||||||
|
/>}
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
content={`You have ${photosCountOutdated || DEBUG_PHOTOS_COUNT_OUTDATED} outdated ${(photosCountOutdated || DEBUG_PHOTOS_COUNT_OUTDATED) === 1 ? 'photo' : 'photos'}`}
|
||||||
|
expandContent={<>
|
||||||
|
<LinkWithStatus
|
||||||
|
href={PATH_ADMIN_OUTDATED}
|
||||||
|
className="underline"
|
||||||
|
>
|
||||||
|
View outdated photos
|
||||||
|
</LinkWithStatus>
|
||||||
|
{' '}
|
||||||
|
to update them in batches.
|
||||||
|
</>}
|
||||||
|
/>}
|
||||||
<ScoreCardRow
|
<ScoreCardRow
|
||||||
icon={<HiOutlinePhotograph
|
icon={<HiOutlinePhotograph
|
||||||
size={17}
|
size={17}
|
||||||
|
|||||||
@ -1,8 +1,44 @@
|
|||||||
|
import { PhotoDateRange } from '@/photo';
|
||||||
|
|
||||||
export type AdminAppInsight =
|
export type AdminAppInsight =
|
||||||
'noFork' |
|
'noFork' |
|
||||||
'forkBehind' |
|
'forkBehind' |
|
||||||
'noAi' |
|
'noAi' |
|
||||||
'noAiRateLimiting' |
|
'noAiRateLimiting' |
|
||||||
|
'outdatedPhotos' |
|
||||||
'photoMatting' |
|
'photoMatting' |
|
||||||
'gridFirst' |
|
'gridFirst' |
|
||||||
'noStaticOptimization';
|
'noStaticOptimization';
|
||||||
|
|
||||||
|
const RECOMMENDATIONS: AdminAppInsight[] = [
|
||||||
|
'noAi',
|
||||||
|
'noAiRateLimiting',
|
||||||
|
'photoMatting',
|
||||||
|
'gridFirst',
|
||||||
|
'noStaticOptimization',
|
||||||
|
];
|
||||||
|
|
||||||
|
export type AdminAppInsights = Record<AdminAppInsight, boolean>
|
||||||
|
|
||||||
|
export const hasTemplateRecommendations = (insights: AdminAppInsights) =>
|
||||||
|
RECOMMENDATIONS.some(insight => insights[insight]);
|
||||||
|
|
||||||
|
export interface PhotoStats {
|
||||||
|
photosCount: number
|
||||||
|
photosCountHidden: number
|
||||||
|
photosCountOutdated: number
|
||||||
|
tagsCount: number
|
||||||
|
camerasCount: number
|
||||||
|
filmSimulationsCount: number
|
||||||
|
lensesCount: number
|
||||||
|
dateRange?: PhotoDateRange
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getInsightIndicator = ({
|
||||||
|
forkBehind,
|
||||||
|
noAiRateLimiting,
|
||||||
|
outdatedPhotos,
|
||||||
|
}: AdminAppInsights) =>
|
||||||
|
forkBehind ||
|
||||||
|
noAiRateLimiting ||
|
||||||
|
outdatedPhotos;
|
||||||
|
|||||||
@ -21,6 +21,9 @@ export const TEMPLATE_REPO_NAME = 'exif-photo-blog';
|
|||||||
export const TEMPLATE_REPO_BRANCH = 'main';
|
export const TEMPLATE_REPO_BRANCH = 'main';
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
export const TEMPLATE_REPO_URL = `https://github.com/${TEMPLATE_REPO_OWNER}/${TEMPLATE_REPO_NAME}`;
|
export const TEMPLATE_REPO_URL = `https://github.com/${TEMPLATE_REPO_OWNER}/${TEMPLATE_REPO_NAME}`;
|
||||||
|
export const TEMPLATE_REPO_URL_FORK = `${TEMPLATE_REPO_URL}/fork`;
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
export const TEMPLATE_REPO_URL_README = `${TEMPLATE_REPO_URL}?tab=readme-ov-file`;
|
||||||
|
|
||||||
export const VERCEL_GIT_PROVIDER =
|
export const VERCEL_GIT_PROVIDER =
|
||||||
process.env.NEXT_PUBLIC_VERCEL_GIT_PROVIDER;
|
process.env.NEXT_PUBLIC_VERCEL_GIT_PROVIDER;
|
||||||
|
|||||||
@ -23,6 +23,7 @@ export default function EnvVar({
|
|||||||
'px-1.5 py-[0.5px]',
|
'px-1.5 py-[0.5px]',
|
||||||
'rounded-md',
|
'rounded-md',
|
||||||
'bg-gray-100 dark:bg-gray-800',
|
'bg-gray-100 dark:bg-gray-800',
|
||||||
|
'whitespace-nowrap',
|
||||||
)}>
|
)}>
|
||||||
{variable}{value && ` = ${value}`}
|
{variable}{value && ` = ${value}`}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -89,7 +89,7 @@ export default function LinkWithStatus({
|
|||||||
{...props }
|
{...props }
|
||||||
href={href}
|
href={href}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
'relative flex transition-[colors,opacity]',
|
'relative inline-flex transition-[colors,opacity]',
|
||||||
(loadingClassName || isControlled)
|
(loadingClassName || isControlled)
|
||||||
? 'opacity-100'
|
? 'opacity-100'
|
||||||
: isLoading ? 'opacity-50' : 'opacity-100',
|
: isLoading ? 'opacity-50' : 'opacity-100',
|
||||||
|
|||||||
@ -5,12 +5,12 @@ import { LuChevronsDownUp, LuChevronsUpDown } from 'react-icons/lu';
|
|||||||
export default function ScoreCardRow({
|
export default function ScoreCardRow({
|
||||||
icon,
|
icon,
|
||||||
content,
|
content,
|
||||||
additionalContent,
|
expandContent,
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
icon: ReactNode
|
icon: ReactNode
|
||||||
content: ReactNode
|
content: ReactNode
|
||||||
additionalContent?: ReactNode
|
expandContent?: ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const [isExpanded, setIsExpanded] = useState(false);
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
@ -33,10 +33,10 @@ export default function ScoreCardRow({
|
|||||||
</div>
|
</div>
|
||||||
{isExpanded &&
|
{isExpanded &&
|
||||||
<div className="text-medium">
|
<div className="text-medium">
|
||||||
{additionalContent}
|
{expandContent}
|
||||||
</div>}
|
</div>}
|
||||||
</div>
|
</div>
|
||||||
{additionalContent && <button
|
{expandContent && <button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => setIsExpanded(!isExpanded)}
|
onClick={() => setIsExpanded(!isExpanded)}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user