/**
* Highlights Panel — shows key match moments with video thumbnails
*/
import { escapeHTML, setHTML } from '../utils/dom.js';
import { IMAGE_BASE, resolveVideo, VIDEO_BASE } from '../utils/media.js';
export class HighlightsPanel {
constructor(containerEl) {
this.containerEl = containerEl;
this._onClick = null;
}
render(events) {
if (!this.containerEl) return;
const keyEvents = events.filter(e =>
e.isKeyMoment || e.type === 'goal' || e.type === 'penalty_awarded' ||
e.type === 'red_card' || e.type === 'shot' || e.type === 'substitution'
);
const videoThumbs = {
goal: `${VIDEO_BASE}/highlight-goal.mp4`,
penalty_awarded: `${VIDEO_BASE}/football-goal-2.mp4`,
red_card: `${VIDEO_BASE}/match-action.mp4`,
shot: `${VIDEO_BASE}/highlight-shot.mp4`,
substitution: `${VIDEO_BASE}/football-match-2.mp4`,
foul: `${VIDEO_BASE}/football-match-1.mp4`,
};
const imageThumbs = {
goal: `${IMAGE_BASE}/crowd-fans.jpg`,
penalty_awarded: `${IMAGE_BASE}/stadium-night.jpg`,
red_card: `${IMAGE_BASE}/match-action.jpg`,
shot: `${IMAGE_BASE}/pitch-green.jpg`,
substitution: `${IMAGE_BASE}/stadium-aerial.jpg`,
};
setHTML(this.containerEl, `
${keyEvents.length} key moments — tap to replay with AI commentary
${keyEvents.map(ev => {
const type = this._safeEventType(ev.type);
const id = Number.isFinite(Number(ev.id)) ? Number(ev.id) : '';
const minute = escapeHTML(ev.minute ?? '');
const details = escapeHTML(ev.details || 'Match highlight');
const player = escapeHTML(ev.player || '');
const team = escapeHTML(ev.team || '');
return `
${minute}'
${this._typeLabel(type)}
${details}
${player ? `
${player} · ${team}
` : ''}
`;
}).join('')}
`);
// Hover to play video preview
this.containerEl.querySelectorAll('.highlight-card').forEach(card => {
const video = card.querySelector('video');
if (!video) return;
card.addEventListener('mouseenter', () => {
if (video.dataset.src && !video.src) {
const resolved = resolveVideo(video.dataset.src);
if (resolved) video.src = resolved;
}
video.play().catch(() => {});
});
card.addEventListener('mouseleave', () => {
video.pause();
video.currentTime = 0;
});
// Click handler
const handler = () => {
const id = parseInt(card.dataset.eventId, 10);
const event = events.find(e => e.id === id);
if (event && this._onClick) this._onClick(event);
};
card.addEventListener('click', handler);
card.addEventListener('keydown', (e) => { if (e.key === 'Enter') handler(); });
});
}
onHighlightClick(fn) {
this._onClick = fn;
}
_typeLabel(type) {
const labels = {
goal: '⚽ Goal',
penalty_awarded: '⚠️ Penalty',
red_card: '🟥 Red Card',
yellow_card: '🟨 Yellow Card',
shot: '🎯 Shot',
substitution: '🔄 Substitution',
foul: '⚡ Foul',
corner: '🚩 Corner',
half_time: '⏱️ Half Time',
extra_time: '⏰ Extra Time',
penalty_shootout: '🥅 Shootout',
kickoff: '🏁 Kickoff',
second_half: '🔄 Second Half',
possession: '📊 Possession',
};
return labels[type] || type;
}
_safeEventType(type = '') {
const safe = String(type).toLowerCase().replace(/[^a-z_]/g, '');
const allowed = [
'goal',
'penalty_awarded',
'red_card',
'yellow_card',
'shot',
'substitution',
'foul',
'corner',
'half_time',
'extra_time',
'penalty_shootout',
'kickoff',
'second_half',
'possession',
];
return allowed.includes(safe) ? safe : 'possession';
}
}