1
0
Fork 0

feat(structures): hero image header on structure detail page

When the structure type has an image, render it as a full-bleed
cover behind the page header with vertical and horizontal gradient
overlays; text shifts to white for contrast. Headers without an
image keep the previous plain layout.
This commit is contained in:
Jason Fraley 2026-08-25 12:34:35 -04:00
parent fff9e82d62
commit 802873c7fa

View file

@ -72,6 +72,12 @@ export default async function StructureDetailPage({ params }: StructureDetailPag
? convertLexicalToPlaintext({ data: structureType.description }) ? convertLexicalToPlaintext({ data: structureType.description })
: null; : null;
const image =
typeof structureType.image === "object" && structureType.image !== null
? structureType.image
: null;
const imageUrl = image?.url ?? null;
const totalValue = storedResources.reduce((sum, entry) => { const totalValue = storedResources.reduce((sum, entry) => {
const resource = entry.resource as Resource; const resource = entry.resource as Resource;
if (!resource || typeof resource === "number") return sum; if (!resource || typeof resource === "number") return sum;
@ -80,22 +86,49 @@ export default async function StructureDetailPage({ params }: StructureDetailPag
return ( return (
<div className="p-5 flex flex-col gap-6 max-w-7xl"> <div className="p-5 flex flex-col gap-6 max-w-7xl">
<div className="flex flex-col gap-3"> <header
className={`relative flex flex-col gap-3 overflow-hidden rounded-lg${
imageUrl ? " min-h-[280px] justify-between border border-border p-5" : ""
}`}
>
{imageUrl ? (
<>
<img
src={imageUrl}
alt={structure.name}
fetchPriority="high"
className="absolute inset-0 size-full object-cover"
/>
<div className="absolute inset-0 bg-gradient-to-b from-black/50 via-black/40 to-background" />
<div className="absolute inset-0 bg-gradient-to-r from-black/30 via-transparent to-black/40" />
</>
) : null}
<Link <Link
href="/logistics" href="/logistics"
className="text-sm text-muted-foreground hover:text-primary transition-colors flex items-center gap-1 w-fit" className="relative z-10 text-sm text-muted-foreground hover:text-primary transition-colors flex items-center gap-1 w-fit"
> >
<ArrowLeftIcon className="size-3" /> <ArrowLeftIcon className="size-3" />
Back to Structures Back to Structures
</Link> </Link>
<div className="flex items-start justify-between gap-4"> <div className="relative z-10 flex items-start justify-between gap-4">
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Building2Icon className="size-5 text-muted-foreground" /> <Building2Icon
<h1 className="text-lg font-semibold">{structure.name}</h1> className={`size-5 ${imageUrl ? "text-white/80" : "text-muted-foreground"}`}
/>
<h1
className={`text-lg font-semibold ${imageUrl ? "text-white drop-shadow-md" : ""}`}
>
{structure.name}
</h1>
</div> </div>
<div className="flex items-center gap-2 text-sm text-muted-foreground"> <div
className={`flex items-center gap-2 text-sm ${
imageUrl ? "text-white/70" : "text-muted-foreground"
}`}
>
<span>{structureType.name}</span> <span>{structureType.name}</span>
<span>·</span> <span>·</span>
<span>{getCategoryLabel(structureType.category)}</span> <span>{getCategoryLabel(structureType.category)}</span>
@ -116,7 +149,7 @@ export default async function StructureDetailPage({ params }: StructureDetailPag
{user && <ManageStorageButton structureId={structure.id} />} {user && <ManageStorageButton structureId={structure.id} />}
</div> </div>
</div> </div>
</div> </header>
{description && <p className="text-sm text-muted-foreground">{description}</p>} {description && <p className="text-sm text-muted-foreground">{description}</p>}