diff --git a/admin/app/validators/curated_collections.ts b/admin/app/validators/curated_collections.ts index aa578f8..c765cd2 100644 --- a/admin/app/validators/curated_collections.ts +++ b/admin/app/validators/curated_collections.ts @@ -86,6 +86,7 @@ export const creatorPacksSpecSchema = vine.object({ video_count: vine.number().min(0), size_mb: vine.number().min(0), license_id: vine.string(), + banner_url: vine.string().url().optional(), poster_url: vine.string().url().optional(), logo_url: vine.string().url().optional(), }) diff --git a/admin/inertia/components/CreatorPackCard.tsx b/admin/inertia/components/CreatorPackCard.tsx index 7752169..820d0e4 100644 --- a/admin/inertia/components/CreatorPackCard.tsx +++ b/admin/inertia/components/CreatorPackCard.tsx @@ -1,3 +1,4 @@ +import { useState } from 'react' import { formatBytes } from '~/lib/util' import type { CreatorPackWithStatus } from '../../types/collections' import classNames from 'classnames' @@ -22,72 +23,75 @@ const CreatorPackCard: React.FC = ({ pack, selected, onCli const sizeBytes = pack.size_mb * 1024 * 1024 // Installed packs are inert unless a newer version is available (click = update). - // Selected (wizard) and available packs are always clickable. const clickable = selected || !isInstalled || hasUpdate - const highlighted = selected || isDownloading || (isInstalled && !hasUpdate) + // Prefer a catalog-supplied banner (future remote creators); otherwise the + // banner bundled with the app by pack id. Both are the branded 1060x175 art we + // build into the ZIM. Fall back to a simple header only if the image is absent. + const [bannerFailed, setBannerFailed] = useState(false) + const bannerSrc = pack.banner_url || `/creator-packs/${pack.id}.webp` + + const statusBadge = selected ? ( + + + Selected + + ) : isDownloading ? ( + + + Downloading + + ) : isInstalled ? ( + + + Installed + + ) : ( + + Install + + + ) + return (
{ if (!clickable) return onClick?.(pack) }} > -
-
-
- {pack.logo_url ? ( - - ) : ( - - )} -

{pack.name}

-
- {selected ? ( -
- - Selected -
- ) : isDownloading ? ( -
- - Downloading -
- ) : isInstalled ? ( -
- - Installed -
- ) : ( - - )} + {!bannerFailed ? ( + {pack.name} setBannerFailed(true)} + /> + ) : ( +
+ +

{pack.name}

-
- -

by {pack.creator}

- -

{pack.description}

- - {hasUpdate && ( - - Update available - )} -
-

- {pack.video_count} videos | {formatBytes(sizeBytes, 0)} -

+
+
+

+ {pack.video_count} videos · {formatBytes(sizeBytes, 0)} +

+ {hasUpdate && ( + + Update available + + )} +
+ {statusBadge}
) diff --git a/admin/inertia/components/CreatorPacksSection.tsx b/admin/inertia/components/CreatorPacksSection.tsx index 9b22183..3ee636f 100644 --- a/admin/inertia/components/CreatorPacksSection.tsx +++ b/admin/inertia/components/CreatorPacksSection.tsx @@ -58,7 +58,7 @@ const CreatorPacksSection: React.FC = () => {
{packs.length > 0 ? ( -
+
{packs.map((pack) => ( ))} diff --git a/admin/inertia/pages/easy-setup/index.tsx b/admin/inertia/pages/easy-setup/index.tsx index 6f75ea8..8701a18 100644 --- a/admin/inertia/pages/easy-setup/index.tsx +++ b/admin/inertia/pages/easy-setup/index.tsx @@ -1037,7 +1037,7 @@ export default function EasySetupWizard(props: {
{creatorPacks.length > 0 ? ( -
+
{creatorPacks.map((pack) => ( .webp`). The + * Kiwix grid icon is baked into the ZIM separately. + */ + banner_url?: string + /** Optional card art. */ poster_url?: string logo_url?: string }