(event: TechEvent)
| 148 | } |
| 149 | |
| 150 | private buildEvent(event: TechEvent): HTMLElement { |
| 151 | const startDate = new Date(event.startDate); |
| 152 | const endDate = new Date(event.endDate); |
| 153 | const now = new Date(); |
| 154 | |
| 155 | const isToday = startDate.toDateString() === now.toDateString(); |
| 156 | const isSoon = !isToday && startDate <= new Date(now.getTime() + 2 * 24 * 60 * 60 * 1000); |
| 157 | const isThisWeek = startDate <= new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000); |
| 158 | |
| 159 | const dateStr = startDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); |
| 160 | const endDateStr = endDate > startDate && endDate.toDateString() !== startDate.toDateString() |
| 161 | ? ` - ${endDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}` |
| 162 | : ''; |
| 163 | |
| 164 | const typeIcons: Record<string, string> = { |
| 165 | conference: '🎤', |
| 166 | earnings: '📊', |
| 167 | ipo: '🔔', |
| 168 | other: '📌', |
| 169 | }; |
| 170 | |
| 171 | const typeClasses: Record<string, string> = { |
| 172 | conference: 'type-conference', |
| 173 | earnings: 'type-earnings', |
| 174 | ipo: 'type-ipo', |
| 175 | other: 'type-other', |
| 176 | }; |
| 177 | |
| 178 | const className = [ |
| 179 | 'tech-event', |
| 180 | typeClasses[event.type], |
| 181 | isToday ? 'is-today' : '', |
| 182 | isSoon ? 'is-soon' : '', |
| 183 | isThisWeek ? 'is-this-week' : '', |
| 184 | ].filter(Boolean).join(' '); |
| 185 | |
| 186 | const safeEventUrl = sanitizeUrl(event.url || ''); |
| 187 | |
| 188 | return h('div', { className }, |
| 189 | h('div', { className: 'event-date' }, |
| 190 | h('span', { className: 'event-month' }, startDate.toLocaleDateString('en-US', { month: 'short' }).toUpperCase()), |
| 191 | h('span', { className: 'event-day' }, String(startDate.getDate())), |
| 192 | isToday ? h('span', { className: 'today-badge' }, t('components.techEvents.today')) : false, |
| 193 | isSoon ? h('span', { className: 'soon-badge' }, t('components.techEvents.soon')) : false, |
| 194 | ), |
| 195 | h('div', { className: 'event-content' }, |
| 196 | h('div', { className: 'event-header' }, |
| 197 | h('span', { className: 'event-icon' }, typeIcons[event.type] ?? '📌'), |
| 198 | h('span', { className: 'event-title' }, event.title), |
| 199 | safeEventUrl |
| 200 | ? h('a', { href: safeEventUrl, target: '_blank', rel: 'noopener', className: 'event-url', title: t('components.techEvents.moreInfo') }, '↗') |
| 201 | : false, |
| 202 | ), |
| 203 | h('div', { className: 'event-meta' }, |
| 204 | h('span', { className: 'event-dates' }, `${dateStr}${endDateStr}`), |
| 205 | event.location |
| 206 | ? h('span', { className: 'event-location' }, event.location) |
| 207 | : false, |
no test coverage detected