fix: filter past MC events by start time, fix no-image card collapse, center cards

- isUpcoming() now checks startTime, not just date — past events hide after they start
- Cards without images get a gradient placeholder instead of collapsing to 0 height
- Merge two Reveal wrappers into one to prevent empty Reveal stuck at opacity 0
- Use flex layout with justify-center so single cards are centered

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-20 18:07:41 +03:00
parent 650f8dc719
commit b0c9a77474
11 changed files with 37 additions and 30 deletions

1
content_tmp.json Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -71,12 +71,17 @@ function calcDuration(slot: MasterClassSlot): string {
}
function isUpcoming(item: MasterClassItem): boolean {
const today = new Date();
today.setHours(0, 0, 0, 0);
const lastDate = (item.slots ?? [])
.map((s) => parseDate(s.date))
.reduce((a, b) => (a > b ? a : b), new Date(0));
return lastDate >= today;
const now = new Date();
return (item.slots ?? []).some((s) => {
const slotDate = parseDate(s.date);
if (s.startTime) {
const [h, m] = s.startTime.split(":").map(Number);
slotDate.setHours(h, m, 0, 0);
} else {
slotDate.setHours(23, 59, 59, 999);
}
return slotDate > now;
});
}
function MasterClassCard({
@@ -90,10 +95,11 @@ function MasterClassCard({
const slotsDisplay = formatSlots(item.slots);
return (
<div className="group relative flex flex-col overflow-hidden rounded-2xl bg-black">
{/* Full-bleed image */}
{item.image && (
<div className="group relative flex w-full max-w-sm flex-col overflow-hidden rounded-2xl bg-black">
{/* Full-bleed image or placeholder */}
<div className="relative aspect-[3/4] sm:aspect-[2/3] w-full overflow-hidden">
{item.image ? (
<>
<Image
src={item.image}
alt={item.title}
@@ -102,10 +108,12 @@ function MasterClassCard({
sizes="(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw"
className="object-cover transition-transform duration-700 group-hover:scale-110"
/>
{/* Dark overlay that intensifies on hover */}
<div className="absolute inset-0 bg-gradient-to-t from-black via-black/20 to-transparent opacity-80 transition-opacity duration-500 group-hover:opacity-90" />
</div>
</>
) : (
<div className="absolute inset-0 bg-gradient-to-b from-neutral-800 to-black" />
)}
</div>
{/* Content overlay at bottom */}
<div className="absolute inset-x-0 bottom-0 flex flex-col p-5 sm:p-6">
@@ -207,8 +215,8 @@ export function MasterClasses({ data }: MasterClassesProps) {
<SectionHeading centered>{data.title}</SectionHeading>
</Reveal>
{upcoming.length === 0 ? (
<Reveal>
{upcoming.length === 0 ? (
<div className="mt-10 py-12 text-center">
<p className="text-sm text-neutral-500 dark:text-white/40">
Следите за анонсами мастер-классов в нашем{" "}
@@ -222,10 +230,8 @@ export function MasterClasses({ data }: MasterClassesProps) {
</a>
</p>
</div>
</Reveal>
) : (
<Reveal>
<div className="mx-auto mt-10 grid max-w-5xl grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
<div className="mx-auto mt-10 flex max-w-5xl flex-wrap justify-center gap-5">
{upcoming.map((item) => (
<MasterClassCard
key={item.title}
@@ -234,8 +240,8 @@ export function MasterClasses({ data }: MasterClassesProps) {
/>
))}
</div>
</Reveal>
)}
</Reveal>
</div>
<SignupModal