File size: 3,814 Bytes
c95a333 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | import React, { useState, useEffect } from "react";
import { motion, AnimatePresence } from "framer-motion";
import { Medal, Star, Shield, Award } from "lucide-react";
interface ReputationSystemProps {
actionCount: number; // Trigger updates based on actions
}
const ReputationSystem: React.FC<ReputationSystemProps> = ({ actionCount }) => {
// Persist XP in local storage for demo purposes
const [xp, setXp] = useState(() => parseInt(localStorage.getItem("ride_xp") || "0"));
const [level, setLevel] = useState(1);
const [showLevelUp, setShowLevelUp] = useState(false);
useEffect(() => {
// Calculate Level: Level 1 = 0-100xp, Level 2 = 101-300xp, etc.
const newLevel = Math.floor(Math.sqrt(xp / 100)) + 1;
if (newLevel > level) {
setShowLevelUp(true);
setTimeout(() => setShowLevelUp(false), 3000);
}
setLevel(newLevel);
}, [xp]);
// Update XP when actionCount increments (parent tells us something happened)
useEffect(() => {
if (actionCount > 0) {
const gain = Math.floor(Math.random() * 20) + 10;
const newXp = xp + gain;
setXp(newXp);
localStorage.setItem("ride_xp", newXp.toString());
}
}, [actionCount]);
const getRankTitle = (lvl: number) => {
if (lvl < 2) return "Novice Observer";
if (lvl < 5) return "Data Scout";
if (lvl < 10) return "Pattern Analyst";
return "Neural Architect";
};
return (
<div className="relative group cursor-default">
{/* Level Up Notification */}
<AnimatePresence>
{showLevelUp && (
<motion.div
initial={{ opacity: 0, scale: 0.5, y: 50 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.5, y: -50 }}
className="fixed inset-0 z-50 flex items-center justify-center pointer-events-none"
>
<div className="bg-deep-bg/90 border-2 border-yellow-400 p-8 rounded-2xl flex flex-col items-center shadow-[0_0_50px_rgba(250,204,21,0.5)] backdrop-blur-xl">
<Award size={64} className="text-yellow-400 mb-4 animate-bounce" />
<h2 className="text-3xl font-bold text-white mb-2">LEVEL UP!</h2>
<p className="text-yellow-400 font-mono text-xl">Level {level}</p>
<p className="text-gray-400 mt-2">New privileges accessing...</p>
</div>
</motion.div>
)}
</AnimatePresence>
<div className="flex items-center gap-3 bg-deep-card/50 px-3 py-1.5 rounded-lg border border-white/5 hover:border-neon-blue/30 transition-colors">
<div className="relative">
<Shield className="w-8 h-8 text-neon-blue" />
<span className="absolute inset-0 flex items-center justify-center text-[10px] font-bold text-black">{level}</span>
</div>
<div>
<div className="text-xs text-gray-400 font-mono uppercase tracking-wider">{getRankTitle(level)}</div>
<div className="w-24 h-1.5 bg-gray-700 rounded-full mt-1 overflow-hidden">
<motion.div
className="h-full bg-gradient-to-r from-neon-blue to-neon-purple"
initial={{ width: 0 }}
animate={{ width: `${(xp % 100)}%` }} // Simplified progress
/>
</div>
</div>
</div>
</div>
);
};
export default ReputationSystem;
|