148 lines
7.5 KiB
TypeScript
148 lines
7.5 KiB
TypeScript
import { useState } from 'react'
|
||
import { events } from '@/data/events'
|
||
import type { EventType } from '@/data/events'
|
||
import { useLang } from '@/context/LangContext'
|
||
|
||
type FilterValue = 'all' | EventType
|
||
|
||
const TYPE_BORDER_COLORS: Record<EventType, string> = {
|
||
conference: 'var(--ink)',
|
||
exhibition: '#6b4a00',
|
||
seminar: '#1a4a2a',
|
||
international: 'var(--red)',
|
||
}
|
||
|
||
const TYPE_LABELS = {
|
||
fa: { conference: 'کنگره', exhibition: 'نمایشگاه', seminar: 'سمینار', international: 'جهانی' },
|
||
en: { conference: 'Conference', exhibition: 'Exhibition', seminar: 'Seminar', international: 'International' },
|
||
}
|
||
|
||
const FILTERS = {
|
||
fa: [
|
||
{ label: 'همه', value: 'all' as FilterValue },
|
||
{ label: 'کنگره', value: 'conference' as FilterValue },
|
||
{ label: 'نمایشگاه', value: 'exhibition' as FilterValue },
|
||
{ label: 'سمینار', value: 'seminar' as FilterValue },
|
||
{ label: 'جهانی', value: 'international' as FilterValue },
|
||
],
|
||
en: [
|
||
{ label: 'All', value: 'all' as FilterValue },
|
||
{ label: 'Conference', value: 'conference' as FilterValue },
|
||
{ label: 'Exhibition', value: 'exhibition' as FilterValue },
|
||
{ label: 'Seminar', value: 'seminar' as FilterValue },
|
||
{ label: 'International', value: 'international' as FilterValue },
|
||
],
|
||
}
|
||
|
||
const T = {
|
||
fa: { heading: 'رویدادهای پیشرو', year: '۱۴۰۳–۱۴۰۴', empty: 'رویدادی در این دستهبندی یافت نشد' },
|
||
en: { heading: 'Upcoming Events', year: '1403–1404', empty: 'No events found in this category' },
|
||
}
|
||
|
||
function EventCard({ event, lang }: { event: (typeof events)[0]; lang: 'fa' | 'en' }) {
|
||
const [hovered, setHovered] = useState(false)
|
||
const borderColor = TYPE_BORDER_COLORS[event.type]
|
||
const typeLabels = TYPE_LABELS[lang]
|
||
|
||
return (
|
||
<article
|
||
style={{ border: '1px solid var(--rule-thin)', background: hovered ? 'var(--paper-2)' : 'var(--paper)', transition: 'background 140ms', display: 'flex', flexDirection: 'column' }}
|
||
onMouseEnter={() => setHovered(true)}
|
||
onMouseLeave={() => setHovered(false)}
|
||
>
|
||
<div style={{ display: 'flex', borderBottom: '2px solid var(--ink)' }}>
|
||
<div style={{ padding: '18px 24px', borderLeft: '1px solid var(--rule-thin)', minWidth: 100, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', background: 'var(--paper)', flexShrink: 0 }}>
|
||
<span style={{ fontSize: 52, fontWeight: 900, lineHeight: 1, color: 'var(--ink)', letterSpacing: '-2px', display: 'block' }}>
|
||
{event.date.toLocaleString(lang === 'fa' ? 'fa-IR' : 'en-US')}
|
||
</span>
|
||
<span style={{ fontSize: 11, fontWeight: 500, color: 'var(--ink-4)', marginTop: 4, display: 'block', whiteSpace: 'nowrap' }}>
|
||
{event.month} {event.year.toLocaleString(lang === 'fa' ? 'fa-IR' : 'en-US')}
|
||
</span>
|
||
</div>
|
||
<div style={{ flex: 1, padding: '18px 20px', display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 8 }}>
|
||
<span style={{ display: 'inline-block', alignSelf: 'flex-start', fontSize: 10, fontWeight: 700, letterSpacing: '1.2px', color: borderColor, border: `1px solid ${borderColor}`, padding: '2px 8px' }}>
|
||
{typeLabels[event.type]}
|
||
</span>
|
||
<h3 style={{ fontSize: 16, fontWeight: 800, lineHeight: 1.45, color: 'var(--ink)', letterSpacing: '-0.3px' }}>{event.title}</h3>
|
||
</div>
|
||
</div>
|
||
|
||
<div style={{ padding: '16px 20px', flex: 1, display: 'flex', flexDirection: 'column', gap: 10 }}>
|
||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||
<span style={{ width: 4, height: 4, background: 'var(--ink-4)', flexShrink: 0, display: 'inline-block' }} />
|
||
<span style={{ fontSize: 12, color: 'var(--ink-4)', fontWeight: 400 }}>{event.location}، {event.city}</span>
|
||
</div>
|
||
<p style={{ fontSize: 13, fontWeight: 300, color: 'var(--ink-3)', lineHeight: 1.8 }}>{event.description}</p>
|
||
</div>
|
||
|
||
<div style={{ borderTop: '1px solid var(--rule-thin)', padding: '12px 20px', display: 'flex', alignItems: 'center', justifyContent: 'flex-end' }}>
|
||
{event.registrationOpen ? (
|
||
<a href="#" style={{ fontSize: 13, fontWeight: 700, color: 'var(--red)', textDecoration: 'none', display: 'flex', alignItems: 'center', gap: 4 }}>
|
||
{lang === 'fa' ? 'ثبتنام' : 'Register'}<span style={{ fontSize: 15 }}>←</span>
|
||
</a>
|
||
) : (
|
||
<span style={{ fontSize: 11, fontWeight: 500, color: 'var(--ink-5)' }}>
|
||
{lang === 'fa' ? 'بهزودی' : 'Coming Soon'}
|
||
</span>
|
||
)}
|
||
</div>
|
||
</article>
|
||
)
|
||
}
|
||
|
||
export default function Events() {
|
||
const { lang } = useLang()
|
||
const t = T[lang]
|
||
const filters = FILTERS[lang]
|
||
const [activeFilter, setActiveFilter] = useState<FilterValue>('all')
|
||
|
||
const filtered = activeFilter === 'all' ? events : events.filter(e => e.type === activeFilter)
|
||
|
||
return (
|
||
<div style={{ maxWidth: 1280, margin: '0 auto', padding: '64px 48px', direction: lang === 'fa' ? 'rtl' : 'ltr' }}>
|
||
<div style={{ display: 'flex', alignItems: 'stretch', borderBottom: '3px solid var(--ink)', marginBottom: 36 }}>
|
||
<div style={{ background: 'var(--ink)', padding: '14px 28px', display: 'flex', alignItems: 'center' }}>
|
||
<h1 style={{ fontSize: 24, fontWeight: 900, color: 'var(--paper)', letterSpacing: '-0.5px', whiteSpace: 'nowrap' }}>{t.heading}</h1>
|
||
</div>
|
||
<div style={{ flex: 1 }} />
|
||
<div style={{ display: 'flex', alignItems: 'center', padding: '14px 0 14px 4px' }}>
|
||
<span style={{ fontSize: 13, fontWeight: 500, color: 'var(--ink-4)' }}>{t.year}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div style={{ display: 'flex', gap: 0, borderBottom: '1px solid var(--rule-thin)', marginBottom: 40, overflowX: 'auto', scrollbarWidth: 'none' }}>
|
||
{filters.map(f => {
|
||
const isActive = activeFilter === f.value
|
||
return (
|
||
<button
|
||
key={f.value}
|
||
onClick={() => setActiveFilter(f.value)}
|
||
style={{ padding: '10px 24px', fontSize: 13, fontWeight: isActive ? 700 : 500, border: 'none', background: isActive ? 'var(--ink)' : 'transparent', color: isActive ? 'var(--paper)' : 'var(--ink-4)', cursor: 'pointer', fontFamily: 'inherit', whiteSpace: 'nowrap', flexShrink: 0, transition: 'all 120ms', marginBottom: -1, outline: 'none' }}
|
||
>
|
||
{f.label}
|
||
{f.value !== 'all' && (
|
||
<span style={{ marginLeft: 6, fontSize: 10, color: isActive ? 'rgba(244,239,231,0.6)' : 'var(--ink-6)' }}>
|
||
({events.filter(e => e.type === f.value).length})
|
||
</span>
|
||
)}
|
||
</button>
|
||
)
|
||
})}
|
||
</div>
|
||
|
||
<div className="events-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 1, background: 'var(--rule-thin)' }}>
|
||
{filtered.map(event => <EventCard key={event.id} event={event} lang={lang} />)}
|
||
{filtered.length % 2 !== 0 && <div style={{ background: 'var(--paper)' }} />}
|
||
</div>
|
||
|
||
{filtered.length === 0 && (
|
||
<div style={{ padding: '80px 0', textAlign: 'center', color: 'var(--ink-5)' }}>
|
||
<p style={{ fontSize: 15, fontWeight: 300 }}>{t.empty}</p>
|
||
</div>
|
||
)}
|
||
|
||
<style>{`@media (max-width: 767px) { .events-grid { grid-template-columns: 1fr !important; } }`}</style>
|
||
</div>
|
||
)
|
||
}
|