Auto commit: 2026-02-17T19:08:09.489Z

This commit is contained in:
Flatlogic Bot 2026-02-17 19:08:09 +00:00
parent 09b1594a66
commit d10f6425ae
4 changed files with 243 additions and 22 deletions

View File

@ -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()]);

View File

@ -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
View File

@ -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 {

View File

@ -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']): ?>