- Fixed Challenge Modal overlap issue by adjusting the main stacking context in App.tsx

- Implemented "click-outside-to-close" functionality for both the Challenge Modal and User Dropdown
- Added protocol-specific action buttons for challenges: "Open in new tab" for HTTP and "Copy to clipboard" for NC
- Enhanced Scoreboard rankings with significantly larger, consistent font sizes (text-2xl) for better readability
- Rebranded "TEAM_IDENTIFIER" to "PLAYER" and "TOTAL_POINTS" to "POINTS" across the platform (Scoreboard, Matrix, User Menu)
- Updated navigation: Renamed "SCOREBOARD" to "SCORES" in the nav bar and dynamic page titles
- Modernized User Dropdown menu with a dedicated "PLAYER" header and "LOGOUT" action
- Improved Score Matrix and Score Graph titles for consistency with the new "Player" terminology
- Added CAPTCHA human verification (svg-captcha) to Login and Registration flows for enhanced security
- Optimized frontend assets by migrating Tailwind and JetBrains Mono to local hosting
- Refactored Admin panel: Renamed "Operators" to "Users" and improved layout alignment
This commit is contained in:
m0rph3us1987
2026-03-11 17:47:46 +01:00
parent 27566a7813
commit 0d07264788
20 changed files with 753 additions and 489 deletions

View File

@@ -1,6 +1,6 @@
import React, { useState, useMemo } from 'react';
import { X, CheckCircle2, Download, Globe, Sparkles, Heart, Clock, Bell } from 'lucide-react';
import { X, CheckCircle2, Download, Globe, Sparkles, Heart, Clock, Bell, Copy, ExternalLink } from 'lucide-react';
import { useCTF } from './CTFContext';
import { Challenge, Difficulty } from './types';
import { CATEGORIES } from './constants';
@@ -43,8 +43,8 @@ const ChallengeModal: React.FC<{ challenge: Challenge; onClose: () => void; }> =
const difficultyColor = challenge.difficulty === 'Low' ? 'text-[#00ff00]' : challenge.difficulty === 'Medium' ? 'text-[#ffaa00]' : 'text-[#ff0000]';
return (
<div className="fixed inset-0 bg-black/95 z-[300] flex items-center justify-center p-4">
<div className="hxp-border border-4 max-w-2xl w-full bg-black p-8 relative overflow-y-auto max-h-[90vh] custom-scrollbar">
<div className="fixed inset-0 bg-black/95 z-[300] flex items-center justify-center p-4" onClick={onClose}>
<div className="hxp-border border-4 max-w-2xl w-full bg-black p-8 relative overflow-y-auto max-h-[90vh] custom-scrollbar" onClick={(e) => e.stopPropagation()}>
<button onClick={onClose} className="absolute top-4 right-4 bg-[#ff0000] text-black font-black p-2 hover:bg-white transition-colors"><X size={24} /></button>
<div className="mb-8">
<h3 className="text-4xl font-black italic tracking-tighter text-white mb-2">{challenge.title}</h3>
@@ -57,7 +57,34 @@ const ChallengeModal: React.FC<{ challenge: Challenge; onClose: () => void; }> =
{connectionDetails && (
<div className="mb-8 p-4 hxp-border border-[#00ccff] bg-[#00ccff]/5">
<h4 className="text-[10px] font-black text-[#00ccff] uppercase tracking-[0.2em] mb-2 flex items-center gap-2"><Globe size={12}/> Connect to:</h4>
<code className="block bg-black p-3 text-[#00ccff] font-black text-sm border border-[#00ccff]/30 break-all select-all">{connectionDetails}</code>
<div className="flex gap-2">
<code className="flex-1 bg-black p-3 text-[#00ccff] font-black text-sm border border-[#00ccff]/30 break-all select-all">{connectionDetails}</code>
{(challenge.connectionType || 'nc') === 'nc' ? (
<button
onClick={() => {
navigator.clipboard.writeText(connectionDetails);
const btn = document.activeElement as HTMLButtonElement;
const originalContent = btn.innerHTML;
btn.innerHTML = 'COPIED!';
setTimeout(() => { btn.innerHTML = originalContent; }, 2000);
}}
className="bg-[#00ccff] text-black p-3 hover:bg-white transition-colors flex items-center justify-center min-w-[50px]"
title="Copy to clipboard"
>
<Copy size={20} />
</button>
) : (
<a
href={connectionDetails}
target="_blank"
rel="noopener noreferrer"
className="bg-[#00ccff] text-black p-3 hover:bg-white transition-colors flex items-center justify-center min-w-[50px]"
title="Open in new tab"
>
<ExternalLink size={20} />
</a>
)}
</div>
</div>
)}
@@ -87,7 +114,7 @@ const ChallengeModal: React.FC<{ challenge: Challenge; onClose: () => void; }> =
) : <p className="text-center font-bold text-red-500 mb-10 uppercase tracking-widest">PLEASE_SIGN_IN_TO_SOLVE</p>}
<div className="border-t-4 border-[#333] pt-6">
<h4 className="text-sm font-black text-white tracking-[0.2em] mb-4 flex items-center gap-2 uppercase"><Sparkles size={14} /> SOLVER_LOG</h4>
<h4 className="text-sm font-black text-white tracking-[0.2em] mb-4 flex items-center gap-2 uppercase"><Sparkles size={14} /> SOLVES</h4>
<div className="space-y-1">
{(challenge.solves || []).length > 0 ? (
state.solves.filter(s => s.challengeId === challenge.id).sort((a, b) => a.timestamp - b.timestamp).map((solve, idx) => {
@@ -100,7 +127,7 @@ const ChallengeModal: React.FC<{ challenge: Challenge; onClose: () => void; }> =
<div key={solve.teamId + idx} className="flex justify-between items-center p-3 bg-white/5 border border-white/10 text-[10px] font-bold">
<div className="flex items-center gap-3">
<span className={`w-5 h-5 flex items-center justify-center font-black ${idx === 0 ? 'bg-[#ffaa00] text-black' : idx === 1 ? 'bg-slate-400 text-black' : idx === 2 ? 'bg-[#cd7f32] text-black' : 'bg-white/10 text-slate-500'}`}>{idx + 1}</span>
<span className="text-white text-xs uppercase italic">{team?.name}</span>
<span className="text-white text-xs font-black">{team?.name}</span>
{bonus > 0 && <span className="text-[#00ff00] animate-pulse">+{bonus} BONUS</span>}
</div>
<div className="flex items-center gap-4">
@@ -170,8 +197,8 @@ export const ChallengeList: React.FC = () => {
return (
<div className="max-w-4xl mx-auto py-32 text-center hxp-border border-4 p-12">
<Clock className="w-20 h-20 text-slate-500 mx-auto mb-6" />
<h2 className="text-4xl font-black italic mb-4 uppercase">{now > endTime ? 'MISSION_COMPLETE' : 'BOARD_PAUSED'}</h2>
<p className="text-slate-500 font-bold tracking-widest uppercase">{now > endTime ? 'The operation timeframe has concluded.' : 'Waiting for HQ clearance...'}</p>
<h2 className="text-4xl font-black italic mb-4 uppercase">{now > endTime ? 'TIME IS UP' : 'BOARD_PAUSED'}</h2>
<p className="text-slate-500 font-bold tracking-widest uppercase">{now > endTime ? 'THE EVENT HAS CONCLUDED' : 'Waiting for HQ clearance...'}</p>
</div>
);
}