Auto commit: 2026-02-17T19:08:09.489Z
This commit is contained in:
parent
09b1594a66
commit
d10f6425ae
24
api/chat.php
24
api/chat.php
@ -21,9 +21,31 @@ if ($method === 'GET') {
|
||||
|
||||
db()->query("DELETE FROM messages WHERE created_at < DATE_SUB(NOW(), INTERVAL 6 HOUR)");
|
||||
|
||||
$stmt = db()->prepare("SELECT m.*, ul.custom_color FROM messages m LEFT JOIN user_likes ul ON m.username = ul.username ORDER BY m.created_at DESC LIMIT 50");
|
||||
$stmt = db()->prepare("SELECT m.*, f.points, f.is_fan_of_month FROM messages m LEFT JOIN fans f ON m.username = f.name ORDER BY m.created_at DESC LIMIT 50");
|
||||
$stmt->execute();
|
||||
$messages = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($messages as &$msg) {
|
||||
$points = $msg['points'] ?? 0;
|
||||
$msg['level_color'] = '#94a3b8';
|
||||
$msg['level_emoji'] = '';
|
||||
|
||||
if ($msg['is_fan_of_month']) {
|
||||
$msg['level_color'] = '#facc15';
|
||||
} elseif ($points >= 2500) {
|
||||
$msg['level_color'] = '#a855f7';
|
||||
$msg['level_emoji'] = '👑';
|
||||
} elseif ($points >= 1000) {
|
||||
$msg['level_color'] = '#f97316';
|
||||
$msg['level_emoji'] = '🔥';
|
||||
} elseif ($points >= 500) {
|
||||
$msg['level_color'] = '#22c55e';
|
||||
$msg['level_emoji'] = '⭐';
|
||||
} elseif ($points >= 100) {
|
||||
$msg['level_color'] = '#3b82f6';
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode(array_reverse($messages));
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
|
||||
@ -5,26 +5,89 @@ header('Content-Type: application/json');
|
||||
|
||||
try {
|
||||
$pdo = db();
|
||||
$username = $_GET['username'] ?? null;
|
||||
|
||||
// Get top fans from the fans table
|
||||
$stmt = $pdo->query("SELECT name as username, points as total_likes, photo, is_fan_of_month FROM fans ORDER BY points DESC LIMIT 5");
|
||||
$fans = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Add badge logic
|
||||
foreach ($fans as &$fan) {
|
||||
$fan['badges'] = [];
|
||||
if ($fan['is_fan_of_month']) {
|
||||
$fan['badges'][] = ['icon' => 'bi-star-fill', 'color' => '#facc15', 'label' => 'Fan del Mes'];
|
||||
// If username provided and not in top 5, fetch it specifically
|
||||
if ($username) {
|
||||
$found = false;
|
||||
foreach ($fans as $f) {
|
||||
if (strtolower($f['username']) === strtolower($username)) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($fan['total_likes'] >= 1000) {
|
||||
$fan['badges'][] = ['icon' => 'bi-gem', 'color' => '#38bdf8', 'label' => 'Diamante'];
|
||||
} elseif ($fan['total_likes'] >= 500) {
|
||||
$fan['badges'][] = ['icon' => 'bi-shield-check', 'color' => '#00c853', 'label' => 'Veterano'];
|
||||
if (!$found) {
|
||||
$stmt = $pdo->prepare("SELECT name as username, points as total_likes, photo, is_fan_of_month FROM fans WHERE name = ?");
|
||||
$stmt->execute([$username]);
|
||||
$userFan = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
if ($userFan) {
|
||||
$userFan['is_extra'] = true;
|
||||
$fans[] = $userFan;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom color fallback
|
||||
$fan['custom_color'] = $fan['is_fan_of_month'] ? '#facc15' : '#38bdf8';
|
||||
}
|
||||
|
||||
// Add level and progress logic
|
||||
foreach ($fans as &$fan) {
|
||||
$fan['badges'] = [];
|
||||
$points = $fan['total_likes'];
|
||||
|
||||
// Level Calculation
|
||||
if ($points >= 2500) {
|
||||
$fan['level'] = 5;
|
||||
$fan['level_name'] = 'Leyenda';
|
||||
$fan['level_color'] = '#a855f7'; // Purple
|
||||
$fan['next_level_points'] = 0;
|
||||
$fan['level_progress'] = 100;
|
||||
$fan['unlocked_emoji'] = '👑';
|
||||
} elseif ($points >= 1000) {
|
||||
$fan['level'] = 4;
|
||||
$fan['level_name'] = 'Super Fan';
|
||||
$fan['level_color'] = '#f97316'; // Orange
|
||||
$fan['next_level_points'] = 2500;
|
||||
$fan['level_progress'] = (($points - 1000) / (2500 - 1000)) * 100;
|
||||
$fan['unlocked_emoji'] = '🔥';
|
||||
} elseif ($points >= 500) {
|
||||
$fan['level'] = 3;
|
||||
$fan['level_name'] = 'Fan Leal';
|
||||
$fan['level_color'] = '#22c55e'; // Green
|
||||
$fan['next_level_points'] = 1000;
|
||||
$fan['level_progress'] = (($points - 500) / (1000 - 500)) * 100;
|
||||
$fan['unlocked_emoji'] = '⭐';
|
||||
} elseif ($points >= 100) {
|
||||
$fan['level'] = 2;
|
||||
$fan['level_name'] = 'Oyente Frecuente';
|
||||
$fan['level_color'] = '#3b82f6'; // Blue
|
||||
$fan['next_level_points'] = 500;
|
||||
$fan['level_progress'] = (($points - 100) / (500 - 100)) * 100;
|
||||
$fan['unlocked_emoji'] = '';
|
||||
} else {
|
||||
$fan['level'] = 1;
|
||||
$fan['level_name'] = 'Novato';
|
||||
$fan['level_color'] = '#94a3b8'; // Slate
|
||||
$fan['next_level_points'] = 100;
|
||||
$fan['level_progress'] = ($points / 100) * 100;
|
||||
$fan['unlocked_emoji'] = '';
|
||||
}
|
||||
|
||||
if ($fan['is_fan_of_month']) {
|
||||
$fan['badges'][] = ['icon' => 'bi-star-fill', 'color' => '#facc15', 'label' => 'Fan del Mes'];
|
||||
}
|
||||
if ($points >= 1000) {
|
||||
$fan['badges'][] = ['icon' => 'bi-gem', 'color' => '#38bdf8', 'label' => 'Diamante'];
|
||||
} elseif ($points >= 500) {
|
||||
$fan['badges'][] = ['icon' => 'bi-shield-check', 'color' => '#00c853', 'label' => 'Veterano'];
|
||||
}
|
||||
|
||||
// Custom color based on level
|
||||
$fan['custom_color'] = $fan['level_color'];
|
||||
if ($fan['is_fan_of_month']) $fan['custom_color'] = '#facc15';
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'fans' => $fans]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
|
||||
128
index.php
128
index.php
@ -1247,6 +1247,50 @@ $twitter_link = "https://twitter.com/";
|
||||
.badge-icon {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
/* Level System Styles */
|
||||
.level-badge {
|
||||
font-size: 0.65rem;
|
||||
font-weight: 800;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: white;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
|
||||
}
|
||||
.level-progress-container {
|
||||
margin-top: 1rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: 1px solid var(--glass-border);
|
||||
border-radius: 20px;
|
||||
padding: 1rem;
|
||||
animation: fadeIn 0.5s ease;
|
||||
}
|
||||
.level-bar-bg {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 4px;
|
||||
margin-top: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.level-bar-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
|
||||
width: 0%;
|
||||
transition: width 1s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
box-shadow: 0 0 10px var(--primary-color);
|
||||
}
|
||||
.unlocked-reward {
|
||||
font-size: 0.7rem;
|
||||
color: var(--accent-color);
|
||||
margin-top: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-weight: 700;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -1388,7 +1432,7 @@ $twitter_link = "https://twitter.com/";
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="user-name">NOMBRE</label>
|
||||
<input type="text" id="user-name" placeholder="Tu nombre..." oninput="this.style.borderColor='rgba(255, 255, 255, 0.2)'">
|
||||
<input type="text" id="user-name" placeholder="Tu nombre..." oninput="this.style.borderColor='rgba(255, 255, 255, 0.2)'; if(this.value.length >= 3) fetchLeaderboard();">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@ -1438,6 +1482,23 @@ $twitter_link = "https://twitter.com/";
|
||||
<i class="bi bi-trophy-fill" style="font-size: 1.5rem; color: #facc15;"></i>
|
||||
</div>
|
||||
|
||||
<!-- User Level Progress Section -->
|
||||
<div id="user-progress-container" class="level-progress-container" style="display: none;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<span id="user-level-badge" class="level-badge">Nivel 1</span>
|
||||
<span id="user-level-name" style="font-size: 0.85rem; font-weight: 700;">Novato</span>
|
||||
</div>
|
||||
<span id="user-points-display" style="font-size: 0.75rem; opacity: 0.7;">0 / 100 pts</span>
|
||||
</div>
|
||||
<div class="level-bar-bg">
|
||||
<div id="user-level-bar" class="level-bar-fill"></div>
|
||||
</div>
|
||||
<div id="unlocked-rewards-msg" class="unlocked-reward">
|
||||
<i class="bi bi-unlock-fill"></i> Siguiente: Color de chat azul
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Espacio para Código QR -->
|
||||
<div class="payment-section">
|
||||
<div class="qr-placeholder" style="padding: 0; overflow: hidden; background: rgba(255,255,255,0.1);" onclick="openQRModal()">
|
||||
@ -2042,7 +2103,8 @@ $twitter_link = "https://twitter.com/";
|
||||
const div = document.createElement('div');
|
||||
const isLike = msg.message.includes('❤️');
|
||||
|
||||
const customColor = msg.custom_color ? `color: ${msg.custom_color} !important;` : '';
|
||||
const nameColor = msg.level_color || (msg.is_fan_of_month ? '#facc15' : 'var(--primary-color)');
|
||||
const levelEmoji = msg.level_emoji || '';
|
||||
|
||||
div.style.background = isLike ? 'rgba(255, 68, 68, 0.15)' : 'rgba(255,255,255,0.05)';
|
||||
div.style.padding = '0.8rem';
|
||||
@ -2057,8 +2119,8 @@ $twitter_link = "https://twitter.com/";
|
||||
}
|
||||
|
||||
div.innerHTML = `
|
||||
<div style="font-size: 0.7rem; font-weight: bold; ${customColor || (isLike ? 'color: #ff4444' : 'var(--primary-color)')}; margin-bottom: 2px;">
|
||||
${msg.username}
|
||||
<div style="font-size: 0.7rem; font-weight: bold; color: ${nameColor}; margin-bottom: 2px; display: flex; align-items: center; gap: 4px;">
|
||||
${msg.username} ${levelEmoji ? `<span style="font-size: 0.8rem;">${levelEmoji}</span>` : ''}
|
||||
</div>
|
||||
${content}
|
||||
<div style="font-size: 0.6rem; opacity: 0.4; margin-top: 4px; text-align: right;">${new Date(msg.created_at).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})}</div>
|
||||
@ -2564,8 +2626,9 @@ $twitter_link = "https://twitter.com/";
|
||||
|
||||
async function fetchLeaderboard() {
|
||||
const container = document.getElementById('leaderboard-list');
|
||||
const currentUserName = document.getElementById('user-name').value.trim();
|
||||
try {
|
||||
const response = await fetch('api/get_top_fans.php');
|
||||
const response = await fetch(`api/get_top_fans.php?username=${encodeURIComponent(currentUserName)}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
@ -2574,25 +2637,74 @@ $twitter_link = "https://twitter.com/";
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = data.fans.map((fan, index) => `
|
||||
// Filter out extra user if they are not in top 5 for the list display,
|
||||
// but we use them for the progress bar.
|
||||
const topFans = data.fans.filter(f => !f.is_extra);
|
||||
|
||||
container.innerHTML = topFans.map((fan, index) => `
|
||||
<div class="leaderboard-item">
|
||||
<div class="leaderboard-rank">${index + 1}</div>
|
||||
<img src="${fan.photo || 'assets/pasted-20260215-163754-def41f49.png'}" alt="${fan.username}" class="leaderboard-avatar">
|
||||
<div class="leaderboard-info">
|
||||
<div class="leaderboard-name">
|
||||
${fan.username}
|
||||
<span style="color: ${fan.level_color};">${fan.username}</span>
|
||||
${fan.unlocked_emoji ? `<span style="font-size: 0.8rem;">${fan.unlocked_emoji}</span>` : ''}
|
||||
${fan.badges.map(b => `<i class="bi ${b.icon}" style="color: ${b.color};" title="${b.label}"></i>`).join('')}
|
||||
</div>
|
||||
<div class="leaderboard-points">${parseInt(fan.total_likes).toLocaleString()} puntos</div>
|
||||
<div style="display: flex; align-items: center; gap: 8px;">
|
||||
<span class="level-badge" style="background: ${fan.level_color}; font-size: 0.55rem;">Nivel ${fan.level}</span>
|
||||
<div class="leaderboard-points">${parseInt(fan.total_likes).toLocaleString()} pts</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
// Update current user's progress
|
||||
if (currentUserName) {
|
||||
const currentUserData = data.fans.find(f => f.username.toLowerCase() === currentUserName.toLowerCase());
|
||||
if (currentUserData) {
|
||||
showUserProgress(currentUserData);
|
||||
} else {
|
||||
document.getElementById('user-progress-container').style.display = 'none';
|
||||
}
|
||||
} else {
|
||||
document.getElementById('user-progress-container').style.display = 'none';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
container.innerHTML = '<div style="opacity: 0.5; font-size: 0.85rem; text-align: center; padding: 1rem; color: #ff4444;">Error al cargar el ranking.</div>';
|
||||
}
|
||||
}
|
||||
|
||||
function showUserProgress(fan) {
|
||||
const container = document.getElementById('user-progress-container');
|
||||
const badge = document.getElementById('user-level-badge');
|
||||
const name = document.getElementById('user-level-name');
|
||||
const pointsDisplay = document.getElementById('user-points-display');
|
||||
const bar = document.getElementById('user-level-bar');
|
||||
const rewards = document.getElementById('unlocked-rewards-msg');
|
||||
|
||||
container.style.display = 'block';
|
||||
badge.innerText = `Nivel ${fan.level}`;
|
||||
badge.style.background = fan.level_color;
|
||||
name.innerText = fan.level_name;
|
||||
name.style.color = fan.level_color;
|
||||
|
||||
pointsDisplay.innerText = `${parseInt(fan.total_likes).toLocaleString()} / ${fan.next_level_points || 'MAX'} pts`;
|
||||
bar.style.width = `${fan.level_progress}%`;
|
||||
bar.style.boxShadow = `0 0 10px ${fan.level_color}`;
|
||||
|
||||
let nextReward = '';
|
||||
switch(fan.level) {
|
||||
case 1: nextReward = 'Siguiente: Color de chat azul (100 pts)'; break;
|
||||
case 2: nextReward = 'Siguiente: Color verde + ⭐ Emoji (500 pts)'; break;
|
||||
case 3: nextReward = 'Siguiente: Color naranja + 🔥 Emoji (1000 pts)'; break;
|
||||
case 4: nextReward = 'Siguiente: Color púrpura + 👑 Emoji (2500 pts)'; break;
|
||||
default: nextReward = '¡Nivel Máximo alcanzado! Eres una Leyenda.'; break;
|
||||
}
|
||||
rewards.innerHTML = `<i class="bi bi-unlock-fill"></i> ${nextReward}`;
|
||||
}
|
||||
|
||||
async function fetchGallery() {
|
||||
const container = document.getElementById('gallery-container');
|
||||
try {
|
||||
|
||||
26
ranking.php
26
ranking.php
@ -137,6 +137,16 @@ try {
|
||||
.fan-name { font-weight: 700; font-size: 1.1rem; }
|
||||
.fan-points { font-size: 0.8rem; opacity: 0.6; }
|
||||
|
||||
.level-badge {
|
||||
font-size: 0.6rem;
|
||||
font-weight: 800;
|
||||
padding: 2px 6px;
|
||||
border-radius: 8px;
|
||||
text-transform: uppercase;
|
||||
color: white;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.trophy { color: #facc15; font-size: 1.2rem; }
|
||||
|
||||
.empty-state {
|
||||
@ -167,6 +177,16 @@ try {
|
||||
<?php
|
||||
$rank = $index + 1;
|
||||
$rankClass = $rank <= 3 ? "rank-$rank" : "";
|
||||
$points = $fan['points'];
|
||||
|
||||
// Level Calculation
|
||||
if ($points >= 2500) { $level = 5; $levelColor = '#a855f7'; $emoji = '👑'; }
|
||||
elseif ($points >= 1000) { $level = 4; $levelColor = '#f97316'; $emoji = '🔥'; }
|
||||
elseif ($points >= 500) { $level = 3; $levelColor = '#22c55e'; $emoji = '⭐'; }
|
||||
elseif ($points >= 100) { $level = 2; $levelColor = '#3b82f6'; $emoji = ''; }
|
||||
else { $level = 1; $levelColor = '#94a3b8'; $emoji = ''; }
|
||||
|
||||
if ($fan['is_fan_of_month']) $levelColor = '#facc15';
|
||||
?>
|
||||
<div class="fan-item">
|
||||
<div class="rank <?= $rankClass ?>">
|
||||
@ -178,7 +198,11 @@ try {
|
||||
</div>
|
||||
<img src="<?= htmlspecialchars($fan['photo'] ?: 'assets/pasted-20260215-163754-def41f49.png') ?>" alt="<?= htmlspecialchars($fan['name']) ?>" class="fan-photo">
|
||||
<div class="fan-info">
|
||||
<div class="fan-name"><?= htmlspecialchars($fan['name']) ?></div>
|
||||
<div class="fan-name">
|
||||
<span style="color: <?= $levelColor ?>"><?= htmlspecialchars($fan['name']) ?></span>
|
||||
<?= $emoji ?>
|
||||
<span class="level-badge" style="background: <?= $levelColor ?>">Nivel <?= $level ?></span>
|
||||
</div>
|
||||
<div class="fan-points"><?= number_format($fan['points']) ?> puntos</div>
|
||||
</div>
|
||||
<?php if ($fan['is_fan_of_month']): ?>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user