import {useState} from 'react'; import {Image} from '@shopify/hydrogen'; import {Link} from 'react-router'; import {motion} from 'motion/react'; import type {CSSProperties} from 'react'; import type {AbiirJournalIndexCms, AbiirJournalPostCms} from '~/lib/journal'; import {abiirJournalImageIsRemote, MOCK_ABIIR_JOURNAL_INDEX} from '~/lib/journal'; import type {NewsletterSectionCms} from '~/lib/newsletter'; import {Newsletter} from '~/components/Newsletter'; import {PAGE_TITLE_TYPOGRAPHY_CLASS} from '~/lib/pageTitle'; import iconClockFeatured from '~/assets/journal/icon-clock-featured.svg'; import iconClockGrid from '~/assets/journal/icon-clock-grid.svg'; import iconReadArrow from '~/assets/journal/icon-read-arrow.svg'; /** * Bundle: journal index singleton + all posts. * Use with `context.sanity.loadQuery(JOURNAL_PAGE_GROQ)` or `useQuery`. */ export const JOURNAL_PAGE_GROQ = `{ "index": *[_type == "journalIndex" && _id == "journalIndex"][0]{ seo{ title, description }, eyebrow, headline, description, categoryFilters }, "posts": *[_type == "journalPost"] | order(_updatedAt desc) { "slug": slug.current, title, subtitle, excerpt, cardExcerpt, content, author, authorRole, date, readTime, category, featured, tags, image{ alt, asset->{ _id, url, metadata{dimensions{width,height}} } } } }` as const; export {MOCK_ABIIR_JOURNAL_INDEX}; export type {AbiirJournalIndexCms, AbiirJournalPostCms}; const CHARCOAL = '#1a1a18'; const ACCENT = '#c17d5c'; const MUTED = '#5a5550'; const MUTED_MID = '#7a7068'; const CREAM_TEXT = '#f8f4ef'; const HERO_BG = '#ede8e0'; const BORDER_HAIRLINE = 'rgba(26,26,24,0.08)'; const BORDER_BTN = 'rgba(26,26,24,0.15)'; const DOT_MUTED = 'rgba(26,26,24,0.15)'; function gridCardTeaser(post: AbiirJournalPostCms) { if (post.cardExcerpt) return post.cardExcerpt; const t = post.excerpt.trim(); return t.length > 100 ? `${t.slice(0, 97).trim()}...` : t; } /** * Hydrogen `Image` with numeric `width`/`height` uses FixedWidthImage and fixed `` dimensions. * Pairing that with `absolute inset-0` broke rendering. Use fluid images (`width="100%"`) + `aspectRatio`. */ function JournalPostImage({ url, alt, className, sizes, style, aspectRatio, }: { url: string; alt: string; className?: string; sizes: string; style?: CSSProperties; aspectRatio: '4/3' | '8/5'; }) { const imgClass = `block size-full object-cover ${className ?? ''}`; if (abiirJournalImageIsRemote(url)) { return ( {alt} src ?? ''} sizes={sizes} style={style} width="100%" /> ); } return ( {alt} ); } export type JournalPageProps = { indexCms: AbiirJournalIndexCms; postsCms: AbiirJournalPostCms[]; newsletterCms: NewsletterSectionCms; }; export function JournalPage({indexCms, postsCms, newsletterCms}: JournalPageProps) { const [activeCategory, setActiveCategory] = useState('All'); const blogCategories = indexCms.categoryFilters; const filtered = activeCategory === 'All' ? postsCms : postsCms.filter((p) => p.category === activeCategory); const featured = postsCms.filter((p) => p.featured); const rest = filtered.filter( (p) => !p.featured || activeCategory !== 'All', ); return (

{indexCms.eyebrow}

{indexCms.headline}

{indexCms.description}

{activeCategory === 'All' && (
{featured.map((post, i) => (
{post.category}
{post.readTime}

{post.title}

{post.excerpt}

))}
)}
{blogCategories.map((cat) => { const active = activeCategory === cat; return ( ); })}
{(activeCategory === 'All' ? rest : filtered).map((post, i) => (
{post.category} ยท
{post.readTime}

{post.title}

{gridCardTeaser(post)}

{post.date} Read More
))}
); }