feat(client): event card

Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
2026-02-01 09:54:19 +08:00
committed by Asai Neko
parent e5c12b4cfe
commit cee71097af
5 changed files with 74 additions and 43 deletions

View File

@@ -53,6 +53,7 @@
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"culori": "^4.0.2", "culori": "^4.0.2",
"dayjs": "^1.11.19",
"immer": "^11.1.0", "immer": "^11.1.0",
"lodash-es": "^4.17.22", "lodash-es": "^4.17.22",
"lucide-react": "^0.562.0", "lucide-react": "^0.562.0",

View File

@@ -125,6 +125,9 @@ importers:
culori: culori:
specifier: ^4.0.2 specifier: ^4.0.2
version: 4.0.2 version: 4.0.2
dayjs:
specifier: ^1.11.19
version: 1.11.19
immer: immer:
specifier: ^11.1.0 specifier: ^11.1.0
version: 11.1.3 version: 11.1.3
@@ -2850,6 +2853,9 @@ packages:
resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
engines: {node: '>=12'} engines: {node: '>=12'}
dayjs@1.11.19:
resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
debug@4.4.3: debug@4.4.3:
resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
@@ -7765,6 +7771,8 @@ snapshots:
d3-timer@3.0.1: {} d3-timer@3.0.1: {}
dayjs@1.11.19: {}
debug@4.4.3: debug@4.4.3:
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3

View File

@@ -1,39 +0,0 @@
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
Card,
CardAction,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
export function EventCard({ type, coverImage, eventName, description, startTime, endTime }:
{
type: 'official' | 'party',
coverImage: string, eventName: string, description: string, startTime: string, endTime: string
}) {
return (
<Card className="relative mx-auto w-full max-w-sm pt-0">
<div className="absolute inset-0 z-30 aspect-video bg-black/35" />
<img
src="https://avatar.vercel.sh/shadcn1"
alt="Event cover"
className="relative z-20 aspect-video w-full object-cover brightness-60 grayscale dark:brightness-40"
/>
<CardHeader>
<CardAction>
<Badge variant="secondary">Featured</Badge>
</CardAction>
<CardTitle>Design systems meetup</CardTitle>
<CardDescription>
A practical talk on component APIs, accessibility, and shipping
faster.
</CardDescription>
</CardHeader>
<CardFooter>
<Button className="w-full">View Event</Button>
</CardFooter>
</Card>
)
}

View File

@@ -0,0 +1,52 @@
import dayjs from 'dayjs';
import { Calendar } from 'lucide-react';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import {
Card,
CardAction,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@/components/ui/card';
export function EventCardView({ type, coverImage, eventName, description, startTime, endTime }:
{
type: 'official' | 'party';
coverImage: string;
eventName: string;
description: string;
startTime: Date;
endTime: Date;
}) {
const startDayJs = dayjs(startTime);
const endDayJs = dayjs(endTime);
return (
<Card className="relative mx-auto w-full max-w-sm pt-0">
<div className="absolute inset-0 z-30 aspect-video bg-black/10" />
<img
src={coverImage}
alt="Event cover"
className="relative z-20 aspect-video w-full object-cover rounded-t-xl"
/>
<CardHeader>
<CardAction>
{type === 'official' ? <Badge variant="secondary">Official</Badge> : <Badge variant="destructive">Party</Badge>}
</CardAction>
<CardTitle>{eventName}</CardTitle>
<CardDescription className="flex flex-row items-center text-xs">
<Calendar className="size-4 mr-2" />
{`${startDayJs.format('YYYY/MM/DD')} - ${endDayJs.format('YYYY/MM/DD')}`}
</CardDescription>
<CardDescription className="mt-1">
{description}
</CardDescription>
</CardHeader>
<CardFooter>
<Button className="w-full"></Button>
</CardFooter>
</Card>
);
}

View File

@@ -1,12 +1,21 @@
import type { Meta, StoryObj } from '@storybook/react-vite'; import type { Meta, StoryObj } from '@storybook/react-vite';
import { EventCard } from '@/components/workbenchCards/event-card'; import { EventCardView } from '@/components/workbenchCards/event-card.view';
const meta = { const meta = {
title: 'Cards/EventCard', title: 'Cards/EventCard',
component: EventCard, component: EventCardView,
} satisfies Meta<typeof EventCard>; } satisfies Meta<typeof EventCardView>;
export default meta; export default meta;
type Story = StoryObj<typeof meta>; type Story = StoryObj<typeof meta>;
export const Primary: Story = {}; export const Primary: Story = {
args: {
type: 'official',
coverImage: "https://github.com/NixOS/nixos-artwork/blob/master/wallpapers/nix-wallpaper-watersplash.png?raw=true",
eventName: 'Nix CN Conference 26.05',
description: 'Event Description',
startTime: "2026-06-13T04:00:00.000Z",
endTime: "2026-06-14T04:00:00.000Z",
},
};