98 lines
3.8 KiB
TypeScript
98 lines
3.8 KiB
TypeScript
import { Box, Grid, Heading, HStack, Image, Stack, Text } from "@chakra-ui/react";
|
|
import { MapPin } from "lucide-react";
|
|
import type { CaseStudy } from "../../db/schema";
|
|
import { Section } from "./Section";
|
|
import { SectionHeading } from "./SectionHeading";
|
|
|
|
export function Cases({ items }: { items: CaseStudy[] }) {
|
|
if (items.length === 0) return null;
|
|
const [featured, ...rest] = items;
|
|
|
|
return (
|
|
<Section id="cases" tone="white">
|
|
<SectionHeading
|
|
eyebrow="完成案例"
|
|
title="真實村屋工程案例"
|
|
description="我哋為香港各區村屋提供專業太陽能方案,累積豐富施工經驗。"
|
|
/>
|
|
<Stack gap={{ base: 12, md: 16 }}>
|
|
{/* 首案例 featured:雜誌式大圖排版 */}
|
|
<Grid
|
|
templateColumns={{ base: "1fr", lg: "1.2fr 0.8fr" }}
|
|
gap={{ base: 5, lg: 10 }}
|
|
alignItems="end"
|
|
role="group"
|
|
>
|
|
<Box overflow="hidden">
|
|
<Image
|
|
src={featured.imageUrl ?? undefined}
|
|
alt={featured.title}
|
|
w="full"
|
|
aspectRatio={{ base: "4 / 3", lg: "16 / 10" }}
|
|
objectFit="cover"
|
|
transition="transform .5s"
|
|
_groupHover={{ transform: "scale(1.03)" }}
|
|
/>
|
|
</Box>
|
|
<Stack gap="3">
|
|
<HStack gap="3" fontSize="xs" letterSpacing="0.1em" color="fg.muted" fontWeight="medium">
|
|
<Text>{featured.completedAt}</Text>
|
|
{featured.location && (
|
|
<HStack gap="1" color="brand.700">
|
|
<MapPin size={12} />
|
|
<Text>{featured.location}</Text>
|
|
</HStack>
|
|
)}
|
|
</HStack>
|
|
<Heading as="h3" fontSize={{ base: "xl", md: "2xl" }} fontWeight="700" color="ink" lineHeight="1.35" _groupHover={{ color: "brand.700" }} transition="color .2s">
|
|
{featured.title}
|
|
</Heading>
|
|
{featured.description && (
|
|
<Text fontSize="sm" color="fg.muted" lineHeight="1.9">
|
|
{featured.description}
|
|
</Text>
|
|
)}
|
|
</Stack>
|
|
</Grid>
|
|
|
|
{rest.length > 0 && (
|
|
<Grid templateColumns={{ base: "1fr", md: "repeat(2, 1fr)", lg: `repeat(${Math.min(rest.length, 3)}, 1fr)` }} gap={{ base: 10, md: 8 }}>
|
|
{rest.map((item) => (
|
|
<Stack key={item.id} gap="3" role="group" borderTopWidth="2px" borderColor="ink" pt="5">
|
|
<Box overflow="hidden">
|
|
<Image
|
|
src={item.imageUrl ?? undefined}
|
|
alt={item.title}
|
|
w="full"
|
|
aspectRatio="4 / 3"
|
|
objectFit="cover"
|
|
transition="transform .5s"
|
|
_groupHover={{ transform: "scale(1.03)" }}
|
|
/>
|
|
</Box>
|
|
<HStack gap="3" fontSize="xs" letterSpacing="0.1em" color="fg.muted" fontWeight="medium">
|
|
<Text>{item.completedAt}</Text>
|
|
{item.location && (
|
|
<HStack gap="1" color="brand.700">
|
|
<MapPin size={12} />
|
|
<Text>{item.location}</Text>
|
|
</HStack>
|
|
)}
|
|
</HStack>
|
|
<Heading as="h3" fontSize="lg" fontWeight="700" color="ink" lineHeight="1.4" _groupHover={{ color: "brand.700" }} transition="color .2s">
|
|
{item.title}
|
|
</Heading>
|
|
{item.description && (
|
|
<Text fontSize="sm" color="fg.muted" lineHeight="1.8">
|
|
{item.description}
|
|
</Text>
|
|
)}
|
|
</Stack>
|
|
))}
|
|
</Grid>
|
|
)}
|
|
</Stack>
|
|
</Section>
|
|
);
|
|
}
|