MiniApp Update

Added scoreboard
This commit is contained in:
Degradin 2025-01-21 09:43:10 +03:00
parent ff3bc2efea
commit f4fe2d2c06
5 changed files with 506 additions and 0 deletions

143
pages.js Normal file
View File

@ -0,0 +1,143 @@
require('dotenv').config();
const sequelize = require('./db'); // Подключение базы данных
// Настраиваем глобальные переменные (опционально)
global.path = require('path');
global.config = require('./config'); // Конфигурация
global.database = sequelize; // База данных
global.utils = require('./utils');
const { Op } = require('sequelize');
const express = require('express');
const { UserModel, CharacterModel, BusinessModel, EnterpriseModel, WarehouseModel } = global.config;
const app = express();
app.use(express.static('public'));
app.get('/scoreboard', (req, res) => {
res.sendFile(global.path.join(__dirname, 'public', 'scoreboard.html'));
});
app.get('/users', async (req, res) => {
const { sortBy } = req.query; // Sorting option passed as a query parameter
const sortOptions = {
money: [sequelize.literal('money'), 'DESC'],
level: [sequelize.literal('level'), 'DESC']
};
const topUser = await UserModel.findAll({
attributes: ['username', 'money', 'level', 'status'],
order: [sortOptions[sortBy] || sortOptions['money']], // Default sort by money
limit: 15
});
res.json(topUser.map(player => ({
username: player.username,
money: player.money,
level: player.level,
status: player.status
})));
});
app.get('/characters', async (req, res) => {
const { sortBy } = req.query; // Use query parameters to specify sorting
const sortOptions = {
dirtymoney: [sequelize.literal('dirtymoney'), 'DESC'],
level: [sequelize.literal('level'), 'DESC'],
force: [sequelize.literal('force'), 'DESC'],
intelligence: [sequelize.literal('intelligence'), 'DESC'],
resilience: [sequelize.literal('resilience'), 'DESC'],
endurance: [sequelize.literal('endurance'), 'DESC']
};
const topCharacter = await CharacterModel.findAll({
attributes: ['username', 'dirtymoney', 'level', 'force', 'intelligence', 'resilience', 'endurance'],
order: [sortOptions[sortBy] || sortOptions['dirtymoney']], // Default sort by dirtymoney
limit: 15
});
res.json(topCharacter.map(character => ({
username: character.username,
dirtymoney: character.dirtymoney,
level: character.level,
force: character.force,
intelligence: character.intelligence,
resilience: character.resilience,
endurance: character.endurance
})));
});
app.get('/enterprises', async (req, res) => {
const enterprises = await EnterpriseModel.findAll({
attributes: ['name', 'resourceType', 'level', 'efficiency', 'warehouseCapacity', 'currentResources', 'playerId']
});
const resourceIcons = {
wood: '🌳',
coal: '⛏️',
oil: '🛢️',
metall: '⚒️',
gold: '💰',
diamond: '💎'
};
const enterprisesData = await Promise.all(enterprises.map(async enterprise => {
const player = await UserModel.findOne({
attributes: ['name'],
where: { telegram_id: enterprise.playerId }
});
return {
name: enterprise.name,
resourceType: enterprise.resourceType,
level: enterprise.level,
efficiency: enterprise.efficiency,
warehouseCapacity: enterprise.warehouseCapacity,
currentResources: enterprise.currentResources,
owner: player ? player.name : 'Unknown',
icon: resourceIcons[enterprise.resourceType] || '❓'
};
}));
res.json(enterprisesData);
});
app.get('/businesses', async (req, res) => {
const { sortBy } = req.query; // Sorting option passed as a query parameter
const sortOptions = {
balance: [sequelize.literal('balance'), 'DESC'],
usersCount: [sequelize.literal('ARRAY_LENGTH(users, 1)'), 'DESC']
};
const businesses = await BusinessModel.findAll({
attributes: ['name', 'balance', 'users', 'owner'],
order: [sortOptions[sortBy] || sortOptions['balance']], // Default sort by balance
limit: 15
});
const businessesData = await Promise.all(businesses.map(async business => {
const owner = await UserModel.findOne({
attributes: ['username'],
where: { telegram_id: business.owner }
});
return {
name: business.name,
balance: business.balance,
usersCount: business.users.length,
owner: owner ? owner.username : 'Unknown'
};
}));
res.json(businessesData);
});
// Запуск сервера
const PORT = process.env.PORT || 3002;
app.listen(PORT, () => console.log(`Сервер запущен на порту ${PORT}`));

78
public/scoreboard.css Normal file
View File

