d3d6af33cc
Light mode — Energetic & Clean (Duolingo/fitness app aesthetic): - GlassPanel rebuilt as function component using useTheme; light mode gets solid colored header banners per panel (gold=Showdown, fire=Hot Streak, cyan=Weekly, blue=Stats/Leaderboard, purple=Rewards/Badges) with white text on color; dark mode keeps existing glass panel + accent glow line - Page background: zinc-100/80 so white cards float visually - Event cards (Showdown, Hot Streak, 12 Appointments) promoted to use GlassPanel title+icon props — each gets its own colored identity banner - OperatorCard: soft shadow + amber ring in light mode; stays dark (sports card aesthetic intentional); harsh rgba(0,0,0,0.6) shadow removed - Live Event Banner: vibrant emerald-600→green-700 gradient in light mode replacing the always-dark green-900; softer shadow and border - Log Action buttons: clean bg-white with shadow-sm, removed dark inset shadow that made them look dirty in light mode - Neon/cyan on white fixed: #AAFF00→green-600, #00E5FF→sky-500/sky-600 - EnergyBar tracks: bg-zinc-200 in light, bg-black/60 in dark - Leaderboard, badge cards, reward nodes: all adapted with dark: variants - XP badge in header: bg-zinc-100/border-zinc-200 in light mode - Import useTheme added to support GlassPanel and OperatorCard hooks Dark mode — unchanged; all existing glass panel aesthetics preserved.
982 lines
74 KiB
React
982 lines
74 KiB
React
import React, { useState, useEffect } from 'react';
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { useAuth } from '../context/AuthContext';
|
|
import { useMockStore } from '../data/mockStore';
|
|
import { GamificationProvider, useGamification } from '../context/GamificationContext';
|
|
import { useGamification as useOldGamification } from '../hooks/useGamification';
|
|
import { useTheme } from '../context/ThemeContext';
|
|
import FloatingXPManager from '../components/ProCanvas/gamification/FloatingXPManager';
|
|
import LevelUpModal from '../components/ProCanvas/gamification/LevelUpModal';
|
|
import profilePic from '../assets/images/Profile_Pic_1_Agent.png';
|
|
import {
|
|
Trophy, Flame, Target, Star, Gift, Camera,
|
|
Map as MapIcon, Users, Crosshair, Award, MapPin,
|
|
CheckCircle, ChevronRight, X, Home, Shield, Lock,
|
|
ClipboardList, Handshake, DoorOpen, UserPlus, Zap, Activity, TrendingUp, Hexagon, Calendar
|
|
} from 'lucide-react';
|
|
|
|
/*
|
|
* ==========================================
|
|
* UTILS & THEME
|
|
* ==========================================
|
|
*/
|
|
const C = {
|
|
gold: '#FFD700',
|
|
neon: '#AAFF00',
|
|
blue: '#3B82F6',
|
|
fire: '#FF4500',
|
|
cyan: '#00E5FF',
|
|
purple: '#B388FF',
|
|
glass: 'rgba(24, 24, 27, 0.65)',
|
|
border: 'rgba(255, 255, 255, 0.08)'
|
|
};
|
|
|
|
// CountUp Component for animated numbers (0 to end)
|
|
const CountUp = ({ to, suffix = "", duration = 1500 }) => {
|
|
const [count, setCount] = useState(0);
|
|
useEffect(() => {
|
|
let start = 0;
|
|
const end = parseInt(to, 10);
|
|
if (isNaN(end)) return;
|
|
let startTime = null;
|
|
const animate = (timestamp) => {
|
|
if (!startTime) startTime = timestamp;
|
|
const progress = timestamp - startTime;
|
|
const percentage = Math.min(progress / duration, 1);
|
|
const ease = 1 - Math.pow(1 - percentage, 4);
|
|
setCount(Math.floor(start + (end - start) * ease));
|
|
if (percentage < 1) {
|
|
requestAnimationFrame(animate);
|
|
} else {
|
|
setCount(end);
|
|
}
|
|
};
|
|
requestAnimationFrame(animate);
|
|
}, [to, duration]);
|
|
return <>{count.toLocaleString()}{suffix}</>;
|
|
};
|
|
|
|
const useMonthResetCountdown = () => {
|
|
const [timeLeft, setTimeLeft] = useState('');
|
|
useEffect(() => {
|
|
const updateTimer = () => {
|
|
const now = new Date();
|
|
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59);
|
|
const diff = endOfMonth - now;
|
|
const d = Math.floor(diff / (1000 * 60 * 60 * 24));
|
|
const h = Math.floor((diff / (1000 * 60 * 60)) % 24);
|
|
const m = Math.floor((diff / 1000 / 60) % 60);
|
|
const s = Math.floor((diff / 1000) % 60);
|
|
setTimeLeft(`${d}d ${h}h ${m}m ${s}s`);
|
|
};
|
|
updateTimer();
|
|
const interval = setInterval(updateTimer, 1000);
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
return timeLeft;
|
|
};
|
|
|
|
const LOG_ACTIONS = [
|
|
{ key: 'knock', label: 'Door Knocked', icon: DoorOpen, color: C.cyan },
|
|
{ key: 'lead', label: 'Lead Gained', icon: UserPlus, color: C.neon },
|
|
{ key: 'appt', label: 'Appointment', icon: Target, color: C.gold },
|
|
{ key: 'meeting', label: 'Client Meet', icon: Handshake, color: C.purple },
|
|
];
|
|
|
|
/*
|
|
* ==========================================
|
|
* PREMIUM COMPONENTS (SPORTS UI)
|
|
* ==========================================
|
|
*/
|
|
|
|
// Hexagonal level badge (Like FIFA overall rating)
|
|
const HexLevelBadge = ({ level, color = C.gold, size = 100, mobileSize = 85 }) => (
|
|
<div className={`relative flex items-center justify-center font-['Barlow_Condensed'] drop-shadow-2xl w-[85px] h-[85px] sm:w-[100px] sm:h-[100px]`}>
|
|
<svg fill="rgba(0,0,0,0.6)" stroke={color} strokeWidth="2" strokeLinejoin="round" className="absolute inset-0 w-[110%] h-[110%] top-[-5%] left-[-5%]" style={{ filter: `drop-shadow(0 0 10px ${color}88)` }} viewBox="0 0 28 28">
|
|
<path d="M22.5 16.5V9.5a2 2 0 0 0-1-1.73l-6-3.46a2 2 0 0 0-2 0l-6 3.46a2 2 0 0 0-1 1.73v7a2 2 0 0 0 1 1.73l6 3.46a2 2 0 0 0 2 0l6-3.46a2 2 0 0 0 1-1.73z" />
|
|
</svg>
|
|
<div className="absolute inset-0 flex flex-col items-center justify-center -mt-1">
|
|
<span className="text-[10px] font-black tracking-widest leading-none text-white/70">OVR</span>
|
|
<span className="text-3xl sm:text-[36px] font-black italic leading-none mt-0.5" style={{ color: color }}><CountUp to={level} /></span>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
// Fut-style Player Card
|
|
const OperatorCard = ({ user, gamestate }) => {
|
|
const { theme } = useTheme();
|
|
const isDark = theme === 'dark';
|
|
return (
|
|
<motion.div
|
|
initial={{ scale: 0.95, opacity: 0 }}
|
|
animate={{ scale: 1, opacity: 1 }}
|
|
className={`relative w-[90%] sm:w-full aspect-[2.5/3.8] max-w-[340px] sm:max-w-[400px] mx-auto rounded-[1.5rem] sm:rounded-[2rem] overflow-hidden cursor-pointer group
|
|
${isDark
|
|
? 'shadow-[0_20px_40px_rgba(0,0,0,0.6)] sm:shadow-[0_30px_60px_rgba(0,0,0,0.7)]'
|
|
: 'shadow-[0_8px_30px_rgba(0,0,0,0.18)] ring-1 ring-amber-200/60'
|
|
}`}
|
|
>
|
|
{/* Card Background (Elite Gold Theme) */}
|
|
<div className={`absolute inset-0 z-0 ${isDark
|
|
? 'bg-gradient-to-br from-zinc-800 via-zinc-900 to-black'
|
|
: 'bg-gradient-to-br from-zinc-800 via-zinc-900 to-black'
|
|
}`} />
|
|
|
|
{/* Glossy overlay */}
|
|
<div className="absolute inset-0 bg-gradient-to-b from-white/20 to-transparent opacity-50 z-10 pointer-events-none" style={{ clipPath: 'polygon(0 0, 100% 0, 100% 30%, 0 50%)' }} />
|
|
|
|
{/* Dynamic Light rays */}
|
|
<motion.div
|
|
className="absolute inset-0 opacity-40 z-0 mix-blend-overlay pointer-events-none"
|
|
style={{ background: `conic-gradient(from 180deg at 50% 50%, transparent 0deg, ${C.gold} 180deg, transparent 360deg)` }}
|
|
animate={{ rotate: 360 }}
|
|
transition={{ duration: 15, repeat: Infinity, ease: 'linear' }}
|
|
/>
|
|
|
|
{/* Inner Content Container */}
|
|
<div className="absolute inset-[4px] rounded-[1.8rem] border border-white/20 bg-gradient-to-b from-transparent to-black/95 z-20 flex flex-col p-6">
|
|
|
|
{/* Header Row: OVR and 3 Badges */}
|
|
<div className="flex justify-between items-start w-full relative z-30">
|
|
<HexLevelBadge level={gamestate?.level || 12} color={C.gold} size={100} />
|
|
<div className="flex gap-2">
|
|
<div className="w-11 h-11 rounded-full border border-[#FF4500]/60 flex items-center justify-center bg-black/60 backdrop-blur-md shadow-[0_0_15px_rgba(255,69,0,0.5)]">
|
|
<Crosshair size={22} className="text-[#FF4500]" />
|
|
</div>
|
|
<div className="w-11 h-11 rounded-full border border-[#AAFF00]/60 flex items-center justify-center bg-black/60 backdrop-blur-md shadow-[0_0_15px_rgba(170,255,0,0.5)]">
|
|
<Zap size={22} className="text-[#AAFF00]" />
|
|
</div>
|
|
<div className="w-11 h-11 rounded-full border border-[#00E5FF]/60 flex items-center justify-center bg-black/60 backdrop-blur-md shadow-[0_0_15px_rgba(0,229,255,0.5)]">
|
|
<Star size={22} className="text-[#00E5FF]" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Player Cutout / Photo */}
|
|
<div className="absolute top-[8%] left-1/2 -translate-x-1/2 w-[115%] h-[60%] z-20 pointer-events-none flex items-end justify-center">
|
|
<motion.img
|
|
initial={{ y: 20, opacity: 0 }}
|
|
animate={{ y: 0, opacity: 1 }}
|
|
transition={{ delay: 0.2 }}
|
|
src={profilePic} alt={user.name}
|
|
className="h-full object-contain drop-shadow-[0_20px_20px_rgba(0,0,0,0.9)]"
|
|
style={{ filter: 'contrast(1.15) saturate(1.2)' }}
|
|
/>
|
|
</div>
|
|
|
|
{/* Name & Title Board */}
|
|
<div className="mt-auto relative z-30 flex flex-col items-center">
|
|
<div className="w-full text-center border-b border-white/10 pb-4 mb-4">
|
|
<h2 className="font-['Barlow_Condensed'] font-black uppercase text-5xl tracking-wide text-white drop-shadow-lg">
|
|
{user.name}
|
|
</h2>
|
|
<span className="font-['Barlow_Condensed'] text-base text-[#FFD700] tracking-widest uppercase font-bold drop-shadow">
|
|
{gamestate.currentTitle || 'LVL 12 - FIELD RUNNER'}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-2 gap-x-6 sm:gap-x-10 gap-y-2 sm:gap-y-3 w-full px-4 sm:px-6">
|
|
<div className="flex items-center justify-between font-['Barlow_Condensed']">
|
|
<span className="text-2xl sm:text-3xl font-black text-white"><CountUp to={gamestate.dailyQuests?.knocks?.current || 18} /></span>
|
|
<span className="text-sm sm:text-lg font-bold text-white/50 tracking-widest">KNK</span>
|
|
</div>
|
|
<div className="flex items-center justify-between font-['Barlow_Condensed']">
|
|
<span className="text-2xl sm:text-3xl font-black text-white"><CountUp to={gamestate.dailyQuests?.leads?.current || 4} /></span>
|
|
<span className="text-sm sm:text-lg font-bold text-white/50 tracking-widest">LED</span>
|
|
</div>
|
|
<div className="flex items-center justify-between font-['Barlow_Condensed']">
|
|
<span className="text-2xl sm:text-3xl font-black text-white"><CountUp to={gamestate.dailyQuests?.inspections?.current || 3} /></span>
|
|
<span className="text-sm sm:text-lg font-bold text-white/50 tracking-widest">APT</span>
|
|
</div>
|
|
<div className="flex items-center justify-between font-['Barlow_Condensed']">
|
|
<span className="text-2xl sm:text-3xl font-black text-[#FF4500]"><CountUp to={gamestate.streak || 12} /></span>
|
|
<span className="text-sm sm:text-lg font-bold text-[#FF4500]/80 tracking-widest leading-none text-right">STR</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Bottom Card Flare */}
|
|
<div className="absolute bottom-0 inset-x-0 h-1/3 bg-gradient-to-t from-[rgba(255,215,0,0.2)] to-transparent pointer-events-none z-10" />
|
|
</motion.div>
|
|
);
|
|
};
|
|
|
|
// Sleek Glass Panel (base container for UI)
|
|
function GlassPanel({ children, className = '', accentColor = 'transparent', title, icon: Icon, extra }) {
|
|
const { theme } = useTheme();
|
|
const isDark = theme === 'dark';
|
|
const hasAccent = accentColor !== 'transparent';
|
|
|
|
return (
|
|
<div className={`relative rounded-[1.5rem] overflow-hidden border flex flex-col
|
|
${isDark
|
|
? 'bg-zinc-900/70 backdrop-blur-xl border-white/[0.08] shadow-[0_12px_40px_rgba(0,0,0,0.4)]'
|
|
: 'bg-white border-zinc-200/60 shadow-md'
|
|
} ${className}`}>
|
|
|
|
{/* Dark mode: subtle accent glow line at top */}
|
|
{isDark && hasAccent && (
|
|
<div className="absolute top-0 inset-x-0 h-[2px] opacity-70 shadow-[0_0_20px_3px]"
|
|
style={{ backgroundColor: accentColor, color: accentColor }} />
|
|
)}
|
|
|
|
{/* Header */}
|
|
{(title || Icon || extra) && (
|
|
isDark ? (
|
|
<div className="px-6 py-4 flex items-center justify-between border-b border-white/[0.08]">
|
|
<div className="flex items-center gap-3">
|
|
{Icon && <Icon size={20} style={{ color: hasAccent ? accentColor : undefined }} className="text-zinc-300" />}
|
|
<h3 className="font-['Barlow_Condensed'] font-bold uppercase tracking-widest text-[#E4E4E5] text-sm">{title}</h3>
|
|
</div>
|
|
{extra && <div className="text-lg font-bold text-white/80">{extra}</div>}
|
|
</div>
|
|
) : (
|
|
<div className="px-5 py-3 flex items-center justify-between rounded-t-[1.4rem]"
|
|
style={{ backgroundColor: hasAccent ? accentColor : '#e4e4e7' }}>
|
|
<div className="flex items-center gap-2.5">
|
|
{Icon && <Icon size={18} className="text-white" style={{ opacity: 0.9 }} />}
|
|
<h3 className="font-['Barlow_Condensed'] font-black uppercase tracking-widest text-white text-sm">{title}</h3>
|
|
</div>
|
|
{extra && <div className="text-sm font-bold text-white/90">{extra}</div>}
|
|
</div>
|
|
)
|
|
)}
|
|
|
|
<div className="relative z-10 p-6 flex-1 flex flex-col">
|
|
{children}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Progress Bar (Sleek, bright energy bar)
|
|
const EnergyBar = ({ current, max, color = C.neon, label }) => {
|
|
const pct = Math.min((current / max) * 100, 100);
|
|
return (
|
|
<div className="w-full">
|
|
{label && (
|
|
<div className="flex justify-between items-end mb-2 font-['Barlow_Condensed']">
|
|
<span className="text-sm font-bold text-zinc-500 dark:text-white/60 tracking-wider uppercase">{label}</span>
|
|
<span className="text-xl font-black text-zinc-900 dark:text-white"><CountUp to={current} /> <span className="text-zinc-400 dark:text-white/40">/ {max}</span></span>
|
|
</div>
|
|
)}
|
|
<div className="h-3 w-full bg-zinc-200 dark:bg-black/60 rounded-full overflow-hidden shadow-inner border border-zinc-300 dark:border-white/5">
|
|
<motion.div
|
|
initial={{ width: 0 }}
|
|
animate={{ width: `${pct}%` }}
|
|
transition={{ duration: 1.5, ease: "easeOut" }}
|
|
className="h-full rounded-full relative"
|
|
style={{ backgroundColor: color, boxShadow: `0 0 15px ${color}88` }}
|
|
>
|
|
<div className="absolute top-0 right-0 bottom-0 w-8 bg-gradient-to-r from-transparent to-white/60" />
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
/*
|
|
* ==========================================
|
|
* MODALS
|
|
* ==========================================
|
|
*/
|
|
|
|
const LogActionModal = ({ actionKey, onClose, gs }) => {
|
|
const action = LOG_ACTIONS.find(a => a.key === actionKey) || LOG_ACTIONS[0];
|
|
const Icon = action.icon;
|
|
const [xpReward, setXpReward] = useState(actionKey === 'knock' ? 10 : actionKey === 'lead' ? 50 : 150);
|
|
|
|
const handleSubmit = (e) => {
|
|
e.preventDefault();
|
|
const coords = { x: window.innerWidth / 2, y: window.innerHeight / 2 };
|
|
if (actionKey === 'knock') gs.logKnock(xpReward > 10, coords);
|
|
else if (actionKey === 'lead' || actionKey === 'meeting') gs.logLead(coords);
|
|
else if (actionKey === 'appt') gs.logInspection(coords);
|
|
onClose();
|
|
};
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
|
|
<div className="absolute inset-0 bg-black/80 backdrop-blur-sm cursor-pointer" onClick={onClose} />
|
|
<motion.div initial={{ scale: 0.95, opacity: 0, y: 20 }} animate={{ scale: 1, opacity: 1, y: 0 }} exit={{ scale: 0.95, opacity: 0, y: 20 }}
|
|
className="relative bg-gradient-to-br from-zinc-900 to-black border border-white/20 p-6 sm:p-8 rounded-[2rem] w-full max-w-md shadow-[0_30px_60px_rgba(0,0,0,0.8)] flex flex-col gap-6">
|
|
|
|
<div className="flex items-center gap-4 border-b border-white/10 pb-4">
|
|
<div className="w-12 h-12 rounded-xl flex items-center justify-center shadow-lg" style={{ backgroundColor: `${action.color}22`, border: `1px solid ${action.color}55` }}>
|
|
<Icon size={24} style={{ color: action.color }} />
|
|
</div>
|
|
<div>
|
|
<h3 className="font-['Barlow_Condensed'] font-black uppercase text-2xl text-white tracking-widest leading-none">Log {action.label}</h3>
|
|
<span className="text-xs font-bold uppercase tracking-widest text-[#AAFF00] mt-1 block">Gain XP instantly</span>
|
|
</div>
|
|
<button onClick={onClose} className="ml-auto text-white/50 hover:text-white"><X size={24} /></button>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
|
{actionKey === 'knock' && (
|
|
<>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest text-zinc-500 dark:text-white/60 uppercase">Address / Location</label>
|
|
<input required type="text" placeholder="e.g. 123 Main St" className="w-full bg-zinc-100 dark:bg-black/50 border border-zinc-300 dark:border-white/10 rounded-lg p-3 text-zinc-900 dark:text-white focus:outline-none focus:border-[#AAFF00] transition-colors" />
|
|
</div>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest text-zinc-500 dark:text-white/60 uppercase">Client Name (Optional)</label>
|
|
<input type="text" placeholder="e.g. John Doe" className="w-full bg-zinc-100 dark:bg-black/50 border border-zinc-300 dark:border-white/10 rounded-lg p-3 text-zinc-900 dark:text-white focus:outline-none focus:border-[#AAFF00] transition-colors" />
|
|
</div>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest text-zinc-500 dark:text-white/60 uppercase">Phone Number (Optional)</label>
|
|
<input type="tel" placeholder="(555) 123-4567" className="w-full bg-zinc-100 dark:bg-black/50 border border-zinc-300 dark:border-white/10 rounded-lg p-3 text-zinc-900 dark:text-white focus:outline-none focus:border-[#AAFF00] transition-colors" />
|
|
</div>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest text-zinc-500 dark:text-white/60 uppercase">Interaction Result</label>
|
|
<select onChange={(e) => setXpReward(e.target.value === 'Pitched' ? 15 : 10)} className="w-full bg-zinc-100 dark:bg-black/50 border border-zinc-300 dark:border-white/10 rounded-lg p-3 text-zinc-900 dark:text-white focus:outline-none focus:border-[#AAFF00] transition-colors">
|
|
<option value="Not Home">Not Home</option>
|
|
<option value="Pitched">Pitched / Conversation</option>
|
|
<option value="Follow Up">Follow Up Needed</option>
|
|
</select>
|
|
</div>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest text-zinc-500 dark:text-white/60 uppercase">Details / Notes</label>
|
|
<textarea rows="2" placeholder="Brief notes about the home or conversation..." className="w-full bg-black/50 border border-white/10 rounded-lg p-3 text-white focus:outline-none focus:border-[#AAFF00] transition-colors resize-none" />
|
|
</div>
|
|
</>
|
|
)}
|
|
{(actionKey === 'lead' || actionKey === 'appt' || actionKey === 'meeting') && (
|
|
<>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest text-zinc-500 dark:text-white/60 uppercase">Client / Lead Name</label>
|
|
<input required type="text" placeholder="e.g. John Doe" className="w-full bg-zinc-100 dark:bg-black/50 border border-zinc-300 dark:border-white/10 rounded-lg p-3 text-zinc-900 dark:text-white focus:outline-none focus:border-[#AAFF00] transition-colors" />
|
|
</div>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest text-zinc-500 dark:text-white/60 uppercase">Phone Number</label>
|
|
<input type="tel" placeholder="(555) 123-4567" className="w-full bg-zinc-100 dark:bg-black/50 border border-zinc-300 dark:border-white/10 rounded-lg p-3 text-zinc-900 dark:text-white focus:outline-none focus:border-[#AAFF00] transition-colors" />
|
|
</div>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest text-zinc-500 dark:text-white/60 uppercase">Address / Location</label>
|
|
<input type="text" placeholder="e.g. 123 Main St" className="w-full bg-zinc-100 dark:bg-black/50 border border-zinc-300 dark:border-white/10 rounded-lg p-3 text-zinc-900 dark:text-white focus:outline-none focus:border-[#AAFF00] transition-colors" />
|
|
</div>
|
|
{actionKey === 'appt' && (
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest text-zinc-500 dark:text-white/60 uppercase">Date</label>
|
|
<input required type="date" className="w-full bg-zinc-100 dark:bg-black/50 border border-zinc-300 dark:border-white/10 rounded-lg p-3 text-zinc-900 dark:text-white focus:outline-none focus:border-[#AAFF00] transition-colors" style={{ colorScheme: 'dark' }} />
|
|
</div>
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest text-zinc-500 dark:text-white/60 uppercase">Time</label>
|
|
<input required type="time" className="w-full bg-zinc-100 dark:bg-black/50 border border-zinc-300 dark:border-white/10 rounded-lg p-3 text-zinc-900 dark:text-white focus:outline-none focus:border-[#AAFF00] transition-colors" style={{ colorScheme: 'dark' }} />
|
|
</div>
|
|
</div>
|
|
)}
|
|
{(actionKey === 'meeting' || actionKey === 'lead') && (
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest text-zinc-500 dark:text-white/60 uppercase">Interaction Type</label>
|
|
<select className="w-full bg-zinc-100 dark:bg-black/50 border border-zinc-300 dark:border-white/10 rounded-lg p-3 text-zinc-900 dark:text-white focus:outline-none focus:border-[#AAFF00] transition-colors">
|
|
<option value="Initial">Initial Contact</option>
|
|
<option value="Follow Up">Follow Up</option>
|
|
<option value="Closing">Closing Attempt</option>
|
|
</select>
|
|
</div>
|
|
)}
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest text-zinc-500 dark:text-white/60 uppercase">Details / Notes</label>
|
|
<textarea rows="2" placeholder="Brief notes..." className="w-full bg-black/50 border border-white/10 rounded-lg p-3 text-white focus:outline-none focus:border-[#AAFF00] transition-colors resize-none" />
|
|
</div>
|
|
</>
|
|
)}
|
|
|
|
<div className="bg-[#AAFF00]/10 border border-[#AAFF00]/30 rounded-xl p-4 mt-2 flex items-center justify-between">
|
|
<span className="font-['Barlow_Condensed'] font-bold text-white uppercase tracking-widest">Predicted Reward:</span>
|
|
<motion.span animate={{ scale: [1, 1.05, 1] }} transition={{ repeat: Infinity, duration: 1.5 }} className="font-['Barlow_Condensed'] font-black text-2xl text-[#AAFF00] drop-shadow-[0_0_10px_rgba(170,255,0,0.5)]">
|
|
+{xpReward} XP
|
|
</motion.span>
|
|
</div>
|
|
|
|
<button type="submit" className="w-full mt-2 bg-gradient-to-r from-[#AAFF00] to-[#77B300] hover:brightness-110 active:scale-95 text-black font-['Barlow_Condensed'] font-black text-xl uppercase tracking-widest py-4 rounded-xl flex items-center justify-center gap-2 transition-all shadow-[0_0_20px_rgba(170,255,0,0.4)]">
|
|
Confirm Action <CheckCircle size={20} />
|
|
</button>
|
|
</form>
|
|
</motion.div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const BadgesModal = ({ onClose, gs }) => (
|
|
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
|
|
<div className="absolute inset-0 bg-black/80 backdrop-blur-sm cursor-pointer" onClick={onClose} />
|
|
<motion.div initial={{ scale: 0.95, opacity: 0, y: 20 }} animate={{ scale: 1, opacity: 1, y: 0 }}
|
|
className="relative bg-gradient-to-br from-zinc-900 to-black border border-white/20 p-6 sm:p-8 rounded-[2rem] w-full max-w-4xl max-h-[90vh] overflow-y-auto shadow-[0_30px_60px_rgba(0,0,0,0.8)] flex flex-col gap-6 scrollbar-hide">
|
|
|
|
<div className="flex items-center justify-between border-b border-zinc-200 dark:border-white/10 pb-4 sticky top-0 bg-white/90 dark:bg-black/50 backdrop-blur-md z-10 -mx-6 px-6 -mt-6 pt-6">
|
|
<div>
|
|
<h3 className="font-['Barlow_Condensed'] font-black uppercase text-3xl text-white tracking-widest leading-none flex items-center gap-3"><Award className="text-[#FFD700]" /> Trophy Room</h3>
|
|
<span className="text-sm font-bold uppercase tracking-widest text-white/50 mt-1 block">View all unlockable badges and progress</span>
|
|
</div>
|
|
<button onClick={onClose} className="text-white/50 hover:text-white bg-white/5 p-2 rounded-full"><X size={24} /></button>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{/* Unlocked */}
|
|
<div className="bg-gradient-to-br from-zinc-800 to-black rounded-xl p-5 border border-[#FF4500]/50 flex flex-col items-center text-center relative shadow-[0_0_20px_rgba(255,69,0,0.15)]">
|
|
<div className="absolute top-3 right-3 bg-[#FF4500]/20 text-[#FF4500] px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-widest">Unlocked</div>
|
|
<div className="w-20 h-20 rounded-full bg-black/60 flex items-center justify-center mb-4 shadow-[0_0_20px_rgba(255,69,0,0.3)]">
|
|
<Crosshair size={40} className="text-[#FF4500]" />
|
|
</div>
|
|
<span className="font-['Barlow_Condensed'] font-black text-xl uppercase tracking-widest text-white">Hot Spot Hunter</span>
|
|
<p className="text-white/50 text-sm mt-2 mb-4">You established a 10-day consistent canvassing streak.</p>
|
|
<div className="w-full bg-[#FF4500]/20 border border-[#FF4500]/50 font-['Barlow_Condensed'] font-black px-3 py-2 rounded-lg text-lg text-[#FF4500]">ACHIEVED</div>
|
|
</div>
|
|
|
|
<div className="bg-gradient-to-br from-zinc-800 to-black rounded-xl p-5 border border-[#FFD700]/50 flex flex-col items-center text-center relative shadow-[0_0_20px_rgba(255,215,0,0.15)]">
|
|
<div className="absolute top-3 right-3 bg-[#FFD700]/20 text-[#FFD700] px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-widest">Unlocked</div>
|
|
<div className="w-20 h-20 rounded-full bg-black/60 flex items-center justify-center mb-4 shadow-[0_0_20px_rgba(255,215,0,0.3)]">
|
|
<Zap size={40} className="text-[#FFD700]" />
|
|
</div>
|
|
<span className="font-['Barlow_Condensed'] font-black text-xl uppercase tracking-widest text-white">Storm Chaser</span>
|
|
<p className="text-white/50 text-sm mt-2 mb-4">Reached the top 3 on the daily leaderboard.</p>
|
|
<div className="w-full bg-[#FFD700]/20 border border-[#FFD700]/50 font-['Barlow_Condensed'] font-black px-3 py-2 rounded-lg text-lg text-[#FFD700]">ACHIEVED</div>
|
|
</div>
|
|
|
|
{/* Locked */}
|
|
<div className="bg-zinc-100 dark:bg-zinc-900/50 rounded-xl p-5 border border-zinc-200 dark:border-white/5 flex flex-col items-center text-center">
|
|
<div className="absolute top-3 right-3 bg-zinc-200 dark:bg-white/10 text-zinc-400 dark:text-white/40 px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-widest flex items-center gap-1"><Lock size={10} /> Locked</div>
|
|
<div className="w-20 h-20 rounded-full bg-zinc-300 dark:bg-black/50 flex items-center justify-center mb-4 opacity-40 grayscale">
|
|
<Star size={40} className="text-zinc-500 dark:text-white" />
|
|
</div>
|
|
<span className="font-['Barlow_Condensed'] font-black text-xl uppercase tracking-widest text-zinc-400 dark:text-white/50">Star Closer</span>
|
|
<p className="text-zinc-400 dark:text-white/40 text-sm mt-2 mb-4">Set 50 total appointments.</p>
|
|
<div className="w-full">
|
|
<div className="flex justify-between text-xs font-bold text-zinc-400 dark:text-white/40 mb-1 uppercase tracking-widest"><span>Progress</span><span>12 / 50</span></div>
|
|
<div className="h-2 w-full bg-zinc-200 dark:bg-black/60 rounded-full overflow-hidden"><div className="h-full bg-zinc-400 dark:bg-white/20" style={{ width: '24%' }} /></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="bg-zinc-100 dark:bg-zinc-900/50 rounded-xl p-5 border border-zinc-200 dark:border-white/5 flex flex-col items-center text-center">
|
|
<div className="absolute top-3 right-3 bg-zinc-200 dark:bg-white/10 text-zinc-400 dark:text-white/40 px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-widest flex items-center gap-1"><Lock size={10} /> Locked</div>
|
|
<div className="w-20 h-20 rounded-full bg-zinc-300 dark:bg-black/50 flex items-center justify-center mb-4 opacity-40 grayscale">
|
|
<MapIcon size={40} className="text-zinc-500 dark:text-white" />
|
|
</div>
|
|
<span className="font-['Barlow_Condensed'] font-black text-xl uppercase tracking-widest text-zinc-400 dark:text-white/50">Map Master</span>
|
|
<p className="text-zinc-400 dark:text-white/40 text-sm mt-2 mb-4">Cover 10 different neighborhoods entirely.</p>
|
|
<div className="w-full">
|
|
<div className="flex justify-between text-xs font-bold text-zinc-400 dark:text-white/40 mb-1 uppercase tracking-widest"><span>Progress</span><span>3 / 10</span></div>
|
|
<div className="h-2 w-full bg-zinc-200 dark:bg-black/60 rounded-full overflow-hidden"><div className="h-full bg-zinc-400 dark:bg-white/20" style={{ width: '30%' }} /></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
);
|
|
|
|
const RewardsModal = ({ onClose, gs }) => (
|
|
<div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
|
|
<div className="absolute inset-0 bg-black/80 backdrop-blur-sm cursor-pointer" onClick={onClose} />
|
|
<motion.div initial={{ scale: 0.95, opacity: 0, y: 20 }} animate={{ scale: 1, opacity: 1, y: 0 }}
|
|
className="relative bg-gradient-to-br from-zinc-900 to-black border border-white/20 p-6 sm:p-8 rounded-[2rem] w-full max-w-2xl max-h-[90vh] overflow-y-auto shadow-[0_30px_60px_rgba(0,0,0,0.8)] flex flex-col gap-6 scrollbar-hide">
|
|
|
|
<div className="flex items-center justify-between border-b border-zinc-200 dark:border-white/10 pb-4 sticky top-0 bg-white/90 dark:bg-black/50 backdrop-blur-md z-10 -mx-6 px-6 -mt-6 pt-6">
|
|
<div>
|
|
<h3 className="font-['Barlow_Condensed'] font-black uppercase text-3xl text-white tracking-widest leading-none flex items-center gap-3"><Gift className="text-[#B388FF]" /> Season Rewards</h3>
|
|
<span className="text-sm font-bold uppercase tracking-widest text-white/50 mt-1 block">Your milestone roadmap for the month</span>
|
|
</div>
|
|
<button onClick={onClose} className="text-white/50 hover:text-white bg-white/5 p-2 rounded-full"><X size={24} /></button>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-4 relative">
|
|
<div className="absolute left-[24px] top-4 bottom-4 w-1 bg-zinc-200 dark:bg-black/50 rounded-full z-0"><div className="w-full bg-gradient-to-b from-[#B388FF] to-[#00E5FF] shadow-[0_0_15px_rgba(179,136,255,0.8)]" style={{ height: '50%' }} /></div>
|
|
|
|
{[
|
|
{ title: 'Daily Grind', target: 'Reach 1,000 XP', reward: '+400 XP Bonus', done: true, claimable: false },
|
|
{ title: 'Door Warrior', target: 'Reach 2,500 XP', reward: '$25 Gift Card', done: true, claimable: false },
|
|
{ title: 'Lead Machine', target: 'Reach 4,800 XP', reward: '$50 Cash Bonus', active: true, claimable: false },
|
|
{ title: 'Closer Mode', target: 'Reach 7,500 XP', reward: '$100 Amazon Card', locked: true },
|
|
{ title: 'Legend Run', target: 'Reach 12,000 XP', reward: '$250 Cash Bonus', locked: true }
|
|
].map((tier, i) => (
|
|
<div key={i} className={`flex items-start gap-6 relative z-10 p-4 rounded-2xl transition-all ${tier.active ? 'bg-[#B388FF]/10 border border-[#B388FF]/30' : tier.done ? 'opacity-70' : 'opacity-50'}`}>
|
|
<div className={`mt-1 shrink-0 w-8 h-8 rounded-full border-4 flex items-center justify-center bg-zinc-900 ${tier.done ? 'border-[#00E5FF]' : tier.active ? 'border-[#B388FF] ring-4 ring-[#B388FF]/30' : 'border-white/10'}`}>
|
|
{tier.done && <CheckCircle size={16} className="text-[#00E5FF]" />}
|
|
</div>
|
|
<div className="flex-1">
|
|
<h4 className={`font-['Barlow_Condensed'] font-black text-2xl uppercase tracking-widest ${tier.active ? 'text-white' : 'text-white/80'}`}>{tier.title}</h4>
|
|
<p className="text-white/60 text-sm font-bold uppercase tracking-wider mb-2">{tier.target}</p>
|
|
<div className={`inline-block px-3 py-1 rounded-md font-['Barlow_Condensed'] font-bold text-lg uppercase tracking-widest ${tier.done ? 'bg-[#00E5FF]/20 text-[#00E5FF]' : tier.active ? 'bg-[#B388FF]/20 text-[#B388FF] border border-[#B388FF]/50 shadow-[0_0_15px_rgba(179,136,255,0.4)]' : 'bg-white/10 text-white/50'}`}>
|
|
Reward: {tier.reward}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</motion.div>
|
|
</div>
|
|
);
|
|
|
|
/*
|
|
* ==========================================
|
|
* MAIN LAYOUT MODULE
|
|
* ==========================================
|
|
*/
|
|
const ProCanvasContent = () => {
|
|
const { user } = useAuth();
|
|
const { users } = useMockStore();
|
|
|
|
const mockDbUser = {
|
|
...(users.find(u => u.email === user?.email) || users.find(u => u.roles?.includes('FIELD_AGENT')) || { name: user?.name || 'Jason T.', empId: 'OP-00' }),
|
|
profilePhoto: profilePic,
|
|
};
|
|
|
|
// Hooks
|
|
const gs = useGamification() || { xp: 3360, streak: 4, level: 12 };
|
|
const oldGamestate = useOldGamification(mockDbUser) || { badges: [], stages: [] };
|
|
const resetCountdown = useMonthResetCountdown();
|
|
const navigate = useNavigate();
|
|
|
|
// Modals
|
|
const [logAction, setLogAction] = useState(null);
|
|
const [showBadges, setShowBadges] = useState(false);
|
|
const [showRewards, setShowRewards] = useState(false);
|
|
|
|
return (
|
|
<div className="min-h-screen bg-zinc-100/80 dark:bg-[#09090b] text-zinc-900 dark:text-white font-sans selection:bg-[#FFD700]/30 overflow-x-hidden pb-40 relative">
|
|
|
|
{/* Ambient Background Glows */}
|
|
<div className="fixed inset-0 pointer-events-none z-0 overflow-hidden">
|
|
<div className="absolute top-[-20%] left-[-10%] w-[60vw] h-[60vw] bg-[#3B82F6]/5 dark:bg-[#3B82F6]/10 rounded-full blur-[180px]" />
|
|
<div className="absolute bottom-[-10%] right-[-10%] w-[50vw] h-[50vw] bg-[#FFD700]/5 rounded-full blur-[180px]" />
|
|
</div>
|
|
|
|
<FloatingXPManager />
|
|
<LevelUpModal />
|
|
|
|
{/* TOP NAVIGATION */}
|
|
<header className="relative z-40 flex items-center justify-between px-8 py-5 backdrop-blur-3xl border-b border-zinc-200 dark:border-white/5 bg-white/90 dark:bg-zinc-950/60 sticky top-0">
|
|
<div className="flex items-center gap-4">
|
|
<div className="w-12 h-12 rounded-xl bg-gradient-to-br from-[#FFD700] to-[#FF8C00] flex items-center justify-center shadow-[0_0_25px_rgba(255,215,0,0.3)]">
|
|
<Flame size={24} className="text-black" fill="currentColor" />
|
|
</div>
|
|
<div className="flex flex-col">
|
|
<h1 className="font-['Barlow_Condensed'] font-black text-3xl uppercase tracking-widest leading-none text-zinc-900 dark:text-white drop-shadow-md">
|
|
LynkedUp <span className="text-[#FFD700]">Pro</span>
|
|
</h1>
|
|
<span className="text-xs font-bold text-zinc-500 dark:text-white/50 tracking-widest uppercase mt-0.5">SEASON 3 ACTIVE</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Resource Bar (Like Coins/Points in FUT) */}
|
|
<div className="flex items-center gap-6">
|
|
<div className="hidden md:flex items-center bg-zinc-100 dark:bg-black/60 border border-zinc-200 dark:border-white/10 rounded-full px-6 py-2 shadow-sm dark:shadow-inner">
|
|
<Trophy size={20} className="text-[#FFD700] mr-3" />
|
|
<span className="font-['Barlow_Condensed'] font-black text-2xl tracking-wider text-zinc-700 dark:text-white">
|
|
<CountUp to={gs.xp || 3360} /> <span className="text-zinc-400 dark:text-white/40 text-sm ml-1">XP</span>
|
|
</span>
|
|
</div>
|
|
<div className="w-14 h-14 rounded-full p-1 bg-gradient-to-b from-[#FFD700] to-transparent cursor-pointer shadow-lg hover:scale-105 transition-transform">
|
|
<div className="w-full h-full rounded-full bg-zinc-900 border-2 border-transparent overflow-hidden">
|
|
<img src={profilePic} alt="Profile" className="w-full h-full object-cover" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
{/* MAIN DASHBOARD */}
|
|
<div className="relative z-10 w-full max-w-[1700px] mx-auto px-4 sm:px-6 mt-6 sm:mt-8 flex flex-col lg:flex-row gap-6 sm:gap-8">
|
|
|
|
{/* LEFT: PLAYER CARD & LIVE ACTIONS */}
|
|
<div className="w-full lg:w-[360px] xl:w-[420px] shrink-0 flex flex-col gap-6 sm:gap-8">
|
|
{/* The Ultimate Player Card */}
|
|
<OperatorCard user={mockDbUser} gamestate={gs} />
|
|
|
|
{/* Quick Stat Bar (Detailed XP/Streak) */}
|
|
<GlassPanel title="Season Stats" icon={TrendingUp} accentColor={C.blue}>
|
|
<div className="flex flex-col gap-6">
|
|
{/* Detailed XP Progress */}
|
|
<div className="w-full">
|
|
<div className="flex justify-between items-end mb-2 font-['Barlow_Condensed']">
|
|
<div className="flex flex-col">
|
|
<span className="text-sm font-bold text-zinc-500 dark:text-white/60 tracking-wider uppercase">XP Progress</span>
|
|
<span className="text-xs font-bold text-green-600 dark:text-[#AAFF00] tracking-widest uppercase mt-0.5">Next: {gs.nextLevelTitle || 'Field Trooper'}</span>
|
|
</div>
|
|
<div className="flex flex-col items-end">
|
|
<span className="text-2xl font-black text-zinc-600 dark:text-white"><CountUp to={gs.xp || 3360} /> <span className="text-zinc-400 dark:text-white/40 text-lg">/ {4800}</span></span>
|
|
<span className="text-xs font-bold text-sky-500 dark:text-[#00E5FF] tracking-widest uppercase mt-0.5">{4800 - (gs.xp || 3360)} XP TO NEXT LEVEL</span>
|
|
</div>
|
|
</div>
|
|
<div className="h-4 w-full bg-zinc-200 dark:bg-black/60 rounded-full overflow-hidden shadow-inner border border-zinc-300 dark:border-white/5">
|
|
<motion.div
|
|
initial={{ width: 0 }}
|
|
animate={{ width: `${Math.min(((gs.xp || 3360) / 4800) * 100, 100)}%` }}
|
|
transition={{ duration: 1.5, ease: "easeOut" }}
|
|
className="h-full rounded-full relative"
|
|
style={{ backgroundColor: C.blue, boxShadow: `0 0 15px ${C.blue}88` }}
|
|
>
|
|
<div className="absolute top-0 right-0 bottom-0 w-12 bg-gradient-to-r from-transparent to-white/60" />
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Streak Flow */}
|
|
<div className="w-full">
|
|
<div className="flex justify-between items-end mb-2 font-['Barlow_Condensed']">
|
|
<span className="text-sm font-bold text-zinc-500 dark:text-white/60 tracking-wider uppercase">Streak Flow</span>
|
|
<span className="text-2xl font-black text-zinc-600 dark:text-white"><CountUp to={gs.streak || 12} /> <span className="text-zinc-400 dark:text-white/40 text-lg">/ 30</span></span>
|
|
</div>
|
|
<div className="h-4 w-full bg-zinc-200 dark:bg-black/60 rounded-full overflow-hidden shadow-inner border border-zinc-300 dark:border-white/5">
|
|
<motion.div
|
|
initial={{ width: 0 }}
|
|
animate={{ width: `${Math.min(((gs.streak || 12) / 30) * 100, 100)}%` }}
|
|
transition={{ duration: 1.5, ease: "easeOut" }}
|
|
className="h-full rounded-full relative"
|
|
style={{ backgroundColor: C.fire, boxShadow: `0 0 15px ${C.fire}88` }}
|
|
>
|
|
<div className="absolute top-0 right-0 bottom-0 w-12 bg-gradient-to-r from-transparent to-white/60" />
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</GlassPanel>
|
|
|
|
{/* Action Hub */}
|
|
<GlassPanel title="Log Action" icon={Activity} accentColor={C.purple}>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
{LOG_ACTIONS.map(({ key, label, icon: I, color }) => (
|
|
<motion.button key={key}
|
|
whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}
|
|
onClick={() => setLogAction(key)}
|
|
className="flex flex-col items-center justify-center p-5 rounded-2xl bg-white dark:bg-black/40 border border-zinc-200 dark:border-white/5 hover:bg-zinc-50 dark:hover:bg-white/5 transition-all gap-3 relative overflow-hidden group shadow-sm dark:shadow-[inset_0_0_20px_rgba(0,0,0,0.5)]">
|
|
<div className="absolute inset-x-0 bottom-0 h-1/2 bg-gradient-to-t opacity-0 group-hover:opacity-30 transition-opacity" style={{ backgroundImage: `linear-gradient(to top, ${color}, transparent)` }} />
|
|
<I size={28} style={{ color }} />
|
|
<span className="font-['Barlow_Condensed'] font-bold text-xs uppercase tracking-widest text-zinc-400 dark:text-[#E4E4E5] group-hover:text-zinc-600 dark:group-hover:text-white">{label}</span>
|
|
</motion.button>
|
|
))}
|
|
</div>
|
|
</GlassPanel>
|
|
|
|
{/* Missing Card Restored: Daily Missions */}
|
|
<GlassPanel title="Daily Missions" icon={Zap} accentColor={C.neon}>
|
|
<div className="flex flex-col gap-6">
|
|
{/* Weekly Challenge */}
|
|
<div className="bg-zinc-100 dark:bg-black/30 p-4 rounded-xl border border-zinc-200 dark:border-white/5">
|
|
<div className="flex items-center gap-2 mb-3">
|
|
<Trophy size={16} className="text-[#FFD700]" />
|
|
<span className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest uppercase text-[#FFD700]">Weekly Challenge</span>
|
|
</div>
|
|
<EnergyBar current={140} max={150} color={C.gold} label="Total Knocks" />
|
|
</div>
|
|
|
|
{/* Daily Quests List */}
|
|
<div className="flex flex-col gap-4">
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<Zap size={16} className="text-green-500 dark:text-[#AAFF00]" />
|
|
<span className="font-['Barlow_Condensed'] font-bold text-sm tracking-widest uppercase text-green-600 dark:text-[#AAFF00]">Daily Quests</span>
|
|
</div>
|
|
<EnergyBar current={gs.dailyQuests?.knocks?.current || 18} max={20} color={C.blue} label="Door Knocks" />
|
|
<EnergyBar current={gs.dailyQuests?.leads?.current || 4} max={5} color={C.neon} label="New Leads" />
|
|
<EnergyBar current={gs.dailyQuests?.inspections?.current || 0} max={1} color={C.fire} label="Inspections" />
|
|
</div>
|
|
|
|
{/* Hit the Map CTA */}
|
|
<motion.button
|
|
animate={{ scale: [1, 1.02, 1], boxShadow: ["0 0 20px rgba(170,255,0,0.3)", "0 0 40px rgba(170,255,0,0.6)", "0 0 20px rgba(170,255,0,0.3)"] }}
|
|
transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
|
|
className="w-full mt-2 bg-[#AAFF00] hover:bg-[#88CC00] text-black font-['Barlow_Condensed'] font-black text-2xl uppercase tracking-widest py-4 rounded-xl flex items-center justify-center gap-2 transition-transform shadow-[0_0_20px_rgba(170,255,0,0.3)]">
|
|
Hit The Map <ChevronRight size={28} />
|
|
</motion.button>
|
|
</div>
|
|
</GlassPanel>
|
|
</div>
|
|
|
|
{/* RIGHT: LIVE EVENTS & CAMPAIGNS */}
|
|
<div className="flex-1 flex flex-col gap-8">
|
|
|
|
{/* HERO LIVE EVENT BANNER (Today's Mission) */}
|
|
<motion.div
|
|
whileHover={{ scale: 1.01 }}
|
|
className="relative w-full rounded-[2rem] overflow-hidden cursor-pointer shadow-[0_8px_30px_rgba(0,0,0,0.15)] dark:shadow-[0_20px_50px_rgba(0,0,0,0.6)] group border border-emerald-600/30 dark:border-white/10"
|
|
onClick={() => navigate('/emp/fa/maps')}
|
|
>
|
|
{/* Event Background Image/Gradient */}
|
|
<div className="absolute inset-0 bg-gradient-to-r from-emerald-600 via-green-700 to-emerald-800 dark:from-green-900 dark:via-[#1A2E1A] dark:to-zinc-900 z-0" />
|
|
<div className="absolute inset-0 bg-[url('https://www.transparenttextures.com/patterns/carbon-fibre.png')] opacity-10 dark:opacity-30 mix-blend-overlay pointer-events-none z-0" />
|
|
|
|
<div className="relative z-10 p-8 sm:p-10 flex flex-col sm:flex-row items-start sm:items-center justify-between min-h-[180px] gap-8">
|
|
<div className="flex flex-col text-center sm:text-left">
|
|
<span className="font-['Barlow_Condensed'] font-black text-base sm:text-lg uppercase tracking-widest text-[#AAFF00] mb-2 flex items-center justify-center sm:justify-start gap-2">
|
|
<Target size={20} /> LIVE EVENT ACTIVE
|
|
</span>
|
|
<h2 className="font-['Barlow_Condensed'] font-black text-4xl sm:text-5xl lg:text-6xl text-white uppercase tracking-tight leading-none drop-shadow-xl">
|
|
Knock 20 Doors<br />
|
|
<span className="text-white/60">Pineland Heights</span>
|
|
</h2>
|
|
</div>
|
|
|
|
{/* Play Button */}
|
|
<motion.div
|
|
animate={{ scale: [1, 1.03, 1], boxShadow: ["0 0 30px rgba(170,255,0,0.4)", "0 0 60px rgba(170,255,0,0.7)", "0 0 30px rgba(170,255,0,0.4)"] }}
|
|
transition={{ duration: 1.5, repeat: Infinity, ease: "easeInOut" }}
|
|
className="shrink-0 flex items-center justify-center px-6 py-4 sm:px-8 sm:py-5 rounded-2xl sm:rounded-3xl bg-gradient-to-br from-[#AAFF00] to-[#558000] border-4 border-[#CCFF33] hover:brightness-110 active:scale-95 transition-all cursor-pointer">
|
|
<span className="font-['Barlow_Condensed'] font-black text-2xl sm:text-3xl italic uppercase tracking-widest text-black flex items-center gap-2 sm:gap-3">
|
|
START PLAY <ChevronRight size={28} className="text-black sm:w-8 sm:h-8 w-6 h-6" />
|
|
</span>
|
|
</motion.div>
|
|
</div>
|
|
{/* Overlay Flare */}
|
|
<div className="absolute top-0 right-0 w-1/2 h-full bg-gradient-to-l from-white/10 to-transparent transform skew-x-[-20deg] group-hover:translate-x-full transition-transform duration-1000 ease-in-out pointer-events-none" />
|
|
</motion.div>
|
|
|
|
{/* THREE GRID: Showdown, Streak, Challenge */}
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
|
|
{/* Showdown Event Card */}
|
|
<GlassPanel title="Showdown" icon={Trophy} accentColor={C.gold}>
|
|
<div className="absolute top-0 right-0 p-6 opacity-5 dark:opacity-10"><Trophy size={100} fill={C.gold} /></div>
|
|
<span className="font-['Barlow_Condensed'] font-bold text-xs text-zinc-400 dark:text-white/50 tracking-widest uppercase mb-3 block">Leaderboard Event</span>
|
|
<div className="mt-2 flex items-end justify-between relative z-10">
|
|
<div>
|
|
<span className="block font-['Barlow_Condensed'] font-black italic text-5xl text-[#FFD700] leading-none drop-shadow-md">1ST</span>
|
|
<span className="text-sm font-bold text-zinc-400 dark:text-white/70 uppercase tracking-widest mt-1 block">18 Leads Today</span>
|
|
</div>
|
|
<div className="bg-[#FFD700]/20 text-[#ca9a04] dark:text-[#FFD700] border border-[#FFD700]/40 font-['Barlow_Condensed'] font-black px-3 py-1.5 rounded-lg text-lg">+250 XP</div>
|
|
</div>
|
|
</GlassPanel>
|
|
|
|
{/* Streak Event Card */}
|
|
<GlassPanel title="Hot Streak" icon={Flame} accentColor={C.fire}>
|
|
<div className="absolute top-0 right-0 p-6 opacity-5 dark:opacity-10"><Flame size={100} fill={C.fire} /></div>
|
|
<span className="font-['Barlow_Condensed'] font-bold text-xs text-zinc-400 dark:text-white/50 tracking-widest uppercase mb-3 block">Consistency Tracker</span>
|
|
<div className="mt-2 flex items-end justify-between relative z-10">
|
|
<div className="flex flex-col items-center">
|
|
<motion.span
|
|
animate={{ scale: [1, 1.05, 1] }}
|
|
transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
|
|
className="block font-['Barlow_Condensed'] font-black italic text-6xl text-[#FF4500] leading-none ml-2 origin-bottom">
|
|
<CountUp to={gs.streak || 12} />
|
|
</motion.span>
|
|
<span className="text-sm font-bold text-zinc-400 dark:text-white/70 uppercase tracking-widest mt-1">Days Row</span>
|
|
</div>
|
|
<div className="bg-[#4CAF50]/20 text-[#2e7d32] dark:text-[#4CAF50] border border-[#4CAF50]/40 font-['Barlow_Condensed'] font-black px-3 py-1.5 rounded-lg text-lg">+50 XP</div>
|
|
</div>
|
|
</GlassPanel>
|
|
|
|
{/* Weekly Challenge Card */}
|
|
<GlassPanel title="12 Appointments" icon={Star} accentColor={C.cyan} className="md:col-span-2 lg:col-span-1">
|
|
<div className="absolute top-0 right-0 p-6 opacity-5 dark:opacity-10"><Star size={100} fill={C.cyan} /></div>
|
|
<span className="font-['Barlow_Condensed'] font-bold text-xs text-zinc-400 dark:text-white/50 tracking-widest uppercase mb-3 block">Weekly Objective</span>
|
|
|
|
<div className="mt-8 w-full relative z-10">
|
|
<EnergyBar current={3} max={12} color={C.cyan} />
|
|
<div className="flex justify-end mt-4">
|
|
<div className="bg-[#00E5FF]/20 text-sky-600 dark:text-[#00E5FF] border border-[#00E5FF]/40 font-['Barlow_Condensed'] font-black px-3 py-1.5 rounded-lg text-lg">+300 XP</div>
|
|
</div>
|
|
</div>
|
|
</GlassPanel>
|
|
</div>
|
|
|
|
{/* SEASON PASS / REWARD PATH (Restored to larger size, scrollable on mobile) */}
|
|
<GlassPanel className="cursor-pointer" title="Rewards & Checkpoints" icon={Gift} accentColor={C.purple} extra={<span className="font-['Barlow_Condensed'] font-black text-sm text-white/90">48% Complete</span>}>
|
|
<div onClick={() => setShowRewards(true)} className="absolute inset-0 z-20" />
|
|
<div className="relative w-full py-16 min-h-[220px] overflow-x-auto overflow-y-hidden scrollbar-hide z-10 flex items-center">
|
|
<div className="min-w-[700px] w-full relative px-4">
|
|
{/* Track Background */}
|
|
<div className="absolute top-1/2 left-8 right-8 h-2.5 bg-zinc-200 dark:bg-black/60 rounded-full transform -translate-y-1/2 z-0 border border-zinc-300 dark:border-white/5" />
|
|
{/* Track Fill */}
|
|
<div className="absolute top-1/2 left-8 h-2.5 bg-gradient-to-r from-[#B388FF] to-[#00E5FF] rounded-full transform -translate-y-1/2 z-0 shadow-[0_0_20px_rgba(179,136,255,0.7)]" style={{ width: '48%' }} />
|
|
|
|
{/* Nodes */}
|
|
<div className="relative z-10 flex justify-between transform -translate-y-4">
|
|
{[
|
|
{ title: 'Daily Grind', reward: '+400 XP' },
|
|
{ title: 'Door Warrior', reward: '$25 Gift Card' },
|
|
{ title: 'Lead Machine', reward: '$50 Bonus' },
|
|
{ title: 'Closer Mode', reward: '$100 Amazon' },
|
|
{ title: 'Legend Run', reward: '$250 Bonus' }
|
|
].map((node, i) => {
|
|
const done = i < 2;
|
|
const active = i === 2;
|
|
return (
|
|
<div key={i} className="flex flex-col items-center relative group">
|
|
{/* Node Circle */}
|
|
<div className={`w-14 h-14 sm:w-16 sm:h-16 rounded-full border-4 flex items-center justify-center transition-all bg-white dark:bg-zinc-900
|
|
${done ? 'border-[#00E5FF] shadow-[0_0_20px_rgba(0,229,255,0.5)]' : active ? 'border-[#B388FF] shadow-[0_0_25px_rgba(179,136,255,0.8)] ring-4 ring-[#B388FF]/30 scale-110' : 'border-zinc-300 dark:border-white/10'}`}>
|
|
{done ? <CheckCircle size={28} className="text-[#00E5FF]" /> :
|
|
active ? <Gift size={32} className="text-[#B388FF]" /> :
|
|
<Lock size={24} className="text-zinc-400 dark:text-white/30" />}
|
|
</div>
|
|
{/* Reward Label (floating below) */}
|
|
<div className="absolute top-[80px] w-max text-center flex flex-col gap-1.5 items-center">
|
|
<span className={`font-['Barlow_Condensed'] font-black text-sm uppercase tracking-widest ${active || done ? 'text-zinc-500 dark:text-white' : 'text-zinc-400 dark:text-white/40'}`}>
|
|
{node.title}
|
|
</span>
|
|
<div className={`px-2 py-0.5 rounded border font-['Barlow_Condensed'] font-bold text-xs uppercase
|
|
${done ? 'bg-[#00E5FF]/20 border-[#00E5FF]/50 text-[#00E5FF]' :
|
|
active ? 'bg-[#B388FF]/20 border-[#B388FF]/50 text-[#B388FF]' :
|
|
'bg-zinc-100 dark:bg-white/5 border-zinc-300 dark:border-white/10 text-zinc-400 dark:text-white/30'}`}>
|
|
{node.reward}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</GlassPanel>
|
|
|
|
{/* Restored: Badges & Achievements Grid */}
|
|
<GlassPanel className="cursor-pointer hover:border-[#FFD700]/50 transition-colors" title="Badges & Achievements" icon={Award} accentColor={C.gold}>
|
|
<div onClick={() => setShowBadges(true)} className="absolute inset-0 z-20" />
|
|
<div className="grid grid-cols-2 md:grid-cols-5 gap-4 relative z-10">
|
|
{/* Badge 1 */}
|
|
<div className="bg-zinc-100 dark:bg-gradient-to-br dark:from-zinc-800 dark:to-black rounded-xl p-4 border border-[#FF4500]/50 flex flex-col items-center text-center relative overflow-hidden group hover:border-[#FF4500] transition-colors shadow-[0_0_15px_rgba(255,69,0,0.2)]">
|
|
<div className="absolute top-2 right-2"><CheckCircle size={14} className="text-[#FF4500]" /></div>
|
|
<div className="w-16 h-16 rounded-full bg-[#FF4500]/10 dark:bg-black/50 flex items-center justify-center mb-3">
|
|
<Crosshair size={32} className="text-[#FF4500]" />
|
|
</div>
|
|
<span className="font-['Barlow_Condensed'] font-black text-sm uppercase tracking-widest text-zinc-500 dark:text-white">Hot Spot Hunter</span>
|
|
<span className="font-['Barlow_Condensed'] font-bold text-xs uppercase text-[#FF4500] mt-2">Unlocked</span>
|
|
</div>
|
|
{/* Badge 2 */}
|
|
<div className="bg-zinc-100 dark:bg-gradient-to-br dark:from-zinc-800 dark:to-black rounded-xl p-4 border border-[#FFD700]/50 flex flex-col items-center text-center relative overflow-hidden group hover:border-[#FFD700] transition-colors shadow-[0_0_15px_rgba(255,215,0,0.2)]">
|
|
<div className="absolute top-2 right-2"><CheckCircle size={14} className="text-[#FFD700]" /></div>
|
|
<div className="w-16 h-16 rounded-full bg-[#FFD700]/10 dark:bg-black/50 flex items-center justify-center mb-3">
|
|
<Zap size={32} className="text-[#FFD700]" />
|
|
</div>
|
|
<span className="font-['Barlow_Condensed'] font-black text-sm uppercase tracking-widest text-zinc-500 dark:text-white">Storm Chaser</span>
|
|
<span className="font-['Barlow_Condensed'] font-bold text-xs uppercase text-[#FFD700] mt-2">Unlocked</span>
|
|
</div>
|
|
{/* Badge 3 Locked */}
|
|
<div className="bg-zinc-100 dark:bg-zinc-900/50 rounded-xl p-4 border border-zinc-200 dark:border-white/5 flex flex-col items-center text-center opacity-60 grayscale hover:grayscale-0 transition-all">
|
|
<div className="absolute top-2 right-2"><Lock size={14} className="text-zinc-400 dark:text-white/30" /></div>
|
|
<div className="w-16 h-16 rounded-full bg-zinc-200 dark:bg-black/50 flex items-center justify-center mb-3">
|
|
<Star size={32} className="text-zinc-400 dark:text-white/40" />
|
|
</div>
|
|
<span className="font-['Barlow_Condensed'] font-black text-sm uppercase tracking-widest text-zinc-500 dark:text-white/60">Star Closer</span>
|
|
</div>
|
|
{/* Badge 4 Locked */}
|
|
<div className="bg-zinc-100 dark:bg-zinc-900/50 rounded-xl p-4 border border-zinc-200 dark:border-white/5 flex flex-col items-center text-center opacity-60 grayscale hover:grayscale-0 transition-all hidden md:flex">
|
|
<div className="absolute top-2 right-2"><Lock size={14} className="text-zinc-400 dark:text-white/30" /></div>
|
|
<div className="w-16 h-16 rounded-full bg-zinc-200 dark:bg-black/50 flex items-center justify-center mb-3">
|
|
<MapIcon size={32} className="text-zinc-400 dark:text-white/40" />
|
|
</div>
|
|
<span className="font-['Barlow_Condensed'] font-black text-sm uppercase tracking-widest text-zinc-500 dark:text-white/60">Map Master</span>
|
|
</div>
|
|
{/* Badge 5 Locked */}
|
|
<div className="bg-zinc-100 dark:bg-zinc-900/50 rounded-xl p-4 border border-zinc-200 dark:border-white/5 flex flex-col items-center text-center opacity-60 grayscale hover:grayscale-0 transition-all hidden md:flex">
|
|
<div className="absolute top-2 right-2"><Lock size={14} className="text-zinc-400 dark:text-white/30" /></div>
|
|
<div className="w-16 h-16 rounded-full bg-zinc-200 dark:bg-black/50 flex items-center justify-center mb-3">
|
|
<Shield size={32} className="text-zinc-400 dark:text-white/40" />
|
|
</div>
|
|
<span className="font-['Barlow_Condensed'] font-black text-sm uppercase tracking-widest text-zinc-500 dark:text-white/60">First Knock</span>
|
|
</div>
|
|
</div>
|
|
</GlassPanel>
|
|
|
|
{/* Restored: Full Leaderboard List */}
|
|
<GlassPanel title="Leaderboard" icon={Trophy} accentColor={C.blue} extra={<div className="font-['Barlow_Condensed'] font-black text-sm text-white/90 flex items-center gap-1.5"><Calendar size={15} /> Resets in {resetCountdown}</div>}>
|
|
<div className="flex flex-col gap-2 relative mt-4 overflow-x-auto scrollbar-hide">
|
|
<div className="min-w-[400px]">
|
|
{/* Table Header */}
|
|
<div className="grid grid-cols-[40px_1fr_80px] sm:grid-cols-[50px_1fr_100px] gap-2 sm:gap-4 px-4 sm:px-6 py-2 border-b border-zinc-200 dark:border-white/10 mb-2">
|
|
<span className="font-['Barlow_Condensed'] font-bold text-xs sm:text-sm tracking-widest uppercase text-zinc-400 dark:text-white/40">Rank</span>
|
|
<span className="font-['Barlow_Condensed'] font-bold text-xs sm:text-sm tracking-widest uppercase text-zinc-400 dark:text-white/40">Agent</span>
|
|
<span className="font-['Barlow_Condensed'] font-bold text-xs sm:text-sm tracking-widest uppercase text-zinc-400 dark:text-white/40 text-right">XP Total</span>
|
|
</div>
|
|
|
|
{(gs.leaderboard || []).slice(0, 10).map((row, i) => (
|
|
<motion.div key={row.id || i}
|
|
layout
|
|
initial={{ x: -20, opacity: 0 }}
|
|
animate={{ x: 0, opacity: 1, scale: row.isUser ? [1, 1.02, 1] : 1 }}
|
|
transition={{ delay: i * 0.1, duration: 0.5 }}
|
|
className={`grid grid-cols-[40px_1fr_80px] sm:grid-cols-[50px_1fr_100px] gap-2 sm:gap-4 items-center px-4 sm:px-6 py-3 sm:py-4 rounded-xl transition-all hover:bg-white/5 relative
|
|
${row.isUser ? 'bg-[#3B82F6]/10 border border-[#3B82F6]/50 shadow-[0_0_20px_rgba(59,130,246,0.15)] z-10' : 'bg-zinc-100 dark:bg-black/30 border border-zinc-200 dark:border-white/5 z-0'}`}>
|
|
|
|
{row.isUser && (
|
|
<div className="absolute inset-0 border-2 border-[#3B82F6] rounded-xl opacity-0 animate-[pulse_2s_infinite]" />
|
|
)}
|
|
|
|
<div className="flex justify-center">
|
|
{i === 0 ? (
|
|
<div className="w-8 h-8 rounded-full flex items-center justify-center shadow-lg bg-[#FFD700]">
|
|
<Trophy size={16} className="text-black" />
|
|
</div>
|
|
) : i === 1 ? (
|
|
<div className="w-8 h-8 rounded-full flex items-center justify-center shadow-lg bg-[#C0C0C0]">
|
|
<Trophy size={16} className="text-black" />
|
|
</div>
|
|
) : i === 2 ? (
|
|
<div className="w-8 h-8 rounded-full flex items-center justify-center shadow-lg bg-[#CD7F32]">
|
|
<Trophy size={16} className="text-black" />
|
|
</div>
|
|
) : (
|
|
<span className="font-['Barlow_Condensed'] font-black text-2xl text-zinc-400 dark:text-white/50">{i + 1}.</span>
|
|
)}
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4">
|
|
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-full bg-zinc-200 dark:bg-zinc-800 flex items-center justify-center font-bold text-zinc-700 dark:text-white/80 border border-zinc-300 dark:border-white/10 shrink-0 text-xs sm:text-base">
|
|
{row.avatar}
|
|
</div>
|
|
<span className={`font-['Barlow_Condensed'] font-black text-lg sm:text-xl tracking-wide ${row.isUser ? 'text-[#3B82F6]' : 'text-zinc-500 dark:text-white'}`}>
|
|
{row.name}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="text-right">
|
|
<span className={`font-['Barlow_Condensed'] font-black text-xl sm:text-2xl ${row.isUser ? 'text-[#3B82F6]' : 'text-zinc-400 dark:text-white/80'}`}>
|
|
<CountUp to={row.points} duration={1000} />
|
|
</span>
|
|
</div>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</GlassPanel>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
{/* FIXED BOTTOM NAVIGATION */}
|
|
<div className="fixed bottom-0 inset-x-0 h-[80px] sm:h-[100px] bg-white/95 dark:bg-zinc-950/90 backdrop-blur-2xl border-t border-zinc-200 dark:border-white/10 z-50 flex items-center justify-center pointer-events-none">
|
|
<div className="max-w-[1000px] w-full px-2 sm:px-6 flex items-center justify-between pointer-events-auto">
|
|
{[
|
|
{ label: 'Map', icon: MapIcon, active: true },
|
|
{ label: 'Leads', icon: Users, active: false },
|
|
{ label: 'Missions', icon: Target, active: false },
|
|
{ label: 'Ranks', icon: Trophy, active: false },
|
|
{ label: 'Rewards', icon: Gift, active: false },
|
|
].map((item, i) => (
|
|
<div key={i} className="flex flex-col items-center justify-center gap-1.5 sm:gap-2 cursor-pointer group px-2 sm:px-6 py-2 sm:py-3 w-1/5">
|
|
<div className={`relative w-10 h-10 sm:w-14 sm:h-14 rounded-full flex items-center justify-center transition-all duration-300
|
|
${item.active ? 'bg-[#3B82F6] text-white shadow-[0_0_20px_rgba(59,130,246,0.5)] scale-110' : 'bg-transparent text-zinc-400 dark:text-white/40 group-hover:bg-zinc-100 dark:group-hover:bg-white/10 group-hover:text-zinc-700 dark:group-hover:text-white/80'}`}>
|
|
<item.icon className="w-5 h-5 sm:w-[26px] sm:h-[26px]" />
|
|
{item.active && <div className="absolute -bottom-2 sm:-bottom-3 w-1 h-1 sm:w-1.5 sm:h-1.5 rounded-full bg-[#3B82F6] shadow-[0_0_10px_#3B82F6]" />}
|
|
</div>
|
|
<span className={`font-['Barlow_Condensed'] font-bold text-[10px] sm:text-xs uppercase tracking-widest transition-colors mt-0.5 sm:mt-1 hidden min-[350px]:block
|
|
${item.active ? 'text-white' : 'text-zinc-400 dark:text-white/40 group-hover:text-zinc-700 dark:group-hover:text-white/80'}`}>
|
|
{item.label}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Background Texture Overlay */}
|
|
<div className="fixed inset-0 bg-[url('https://www.transparenttextures.com/patterns/cubes.png')] opacity-[0.015] pointer-events-none z-0" />
|
|
|
|
{/* Modal Injection */}
|
|
<AnimatePresence>
|
|
{logAction && <LogActionModal actionKey={logAction} onClose={() => setLogAction(null)} gs={gs} />}
|
|
{showBadges && <BadgesModal onClose={() => setShowBadges(false)} gs={gs} />}
|
|
{showRewards && <RewardsModal onClose={() => setShowRewards(false)} gs={gs} />}
|
|
</AnimatePresence>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default function ProCanvas() {
|
|
return (
|
|
<GamificationProvider>
|
|
<ProCanvasContent />
|
|
</GamificationProvider>
|
|
);
|
|
}
|