first commit
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user