@ -0,0 +1,78 @@
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #f4f4f9;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.tabs {
display: flex;
margin-bottom: 20px;
}
.tab-button {
padding: 10px 20px;
margin-right: 10px;
cursor: pointer;
background-color: #ddd;
border: none;
border-radius: 5px;
transition: background-color 0.3s;
}
.tab-button.active {
background-color: #0066cc;
color: white;
}
.tab-button:hover {
background-color: #005bb5;
}
.tab-content {
display: flex;
flex-direction: column;
}
.tab-pane {
display: none;
}
.tab-pane.active {
display: block;
}
h2 {
font-size: 24px;
margin-bottom: 10px;
}
.card {
background-color: white;
padding: 15px;
margin-bottom: 15px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.card h3 {
margin: 0;
font-size: 18px;
color: #333;
}
.card p {
margin: 5px 0;
color: #777;
}
.card .money {
font-weight: bold;
color: #0066cc;
}

191
public/scoreboard.html Normal file
View File

@ -0,0 +1,191 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Таблица лидеров</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-KyZXEJgP4v8Jf+K6cM7V3zv7+uCGo5F5O+Xq6bM+bB5HXitBzvvhQ2V5+fu+n9U7" crossorigin="anonymous">
<style>
body {
background-color: #121212;
color: #e0e0e0;
font-family: 'Roboto', sans-serif;
margin: 0;
padding: 0;
}
.container {
max-width: 1200px;
margin-top: 40px;
}
h1 {
font-size: 2.5rem;
text-align: center;
color: #d46600;
margin-bottom: 30px;
}
.btn-group {
margin-bottom: 30px;
display: flex;
justify-content: center;
}
.btn {
background-color: #444;
color: #ffffff;
border: 1px solid #333;
font-size: 1.2rem;
padding: 15px 25px;
margin: 0 10px;
border-radius: 8px;
transition: 0.3s ease;
}
.btn:hover {
background-color: #00bcd4;
color: #121212;
}
.card {
background-color: #1f1f1f;
border-radius: 10px;
margin-bottom: 20px;
border: 1px solid #333;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.6);
}
.card-header {
background-color: #222;
color: #d46600;
font-size: 1.3rem;
padding: 15px;
text-align: center;
}
.card-body {
padding: 20px;
background-color: #282828;
color: #b0b0b0;
}
.card-body p {
margin: 10px 0;
font-size: 1.1rem;
}
.money {
color: #00c853;
font-weight: bold;
}
.no-data {
text-align: center;
font-size: 1.5rem;
color: #888;
margin-top: 50px;
}
.icon {
font-size: 1.5rem;
color: #ffeb3b;
}
</style>
</head>
<body>
<div class="container">
<h1>Таблица Лидеров</h1>
<!-- Кнопки для выбора топа -->
<div class="btn-group" role="group">
<button type="button" class="btn" id="btn-users" onclick="fetchTop('users')">Топ Пользователей</button>
<button type="button" class="btn" id="btn-characters" onclick="fetchTop('characters')">Топ Персонажей</button>
<button type="button" class="btn" id="btn-enterprises" onclick="fetchTop('enterprises')">Топ Предприятий</button>
<button type="button" class="btn" id="btn-businesses" onclick="fetchTop('businesses')">Топ Бизнесов</button>
</div>
<!-- Контейнер для вывода данных -->
<div id="top-content"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js" integrity="sha384-oBqDVmMz4fnFO9gyb6I7gDbNf8gU5X7ATpTftdO5n8J2LrC7gg6tGSRW3p5p1p17" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.min.js" integrity="sha384-KyZXEJgP4v8Jf+K6cM7V3zv7+uCGo5F5O+Xq6bM+bB5HXitBzvvhQ2V5+fu+n9U7" crossorigin="anonymous"></script>
<script>
// Функция для загрузки данных топа
async function fetchTop(type) {
console.log(`Загрузка данных топа: ${type}`);
const response = await fetch(`/${type}`);
const data = await response.json();
const topContent = document.getElementById('top-content');
topContent.innerHTML = ''; // Очистка текущего контента
if (data.length === 0) {
topContent.innerHTML = `<div class="no-data">Нет данных для отображения.</div>`;
return;
}
switch (type) {
case 'users':
topContent.innerHTML = data.map(player => `
<div class="card">
<div class="card-header">
${player.username} - ${player.status === 'bronze' ? '[🔺]' : player.status === 'silver' ? '[🔹]' : '[🔸]'}
</div>
<div class="card-body">
<p>Баланс: <span class="money">${player.money} ₽</span></p>
<p>Уровень: ${player.level}</p>
</div>
</div>
`).join('');
break;
case 'characters':
topContent.innerHTML = data.map(character => `
<div class="card">
<div class="card-header">
${character.username}
</div>
<div class="card-body">
<p>Грязные деньги: ${character.dirtymoney} ₽</p>
<p>Уровень: ${character.level}</p>
<p>Сила: ${character.force}</p>
<p>Интеллект: ${character.intelligence}</p>
<p>Устойчивость: ${character.resilience}</p>
<p>Выносливость: ${character.endurance}</p>
</div>
</div>
`).join('');
break;
case 'enterprises':
topContent.innerHTML = data.map(enterprise => `
<div class="card">
<div class="card-header">
${enterprise.name} <span class="icon">${enterprise.icon}</span>
</div>
<div class="card-body">
<p>Тип ресурса: ${enterprise.resourceType}</p>
<p>Уровень: ${enterprise.level}</p>
<p>Производительность: ${enterprise.efficiency} ед./час</p>
<p>Емкость склада: ${enterprise.warehouseCapacity} ед.</p>
<p>Текущие ресурсы: ${enterprise.currentResources} ед.</p>
<p>Владелец: ${enterprise.owner}</p>
</div>
</div>
`).join('');
break;
case 'businesses':
topContent.innerHTML = data.map(business => `
<div class="card">
<div class="card-header">
${business.name}
</div>
<div class="card-body">
<p>Владелец: ${business.owner}</p>
<p>Баланс: ${business.balance} ₽</p>
<p>Пользователи: ${business.usersCount}</p>
</div>
</div>
`).join('');
break;
default:
topContent.innerHTML = `<div class="no-data">Неизвестный тип топа.</div>`;
break;
}
}
// Инициализация
fetchTop('users'); // Показываем топ пользователей по умолчанию
</script>
</body>
</html>

93
public/scoreboard.js Normal file
View File

@ -0,0 +1,93 @@
document.addEventListener('DOMContentLoaded', () => {
const tabs = document.querySelectorAll('.tab-button');
const tabPanes = document.querySelectorAll('.tab-pane');
// Handle tab navigation
tabs.forEach(tab => {
tab.addEventListener('click', () => {
tabs.forEach(t => t.classList.remove('active'));
tabPanes.forEach(pane => pane.classList.remove('active'));
tab.classList.add('active');
document.getElementById(tab.dataset.tab).classList.add('active');
});
});
// Fetch and display top users by balance
async function fetchTopUsers() {
const sortBy = document.getElementById('sort-users').value;
const response = await fetch(`/top-users?sortBy=${sortBy}`);
const data = await response.json();
const topUsersContainer = document.getElementById('top-users');
topUsersContainer.innerHTML = data.map(player => `
<div class="card">
<h3>${player.name} ${player.status === 'bronze' ? '[🔺]' : player.status === 'silver' ? '[🔹]' : '[🔸]'}</h3>
<p>Баланс: <span class="money">${player.money} </span></p>
<p>Уровень: ${player.level}</p>
</div>
`).join('');
}
// Fetch and display top characters by balance
async function fetchTopCharacters() {
const sortBy = document.getElementById('sort-characters').value;
const response = await fetch(`/top-characters?sortBy=${sortBy}`);
const data = await response.json();
const topCharactersContainer = document.getElementById('top-characters');
topCharactersContainer.innerHTML = data.map(character => `
<div class="card">
<h3>${character.name}</h3>
<p>Грязные деньги: ${character.dirtymoney} </p>
<p>Уровень: ${character.level}</p>
<p>Сила: ${character.force}</p>
<p>Интеллект: ${character.intelligence}</p>
<p>Устойчивость: ${character.resilience}</p>
<p>Выносливость: ${character.endurance}</p>
</div>
`).join('');
}
// Fetch and display enterprises
async function fetchEnterprises() {
const response = await fetch('/enterprises');
const data = await response.json();
const enterprisesContainer = document.getElementById('enterprises-list');
enterprisesContainer.innerHTML = data.map(enterprise => `
<div class="card">
<h3>${enterprise.name} ${enterprise.icon}</h3>
<p>Тип ресурса: ${enterprise.resourceType}</p>
<p>Уровень: ${enterprise.level}</p>
<p>Производительность: ${enterprise.efficiency} ед./час</p>
<p>Емкость склада: ${enterprise.warehouseCapacity} ед.</p>
<p>Текущие ресурсы: ${enterprise.currentResources} ед.</p>
<p>Владелец: ${enterprise.owner}</p>
</div>
`).join('');
}
// Fetch and display businesses
async function fetchBusinesses() {
const sortBy = document.getElementById('sort-businesses').value;
const response = await fetch(`/businesses?sortBy=${sortBy}`);
const data = await response.json();
const businessesContainer = document.getElementById('businesses-list');
businessesContainer.innerHTML = data.map(business => `
<div class="card">
<h3>${business.name}</h3>
<p>Владелец: ${business.owner}</p>
<p>Баланс: ${business.balance} </p>
<p>Пользователи: ${business.usersCount}</p>
</div>
`).join('');
}
// Initial fetches
fetchTopUsers();
fetchTopCharacters();
fetchEnterprises();
fetchBusinesses();
});

View File

@ -1,2 +1,3 @@
nodemon --ignore json/ .\server.js
nodemon --ignore json/ .\pages.js
pause