- 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

@@ -23,7 +23,7 @@ export const Admin: React.FC = () => {
return [...state.challenges].sort((a, b) => a.title.localeCompare(b.title));
}, [state.challenges]);
const sortedOperators = useMemo(() => {
const sortedUsers = useMemo(() => {
return [...state.teams]
.filter(t => t.id !== 'admin-0')
.sort((a, b) => (a.name || '').localeCompare(b.name || '', undefined, { sensitivity: 'base' }));
@@ -107,8 +107,7 @@ export const Admin: React.FC = () => {
return (
<div className="max-w-6xl mx-auto py-12 px-6">
<div className="flex flex-wrap gap-4 justify-between items-center mb-12 border-b-4 border-[#ff0000] pb-6">
<h2 className="text-5xl font-black italic text-white uppercase tracking-tighter">ADMIN_CONSOLE</h2>
<div className="flex flex-wrap gap-4 justify-end items-center mb-12 border-b-4 border-[#ff0000] pb-6">
<div className="flex gap-4 items-center">
{isBeforeStart && (
<div className="px-4 py-2 hxp-border-purple bg-[#bf00ff]/10 hidden sm:flex items-center gap-3">
@@ -256,19 +255,19 @@ export const Admin: React.FC = () => {
</section>
<section className="space-y-6">
<h3 className="text-2xl font-black text-[#bf00ff] border-b-2 border-[#bf00ff] uppercase italic">OPERATORS</h3>
<h3 className="text-2xl font-black text-[#bf00ff] border-b-2 border-[#bf00ff] uppercase italic">USERS</h3>
<div className="hxp-border border-2 overflow-hidden bg-black">
<table className="w-full text-[10px] font-black">
<thead className="bg-[#333] uppercase">
<tr>
<th className="p-3 text-left">TEAM_IDENTIFIER</th>
<th className="p-3 text-left">USER_IDENTIFIER</th>
<th className="p-3 text-center">ROLE</th>
<th className="p-3 text-center">STATUS</th>
<th className="p-3 text-right">ACTIONS</th>
</tr>
</thead>
<tbody className="divide-y divide-white/10 italic">
{sortedOperators.map(team => (
{sortedUsers.map(team => (
<tr key={team.id} className="hover:bg-white/5 transition-colors group">
<td className="p-3 text-white text-sm">
<div className="flex items-center gap-2">
@@ -283,7 +282,7 @@ export const Admin: React.FC = () => {
title={team.isAdmin ? "Revoke Admin Privileges" : "Grant Admin Privileges"}
>
{team.isAdmin ? <ShieldCheck size={12} /> : <Shield size={12} />}
<span>{team.isAdmin ? 'ADMIN' : 'OPERATOR'}</span>
<span>{team.isAdmin ? 'ADMIN' : 'USER'}</span>
</button>
</td>
<td className="p-3 text-center uppercase">
@@ -304,9 +303,9 @@ export const Admin: React.FC = () => {
</td>
</tr>
))}
{sortedOperators.length === 0 && (
{sortedUsers.length === 0 && (
<tr>
<td colSpan={4} className="p-10 text-center text-slate-700 font-black italic uppercase tracking-widest text-xs">No registered operators detected.</td>
<td colSpan={4} className="p-10 text-center text-slate-700 font-black italic uppercase tracking-widest text-xs">No registered users detected.</td>
</tr>
)}
</tbody>
@@ -451,7 +450,7 @@ export const Admin: React.FC = () => {
<div className="fixed inset-0 bg-black/95 z-[400] flex items-center justify-center p-4">
<div className="hxp-border border-4 max-md w-full max-w-md bg-black p-8 relative">
<button onClick={() => setEditingTeam(null)} className="absolute top-4 right-4 text-white hover:text-red-500 transition-colors"><X size={24}/></button>
<h3 className="text-3xl font-black italic text-white mb-8 uppercase">OPERATOR_PROFILE</h3>
<h3 className="text-3xl font-black italic text-white mb-8 uppercase">USER_PROFILE</h3>
<form onSubmit={async e => { e.preventDefault(); await updateTeam(editingTeam.id as string, { name: editingTeam.name, isDisabled: editingTeam.isDisabled, isAdmin: editingTeam.isAdmin, password: editingTeam.newPassword }); setEditingTeam(null); }} className="space-y-4">
<input className="w-full bg-black hxp-border-purple p-3 text-white font-black" value={editingTeam.name} onChange={e => setEditingTeam({...editingTeam, name: e.target.value})} required />
<input type="password" placeholder="UPDATE SECRET_KEY (OPTIONAL)" className="w-full bg-black hxp-border-purple p-3 text-white font-black" onChange={e => setEditingTeam({...editingTeam, newPassword: e.target.value})} />