/* certificate.jsx — Cisco Arena certificate + feedback gate
* Requires React 18 + Babel standalone.
* For PNG download, load html2canvas before this file:
*
*/
(function() {
var useState = React.useState;
var useMemo = React.useMemo;
function clamp(n, min, max) {
return Math.max(min, Math.min(max, n));
}
function fmtNum(n) {
return Number(n || 0).toLocaleString();
}
function ordinal(n) {
var v = Number(n || 0);
if (!v) return '—';
var mod10 = v % 10;
var mod100 = v % 100;
if (mod10 === 1 && mod100 !== 11) return v + 'st';
if (mod10 === 2 && mod100 !== 12) return v + 'nd';
if (mod10 === 3 && mod100 !== 13) return v + 'rd';
return v + 'th';
}
function rankLine(rank, totalPlayers, isWinner) {
if (isWinner || Number(rank) === 1) return '🏆 1st Place — Champion';
return ordinal(rank) + ' of ' + Number(totalPlayers || 0) + ' racers';
}
function podiumTone(rank, isWinner) {
if (isWinner || Number(rank) === 1) return 'winner';
if (Number(rank) === 2) return 'silver';
if (Number(rank) === 3) return 'bronze';
return 'finisher';
}
function certificateTitle(isWinner) {
return isWinner ? 'Certificate of Champions' : 'Certificate of Completion';
}
function shareCopy(props) {
var pieces = [
'I just completed the Cisco Arena ISE challenge',
props.rank && props.totalPlayers ? '— finished ' + ordinal(props.rank) + ' of ' + props.totalPlayers : '',
props.finalScore != null ? 'with ' + fmtNum(props.finalScore) + ' points!' : 'and unlocked my certificate!',
'🏆 #CiscoArena #ISE'
].filter(Boolean);
return pieces.join(' ');
}
var ArenaCertificate = React.forwardRef(function ArenaCertificate(props, ref) {
var tone = podiumTone(props.rank, props.isWinner);
var rootRef = props.certificateRef || ref;
var accentStyle = { '--team-accent': props.teamColor || '#00BCEB' };
var champion = !!props.isWinner || Number(props.rank) === 1;
return (
Cisco Arena
{props.eventName || 'Cisco Arena'}
{champion ? tc('🏆 CAMPEÓN') : tc('FINALISTA')}
{certificateTitle(champion)}
{props.playerName || 'Player Name'}
{tc('otorgado por completar')} {props.moduleName || 'Cisco Identity Services Engine'}
{tc('Equipo')}
{props.teamName || tc('Equipo Cisco Arena')}
{tc('Posición final')}
{rankLine(props.rank, props.totalPlayers, champion)}
{tc('Puntuación final')}
{fmtNum(props.finalScore)} pts
{tc('Fecha')}
{props.date || tc('Fecha del evento')}
Cisco Arena · {props.date || 'Event date'}
{champion ? '1' : ordinal(props.rank)}
of {props.totalPlayers || '—'} racers
);
});
function CertificateActions(props) {
var _status = useState('idle'), status = _status[0], setStatus = _status[1];
var shareText = useMemo(function() {
return shareCopy(props);
}, [props.playerName, props.rank, props.totalPlayers, props.finalScore, props.eventName]);
var shareUrl = props.shareUrl || 'https://www.cisco.com';
function resolveNode() {
if (!props.certificateRef) return null;
return props.certificateRef.current || props.certificateRef;
}
function downloadPng() {
var node = resolveNode();
if (!node) {
setStatus('missing-ref');
return;
}
if (typeof window.html2canvas !== 'function') {
setStatus('missing-lib');
return;
}
setStatus('rendering');
window.html2canvas(node, {
backgroundColor: null,
scale: Math.max(2, window.devicePixelRatio || 1),
useCORS: true
}).then(function(canvas) {
var link = document.createElement('a');
var safeName = String(props.playerName || 'player').replace(/[^a-z0-9]+/gi, '-').replace(/^-|-$/g, '').toLowerCase() || 'player';
link.href = canvas.toDataURL('image/png');
link.download = safeName + '-cisco-arena-certificate.png';
link.click();
setStatus('downloaded');
}).catch(function() {
setStatus('error');
});
}
function shareToLinkedIn() {
window.open('https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(shareUrl), '_blank', 'noopener,noreferrer');
}
function copyShareText() {
navigator.clipboard.writeText(shareText).then(function() {
setStatus('copied');
}).catch(function() {
setStatus('copy-error');
});
}
return (
{status === 'rendering' ? tc('Generando PNG del certificado…') : null}
{status === 'downloaded' ? tc('PNG descargado.') : null}
{status === 'copied' ? tc('Texto de LinkedIn copiado.') : null}
{status === 'missing-lib' ? tc('html2canvas no cargado.') : null}
{status === 'missing-ref' ? tc('Referencia del certificado no encontrada.') : null}
{status === 'error' ? tc('No se pudo generar el certificado.') : null}
{status === 'copy-error' ? tc('No se pudo copiar el texto.') : null}
{status === 'idle' ? tc('Descarga un PNG listo para redes o copia el texto para LinkedIn.') : null}
);
}
function StarPicker(props) {
return (
{[1,2,3,4,5].map(function(value) {
return (
);
})}
);
}
function FeedbackGate(props) {
var _form = useState({
stars: { experience: 0, content: 0, difficulty: 0 },
nps: null,
improve: '',
love: ''
}), form = _form[0], setForm = _form[1];
var _submit = useState('idle'), submitState = _submit[0], setSubmitState = _submit[1];
var isValid = form.stars.experience > 0 && form.stars.content > 0 && form.stars.difficulty > 0 && form.nps != null;
function setStar(key, value) {
setForm(function(prev) {
return Object.assign({}, prev, {
stars: Object.assign({}, prev.stars, { [key]: value })
});
});
}
function setField(key, value) {
setForm(function(prev) { return Object.assign({}, prev, { [key]: value }); });
}
function submit(e) {
e.preventDefault();
if (!isValid || !props.onSubmit) return;
setSubmitState('submitting');
Promise.resolve(props.onSubmit({
stars: {
experience: form.stars.experience,
content: form.stars.content,
difficulty: form.stars.difficulty
},
nps: form.nps,
improve: form.improve,
love: form.love
})).then(function() {
setSubmitState('done');
}).catch(function() {
setSubmitState('error');
});
}
return (
);
}
window.ArenaCertificate = ArenaCertificate;
window.CertificateActions = CertificateActions;
window.FeedbackGate = FeedbackGate;
})();