From 9aa6546b90e84484f6b2ab66ef92274c31fd11e9 Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Thu, 20 Jun 2024 19:25:15 -0500 Subject: [PATCH] Introduce official error/warning components --- src/admin/AdminAddAllUploads.tsx | 6 ++-- src/admin/AdminNavClient.tsx | 6 ++-- src/admin/AdminOutdatedClient.tsx | 6 ++-- src/app/admin/configuration/page.tsx | 6 ++-- src/app/tag/hidden/page.tsx | 6 ++-- src/auth/SignInForm.tsx | 6 ++-- src/components/ChecklistRow.tsx | 8 ++++- .../{InfoBlock.tsx => Container.tsx} | 14 +++++++-- src/components/ErrorNote.tsx | 27 +++++----------- src/components/{Banner.tsx => Note.tsx} | 31 ++++++++++--------- src/components/StatusIcon.tsx | 7 ++++- src/components/WarningNote.tsx | 22 +++++++++++++ src/photo/PhotosEmptyState.tsx | 6 ++-- src/site/SiteChecklistClient.tsx | 27 ++++++++++++---- 14 files changed, 113 insertions(+), 65 deletions(-) rename src/components/{InfoBlock.tsx => Container.tsx} (74%) rename src/components/{Banner.tsx => Note.tsx} (54%) create mode 100644 src/components/WarningNote.tsx diff --git a/src/admin/AdminAddAllUploads.tsx b/src/admin/AdminAddAllUploads.tsx index 0fc89731..ac5dbd47 100644 --- a/src/admin/AdminAddAllUploads.tsx +++ b/src/admin/AdminAddAllUploads.tsx @@ -2,7 +2,7 @@ import ErrorNote from '@/components/ErrorNote'; import FieldSetWithStatus from '@/components/FieldSetWithStatus'; -import InfoBlock from '@/components/InfoBlock'; +import Container from '@/components/Container'; import LoaderButton from '@/components/primitives/LoaderButton'; import { addAllUploadsAction } from '@/photo/actions'; import { PATH_ADMIN_PHOTOS } from '@/site/paths'; @@ -84,7 +84,7 @@ export default function AdminAddAllUploads({ <> {actionErrorMessage && {actionErrorMessage}} - +
}
- + ); } diff --git a/src/admin/AdminNavClient.tsx b/src/admin/AdminNavClient.tsx index e81b7377..98c08c64 100644 --- a/src/admin/AdminNavClient.tsx +++ b/src/admin/AdminNavClient.tsx @@ -1,6 +1,6 @@ 'use client'; -import Banner from '@/components/Banner'; +import Note from '@/components/Note'; import SiteGrid from '@/components/SiteGrid'; import { PATH_ADMIN_CONFIGURATION, @@ -96,10 +96,10 @@ export default function AdminNavClient({
{shouldShowBanner && - }> + }> Photo updates detected—they may take several minutes to show up for visitors - } + } } /> diff --git a/src/admin/AdminOutdatedClient.tsx b/src/admin/AdminOutdatedClient.tsx index 80b7db80..cfc19923 100644 --- a/src/admin/AdminOutdatedClient.tsx +++ b/src/admin/AdminOutdatedClient.tsx @@ -4,7 +4,7 @@ import { OUTDATED_THRESHOLD, Photo } from '@/photo'; import AdminPhotosTable from '@/admin/AdminPhotosTable'; import LoaderButton from '@/components/primitives/LoaderButton'; import IconGrSync from '@/site/IconGrSync'; -import Banner from '@/components/Banner'; +import Note from '@/components/Note'; import AdminChildPage from '@/components/AdminChildPage'; import { PATH_ADMIN_PHOTOS } from '@/site/paths'; import { useState } from 'react'; @@ -74,7 +74,7 @@ export default function AdminOutdatedClient({ } >
- +
{photos.length} {' '} @@ -88,7 +88,7 @@ export default function AdminOutdatedClient({ undesired privacy settings {hasAiTextGeneration && ', missing AI-generated text'}
-
+
- + - +
} /> ); diff --git a/src/app/tag/hidden/page.tsx b/src/app/tag/hidden/page.tsx index ae5b896e..cb890821 100644 --- a/src/app/tag/hidden/page.tsx +++ b/src/app/tag/hidden/page.tsx @@ -1,5 +1,5 @@ import AnimateItems from '@/components/AnimateItems'; -import Banner from '@/components/Banner'; +import Note from '@/components/Note'; import SiteGrid from '@/components/SiteGrid'; import PhotoGrid from '@/photo/PhotoGrid'; import { getPhotosNoStore } from '@/photo/cache'; @@ -63,9 +63,9 @@ export default async function HiddenTagPage() { animateOnFirstLoadOnly />
- + Only visible to authenticated admins - +
} diff --git a/src/auth/SignInForm.tsx b/src/auth/SignInForm.tsx index aa686618..b4b35fc4 100644 --- a/src/auth/SignInForm.tsx +++ b/src/auth/SignInForm.tsx @@ -1,7 +1,7 @@ 'use client'; import FieldSetWithStatus from '@/components/FieldSetWithStatus'; -import InfoBlock from '@/components/InfoBlock'; +import Container from '@/components/Container'; import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus'; import { useEffect, useLayoutEffect, useRef, useState } from 'react'; import { getAuthAction, signInAction } from './actions'; @@ -40,7 +40,7 @@ export default function SignInForm() { password.length > 0; return ( - @@ -89,6 +89,6 @@ export default function SignInForm() { - +
); } diff --git a/src/components/ChecklistRow.tsx b/src/components/ChecklistRow.tsx index b491a0e1..297a0962 100644 --- a/src/components/ChecklistRow.tsx +++ b/src/components/ChecklistRow.tsx @@ -8,6 +8,7 @@ export default function ChecklistRow({ status, isPending, optional, + showWarning, experimental, children, }: { @@ -15,6 +16,7 @@ export default function ChecklistRow({ status: boolean isPending?: boolean optional?: boolean + showWarning?: boolean experimental?: boolean children: ReactNode }) { @@ -24,7 +26,11 @@ export default function ChecklistRow({ 'px-4 pt-2 pb-2.5', )}>
diff --git a/src/components/InfoBlock.tsx b/src/components/Container.tsx similarity index 74% rename from src/components/InfoBlock.tsx rename to src/components/Container.tsx index 3d622ed7..3bf5bc13 100644 --- a/src/components/InfoBlock.tsx +++ b/src/components/Container.tsx @@ -1,7 +1,7 @@ import { clsx } from 'clsx/lite'; import { ReactNode } from 'react'; -export default function InfoBlock({ +export default function Container({ children, className, color = 'gray', @@ -11,7 +11,7 @@ export default function InfoBlock({ }: { children: ReactNode className?: string - color?: 'gray' | 'blue' + color?: 'gray' | 'blue' | 'red' | 'yellow' padding?: 'loose' | 'normal' | 'tight' centered?: boolean spaceChildren?: boolean @@ -28,6 +28,16 @@ export default function InfoBlock({ 'bg-blue-50/50 border-blue-200', 'dark:bg-blue-950/30 dark:border-blue-600/50', ]; + case 'red': return [ + 'text-red-600 dark:text-red-500/90', + 'bg-red-50/50 dark:bg-red-950/50', + 'border-red-100 dark:border-red-950', + ]; + case 'yellow': return [ + 'text-amber-700 dark:text-amber-500/90', + 'bg-amber-50/50 dark:bg-amber-950/30', + 'border-amber-200/80 dark:border-amber-800/30', + ]; } }; diff --git a/src/components/ErrorNote.tsx b/src/components/ErrorNote.tsx index e86a612e..934fa4af 100644 --- a/src/components/ErrorNote.tsx +++ b/src/components/ErrorNote.tsx @@ -1,33 +1,22 @@ -import { clsx } from 'clsx/lite'; import { ReactNode } from 'react'; import { BiErrorAlt } from 'react-icons/bi'; +import Note from './Note'; export default function ErrorNote({ className, children, - size = 'medium', }: { className?: string children: ReactNode - size?: 'small' | 'medium' }) { return ( -
- + } + > {children} -
+ ); } diff --git a/src/components/Banner.tsx b/src/components/Note.tsx similarity index 54% rename from src/components/Banner.tsx rename to src/components/Note.tsx index 62e140c7..e141a701 100644 --- a/src/components/Banner.tsx +++ b/src/components/Note.tsx @@ -1,38 +1,39 @@ -import { ReactNode } from 'react'; -import InfoBlock from './InfoBlock'; +import { ComponentProps, ReactNode } from 'react'; +import Container from './Container'; import AnimateItems from './AnimateItems'; import { IoInformationCircleOutline } from 'react-icons/io5'; -export default function Banner({ - icon, +export default function Note({ children, - animate, className, + color = 'blue', + icon, + animate, }: { icon?: ReactNode - children: ReactNode animate?: boolean - className?: string -}) { +} & ComponentProps) { return (
- {icon ?? } + + {icon ?? } + {children}
- , + , ]} animateOnFirstLoadOnly /> diff --git a/src/components/StatusIcon.tsx b/src/components/StatusIcon.tsx index 46dc94de..2021f2c0 100644 --- a/src/components/StatusIcon.tsx +++ b/src/components/StatusIcon.tsx @@ -9,7 +9,7 @@ export default function StatusIcon({ type, loading, }: { - type: 'checked' | 'missing' | 'optional' + type: 'checked' | 'missing' | 'warning' | 'optional' loading?: boolean }) { const getIcon = () => { @@ -24,6 +24,11 @@ export default function StatusIcon({ size={14} className="text-red-400 translate-x-[2px] translate-y-[1.5px]" />; + case 'warning': + return ; case 'optional': return } + > + {children} + + ); +} diff --git a/src/photo/PhotosEmptyState.tsx b/src/photo/PhotosEmptyState.tsx index 184a0428..c3b36166 100644 --- a/src/photo/PhotosEmptyState.tsx +++ b/src/photo/PhotosEmptyState.tsx @@ -1,5 +1,5 @@ import AdminCTA from '@/admin/AdminCTA'; -import InfoBlock from '@/components/InfoBlock'; +import Container from '@/components/Container'; import SiteGrid from '@/components/SiteGrid'; import { IS_SITE_READY } from '@/site/config'; import { PATH_ADMIN_CONFIGURATION } from '@/site/paths'; @@ -12,7 +12,7 @@ export default function PhotosEmptyState() { return ( @@ -47,7 +47,7 @@ export default function PhotosEmptyState() {
} -
} + } /> ); }; diff --git a/src/site/SiteChecklistClient.tsx b/src/site/SiteChecklistClient.tsx index def9f459..a460293a 100644 --- a/src/site/SiteChecklistClient.tsx +++ b/src/site/SiteChecklistClient.tsx @@ -14,7 +14,7 @@ import { BiLockAlt, BiPencil, } from 'react-icons/bi'; -import InfoBlock from '@/components/InfoBlock'; +import Container from '@/components/Container'; import Checklist from '@/components/Checklist'; import { toastSuccess } from '@/toast'; import { ConfigChecklistStatus } from './config'; @@ -25,6 +25,7 @@ import LoaderButton from '@/components/primitives/LoaderButton'; import { testConnectionsAction } from '@/admin/actions'; import ErrorNote from '@/components/ErrorNote'; import Spinner from '@/components/Spinner'; +import WarningNote from '@/components/WarningNote'; export default function SiteChecklistClient({ // Config checklist @@ -167,13 +168,27 @@ export default function SiteChecklistClient({ connection?: { provider: string, error: string } message?: string }) => - + {connection && <> {connection.provider} connection error: {`"${connection.error}"`} } {message} ; + const renderWarning = ({ + connection, + message, + }: { + connection?: { provider: string, error: string } + message?: string + }) => + + {connection && <> + {connection.provider} connection error: {`"${connection.error}"`} + } + {message} + ; + return (
@@ -277,7 +292,7 @@ export default function SiteChecklistClient({ Store auth secret in environment variable: {!hasAuthSecret &&
- +
@@ -288,7 +303,7 @@ export default function SiteChecklistClient({ {renderCopyButton('Secret', secret)}
- +
} {renderEnvVars(['AUTH_SECRET'])} @@ -314,8 +329,8 @@ export default function SiteChecklistClient({ status={hasDomain} > {!hasDomain && - renderError({message: - 'Not configuring a domain may cause ' + + renderWarning({message: + 'Not explicitly setting a domain may cause ' + 'certain features to behave unexpectedly', })} Store in environment variable (displayed in top-right nav):