|
1 | 1 | --- |
| 2 | +import Badge from "./Badge.astro"; |
| 3 | +import Avatar from "./Avatar.astro"; |
2 | 4 | import { urlForImage } from "@/utils/sanity"; |
3 | 5 |
|
4 | 6 | interface Props { |
5 | 7 | title: string; |
6 | | - slug: string; |
7 | | - coverImage?: any; |
8 | | - excerpt?: string; |
9 | | - date?: string; |
| 8 | + url: string; |
| 9 | + type: "blog" | "podcast" | "course" | "video" | "short"; |
| 10 | + thumbnail?: any; |
| 11 | + duration?: string; |
| 12 | + metadata?: string; |
| 13 | + authorName?: string; |
| 14 | + authorImage?: any; |
10 | 15 | } |
11 | 16 |
|
12 | | -const { title, slug, coverImage, excerpt, date } = Astro.props; |
| 17 | +const { title, url, type, thumbnail, duration, metadata, authorName, authorImage } = Astro.props; |
13 | 18 |
|
14 | | -const imageUrl = coverImage |
15 | | - ? urlForImage(coverImage).width(640).height(360).format("webp").url() |
16 | | - : null; |
17 | | -
|
18 | | -const formattedDate = date |
19 | | - ? new Date(date).toLocaleDateString("en-US", { |
20 | | - year: "numeric", |
21 | | - month: "short", |
22 | | - day: "numeric", |
23 | | - }) |
24 | | - : null; |
| 19 | +const thumbnailUrl = thumbnail ? urlForImage(thumbnail).width(640).height(360).url() : undefined; |
| 20 | +const authorImageUrl = authorImage ? urlForImage(authorImage).width(48).height(48).url() : undefined; |
25 | 21 | --- |
26 | 22 |
|
27 | | -<a href={slug} class="group block rounded-lg border hover:border-blue-500 transition-colors overflow-hidden"> |
28 | | - {imageUrl && ( |
29 | | - <img |
30 | | - src={imageUrl} |
31 | | - alt={title} |
32 | | - width={640} |
33 | | - height={360} |
34 | | - class="w-full aspect-video object-cover" |
35 | | - loading="lazy" |
36 | | - /> |
| 23 | +<a href={url} class="group block rounded-xl border border-[--border] bg-[--surface] overflow-hidden transition-all duration-200 hover:border-[--border-hover] hover:shadow-[--shadow-glow] hover:-translate-y-0.5"> |
| 24 | + {/* Thumbnail */} |
| 25 | + {thumbnailUrl && ( |
| 26 | + <div class="relative aspect-video overflow-hidden"> |
| 27 | + <img |
| 28 | + src={thumbnailUrl} |
| 29 | + alt={title} |
| 30 | + class="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105" |
| 31 | + loading="lazy" |
| 32 | + /> |
| 33 | + {duration && ( |
| 34 | + <span class="absolute bottom-2 right-2 bg-black/80 text-white text-xs font-mono px-1.5 py-0.5 rounded-sm"> |
| 35 | + {duration} |
| 36 | + </span> |
| 37 | + )} |
| 38 | + </div> |
37 | 39 | )} |
38 | | - <div class="p-4"> |
39 | | - <h3 class="font-semibold text-lg group-hover:text-blue-600 transition-colors line-clamp-2"> |
| 40 | + |
| 41 | + {/* Content */} |
| 42 | + <div class="p-4 space-y-2"> |
| 43 | + {/* Badge + Metadata Row */} |
| 44 | + <div class="flex items-center gap-2 text-xs"> |
| 45 | + <Badge type={type} /> |
| 46 | + {metadata && <span class="text-[--text-tertiary]">{metadata}</span>} |
| 47 | + </div> |
| 48 | + |
| 49 | + {/* Title */} |
| 50 | + <h3 class="font-semibold text-[--text] line-clamp-2 group-hover:text-primary transition-colors"> |
40 | 51 | {title} |
41 | 52 | </h3> |
42 | | - {excerpt && ( |
43 | | - <p class="text-gray-600 text-sm mt-2 line-clamp-2">{excerpt}</p> |
44 | | - )} |
45 | | - {formattedDate && ( |
46 | | - <time datetime={date} class="text-gray-400 text-xs mt-2 block">{formattedDate}</time> |
| 53 | + |
| 54 | + {/* Author */} |
| 55 | + {authorName && ( |
| 56 | + <div class="flex items-center gap-2"> |
| 57 | + <Avatar src={authorImageUrl} name={authorName} size="xs" /> |
| 58 | + <span class="text-sm text-[--text-secondary]">{authorName}</span> |
| 59 | + </div> |
47 | 60 | )} |
48 | 61 | </div> |
49 | 62 | </a> |
0 commit comments