Dildo items, admin fixes, UI refactor
- Add 85 custom Dildo items (knife/skin pattern names, various rarities) - Create Dildo Case config (hidden, items obtained via contracts) - Fix admin user_detail.html nested script tag (Notify fallback removed) - Fix admin cases.html embedded script tag - Add notifications.js to admin base.html - Fix admin users.html: use counts instead of relationship lists - Replace old money format with |money filter across all templates - Standardize nav partial (_nav.html) with active_page highlighting - Fix RARITY_COLORS rename in activity sidebar - Fix toast animation (forwards) and remove duplicate CSS - Replace showToast() with Notify.* in upgrade page - Add cs2_simulator.db and *.log to .gitignore - Create remote-deploy.sh with DB exclusion
This commit is contained in:
@@ -2,3 +2,5 @@ __pycache__/
|
|||||||
*.pyc
|
*.pyc
|
||||||
*.pyo
|
*.pyo
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
cs2_simulator.db
|
||||||
|
*.log
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ from rpu import get_user_rpu, calculate_rpu_level, get_rpu_description
|
|||||||
admin_router = APIRouter(prefix="/admin", tags=["admin"])
|
admin_router = APIRouter(prefix="/admin", tags=["admin"])
|
||||||
templates = Jinja2Templates(directory="templates")
|
templates = Jinja2Templates(directory="templates")
|
||||||
|
|
||||||
|
def money_filter(value):
|
||||||
|
try:
|
||||||
|
return "{:,.0f}".format(float(value)).replace(",", " ")
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return "0"
|
||||||
|
|
||||||
|
templates.env.filters["money"] = money_filter
|
||||||
|
|
||||||
# Загрузка конфигов
|
# Загрузка конфигов
|
||||||
CASES_FILE = Path("cases.json")
|
CASES_FILE = Path("cases.json")
|
||||||
MAINPAGE_FILE = Path("mainpage.json")
|
MAINPAGE_FILE = Path("mainpage.json")
|
||||||
@@ -137,8 +145,9 @@ async def admin_users(
|
|||||||
"balance": u.balance,
|
"balance": u.balance,
|
||||||
"is_admin": u.is_admin,
|
"is_admin": u.is_admin,
|
||||||
"created_at": u.created_at,
|
"created_at": u.created_at,
|
||||||
"inventory_items": u.inventory_items,
|
"case_openings": db.query(CaseOpening).filter(CaseOpening.user_id == u.id).count(),
|
||||||
"case_openings": u.case_openings,
|
"contracts": db.query(Contract).filter(Contract.user_id == u.id).count(),
|
||||||
|
"inventory_count": db.query(InventoryItem).filter(InventoryItem.user_id == u.id).count(),
|
||||||
"rpu_level": round(calculate_rpu_level(rpu))
|
"rpu_level": round(calculate_rpu_level(rpu))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1448,5 +1448,13 @@
|
|||||||
],
|
],
|
||||||
"image_url": "",
|
"image_url": "",
|
||||||
"description": ""
|
"description": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dildo_case",
|
||||||
|
"display_name": "Dildo Case",
|
||||||
|
"price_open": 149.0,
|
||||||
|
"items": [],
|
||||||
|
"image_url": "",
|
||||||
|
"description": "Самые сомнительные предметы коллекции Dildo"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
+2473
-13
File diff suppressed because it is too large
Load Diff
+69
-12
@@ -58,6 +58,14 @@ app.include_router(admin_router)
|
|||||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||||
templates = Jinja2Templates(directory="templates")
|
templates = Jinja2Templates(directory="templates")
|
||||||
|
|
||||||
|
def money_filter(value):
|
||||||
|
try:
|
||||||
|
return "{:,.0f}".format(float(value)).replace(",", " ")
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return "0"
|
||||||
|
|
||||||
|
templates.env.filters["money"] = money_filter
|
||||||
|
|
||||||
# Создаем отдельный роутер для API оригинального симулятора
|
# Создаем отдельный роутер для API оригинального симулятора
|
||||||
from backend import app as api_app
|
from backend import app as api_app
|
||||||
|
|
||||||
@@ -301,7 +309,8 @@ async def index(request: Request, user: Optional[User] = Depends(get_current_use
|
|||||||
|
|
||||||
return templates.TemplateResponse("index.html", {
|
return templates.TemplateResponse("index.html", {
|
||||||
"request": request,
|
"request": request,
|
||||||
"user": None
|
"user": None,
|
||||||
|
"active_page": "home"
|
||||||
})
|
})
|
||||||
|
|
||||||
@app.get("/login", response_class=HTMLResponse)
|
@app.get("/login", response_class=HTMLResponse)
|
||||||
@@ -312,7 +321,8 @@ async def login_page(request: Request, user: Optional[User] = Depends(get_curren
|
|||||||
|
|
||||||
return templates.TemplateResponse("login.html", {
|
return templates.TemplateResponse("login.html", {
|
||||||
"request": request,
|
"request": request,
|
||||||
"user": None
|
"user": None,
|
||||||
|
"active_page": "login"
|
||||||
})
|
})
|
||||||
|
|
||||||
@app.get("/register", response_class=HTMLResponse)
|
@app.get("/register", response_class=HTMLResponse)
|
||||||
@@ -323,7 +333,8 @@ async def register_page(request: Request, user: Optional[User] = Depends(get_cur
|
|||||||
|
|
||||||
return templates.TemplateResponse("register.html", {
|
return templates.TemplateResponse("register.html", {
|
||||||
"request": request,
|
"request": request,
|
||||||
"user": None
|
"user": None,
|
||||||
|
"active_page": "register"
|
||||||
})
|
})
|
||||||
|
|
||||||
@app.get("/profile", response_class=HTMLResponse)
|
@app.get("/profile", response_class=HTMLResponse)
|
||||||
@@ -403,7 +414,8 @@ async def profile_page(
|
|||||||
"types": sorted(types),
|
"types": sorted(types),
|
||||||
"rarity_order": RARITY_ORDER,
|
"rarity_order": RARITY_ORDER,
|
||||||
"is_public": False,
|
"is_public": False,
|
||||||
"profile_user": user
|
"profile_user": user,
|
||||||
|
"active_page": "profile"
|
||||||
})
|
})
|
||||||
|
|
||||||
# ═══════════════════════════════════════════════════════════════════════════════
|
# ═══════════════════════════════════════════════════════════════════════════════
|
||||||
@@ -436,9 +448,42 @@ async def public_profile_page(
|
|||||||
|
|
||||||
inventory_items = db.query(InventoryItem).filter(
|
inventory_items = db.query(InventoryItem).filter(
|
||||||
InventoryItem.user_id == profile_user.id
|
InventoryItem.user_id == profile_user.id
|
||||||
).all()
|
).order_by(desc(InventoryItem.obtained_at)).all()
|
||||||
|
|
||||||
rarity_order = {k: v for k, v in RARITY_ORDER.items()}
|
rarity_order = {k: v for k, v in RARITY_ORDER.items()}
|
||||||
|
|
||||||
|
rarities = set()
|
||||||
|
types = set()
|
||||||
|
enriched_items = []
|
||||||
|
for item in inventory_items:
|
||||||
|
item_data = None
|
||||||
|
for idx, it in enumerate(ALL_ITEMS):
|
||||||
|
if it.get("_id", idx) == item.item_id:
|
||||||
|
item_data = it
|
||||||
|
break
|
||||||
|
if not item_data:
|
||||||
|
item_data = get_item(item.item_id)
|
||||||
|
image_url = ""
|
||||||
|
price_rub = 100
|
||||||
|
if item_data:
|
||||||
|
image_url = item_data.get("image_url", "")
|
||||||
|
price_rub = item_data.get("price_rub", 100)
|
||||||
|
enriched_items.append({
|
||||||
|
"id": item.id,
|
||||||
|
"item_id": item.item_id,
|
||||||
|
"name": item.market_hash_name,
|
||||||
|
"rarity": item.rarity,
|
||||||
|
"wear": item.wear,
|
||||||
|
"float": item.float_value,
|
||||||
|
"type": item.type,
|
||||||
|
"image_url": image_url,
|
||||||
|
"price_rub": price_rub
|
||||||
|
})
|
||||||
|
if item.rarity:
|
||||||
|
rarities.add(item.rarity)
|
||||||
|
if item.type:
|
||||||
|
types.add(item.type)
|
||||||
|
|
||||||
valuable_items = sorted(
|
valuable_items = sorted(
|
||||||
inventory_items,
|
inventory_items,
|
||||||
key=lambda x: rarity_order.get(x.rarity, 0),
|
key=lambda x: rarity_order.get(x.rarity, 0),
|
||||||
@@ -455,7 +500,12 @@ async def public_profile_page(
|
|||||||
"contracts_completed": contracts_completed,
|
"contracts_completed": contracts_completed,
|
||||||
"recent_openings": recent_openings,
|
"recent_openings": recent_openings,
|
||||||
"recent_contracts": recent_contracts,
|
"recent_contracts": recent_contracts,
|
||||||
"valuable_items": valuable_items
|
"valuable_items": valuable_items,
|
||||||
|
"inventory_items": enriched_items,
|
||||||
|
"rarities": sorted(rarities),
|
||||||
|
"types": sorted(types),
|
||||||
|
"rarity_order": RARITY_ORDER,
|
||||||
|
"active_page": "profile"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -539,7 +589,8 @@ async def cases_page(
|
|||||||
return templates.TemplateResponse("cases.html", {
|
return templates.TemplateResponse("cases.html", {
|
||||||
"request": request,
|
"request": request,
|
||||||
"user": user,
|
"user": user,
|
||||||
"sections": sections
|
"sections": sections,
|
||||||
|
"active_page": "cases"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -598,7 +649,8 @@ async def contracts_page(
|
|||||||
"user": user,
|
"user": user,
|
||||||
"inventory_items": all_items + knife_items,
|
"inventory_items": all_items + knife_items,
|
||||||
"has_contract": has_contract or has_knife_contract,
|
"has_contract": has_contract or has_knife_contract,
|
||||||
"knife_count": len(knife_items)
|
"knife_count": len(knife_items),
|
||||||
|
"active_page": "contracts"
|
||||||
})
|
})
|
||||||
|
|
||||||
@app.get("/inventory")
|
@app.get("/inventory")
|
||||||
@@ -1432,7 +1484,8 @@ async def case_detail_page(
|
|||||||
"items_by_rarity": items_by_rarity,
|
"items_by_rarity": items_by_rarity,
|
||||||
"sorted_rarities": sorted_rarities,
|
"sorted_rarities": sorted_rarities,
|
||||||
"rarities_count": rarities_count,
|
"rarities_count": rarities_count,
|
||||||
"case_items": case_items_json
|
"case_items": case_items_json,
|
||||||
|
"active_page": "cases"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -2175,7 +2228,8 @@ async def upgrade_page(
|
|||||||
"total_attempts": total_attempts,
|
"total_attempts": total_attempts,
|
||||||
"total_success": total_success,
|
"total_success": total_success,
|
||||||
"success_rate": round(success_rate, 1),
|
"success_rate": round(success_rate, 1),
|
||||||
"recent_upgrades": recent_upgrades
|
"recent_upgrades": recent_upgrades,
|
||||||
|
"active_page": "upgrade"
|
||||||
})
|
})
|
||||||
|
|
||||||
@app.post("/web/api/upgrade/calculate")
|
@app.post("/web/api/upgrade/calculate")
|
||||||
@@ -2438,7 +2492,8 @@ async def achievements_page(
|
|||||||
"user": user,
|
"user": user,
|
||||||
"achievements": achievements,
|
"achievements": achievements,
|
||||||
"total": total,
|
"total": total,
|
||||||
"unlocked": unlocked
|
"unlocked": unlocked,
|
||||||
|
"active_page": "achievements"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -2479,7 +2534,8 @@ async def activity_page(
|
|||||||
"created_at": a.created_at.isoformat()
|
"created_at": a.created_at.isoformat()
|
||||||
}
|
}
|
||||||
for a in activities
|
for a in activities
|
||||||
]
|
],
|
||||||
|
"active_page": "activity"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -2614,6 +2670,7 @@ async def crash_page(
|
|||||||
return templates.TemplateResponse("crash.html", {
|
return templates.TemplateResponse("crash.html", {
|
||||||
"request": request,
|
"request": request,
|
||||||
"user": user,
|
"user": user,
|
||||||
|
"active_page": "crash"
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Executable
+16
@@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Deploy to remote server, excluding DB and other local-only files
|
||||||
|
rsync -av \
|
||||||
|
--exclude '.git' \
|
||||||
|
--exclude '__pycache__' \
|
||||||
|
--exclude '*.pyc' \
|
||||||
|
--exclude '.gitignore' \
|
||||||
|
--exclude 'cs2_simulator.db' \
|
||||||
|
--exclude '*.log' \
|
||||||
|
--exclude 'remote-deploy.sh' \
|
||||||
|
--delete \
|
||||||
|
/root/dodep-simulator/ \
|
||||||
|
root@zernmc.ru:/root/dodep/
|
||||||
|
|
||||||
|
ssh root@zernmc.ru "cd /root/dodep && sleep 1 && pkill -f 'frontend:app' 2>/dev/null; setsid python3 -m uvicorn frontend:app --host 0.0.0.0 --port 13669 > /tmp/frontend.log 2>&1 &"
|
||||||
|
echo "Deployed and restarted."
|
||||||
+1
-44
@@ -3984,49 +3984,6 @@ h1, h2, h3, h4, h5, h6, .logo, .nav-link, .btn {
|
|||||||
|
|
||||||
/* Результат */
|
/* Результат */
|
||||||
/* Toast */
|
/* Toast */
|
||||||
.toast-container {
|
|
||||||
position: fixed;
|
|
||||||
top: 70px;
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
z-index: 1000;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.4rem;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast {
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
font-weight: 600;
|
|
||||||
white-space: nowrap;
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-10px);
|
|
||||||
transition: all 0.3s cubic-bezier(0.2, 0.9, 0.3, 1.2);
|
|
||||||
pointer-events: auto;
|
|
||||||
background: var(--bg-card);
|
|
||||||
border: 1px solid var(--border-color);
|
|
||||||
color: var(--text-primary);
|
|
||||||
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast.show {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast.success {
|
|
||||||
border-color: #10b981;
|
|
||||||
box-shadow: 0 4px 20px rgba(16,185,129,0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast.fail {
|
|
||||||
border-color: #ef4444;
|
|
||||||
box-shadow: 0 4px 20px rgba(239,68,68,0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.upgrade-page .result-item-display img {
|
.upgrade-page .result-item-display img {
|
||||||
width: 72px;
|
width: 72px;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
@@ -4515,7 +4472,7 @@ a:not(.btn):not(.nav-link):not(.logo):hover {
|
|||||||
gap: 10px;
|
gap: 10px;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
|
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
|
||||||
animation: toastSlideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
animation: toastSlideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards;
|
||||||
transform-origin: top right;
|
transform-origin: top right;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|||||||
@@ -172,40 +172,37 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const list = document.getElementById('activitySidebarList');
|
const list = document.getElementById('activitySidebarList');
|
||||||
const sidebar = document.getElementById('activitySidebar');
|
const sidebar = document.getElementById('activitySidebar');
|
||||||
document.body.classList.add('has-activity-sidebar');
|
document.body.classList.add('has-activity-sidebar');
|
||||||
|
|
||||||
// Восстанавливаем состояние из localStorage
|
|
||||||
if (localStorage.getItem('sidebar_closed') === 'true') {
|
if (localStorage.getItem('sidebar_closed') === 'true') {
|
||||||
sidebar.classList.add('closed');
|
sidebar.classList.add('closed');
|
||||||
document.body.classList.add('sidebar-closed');
|
document.body.classList.add('sidebar-closed');
|
||||||
document.getElementById('activityToggleBtn').classList.add('visible');
|
document.getElementById('activityToggleBtn').classList.add('visible');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadActivities() {
|
function loadActivities() {
|
||||||
try {
|
fetch('/web/api/activity?limit=10')
|
||||||
const res = await fetch('/web/api/activity?limit=10');
|
.then(r => r.json())
|
||||||
const data = await res.json();
|
.then(data => {
|
||||||
if (data.success && data.activities) {
|
if (data.success && data.activities) {
|
||||||
renderActivities(data.activities);
|
renderActivities(data.activities);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
})
|
||||||
if (list) list.innerHTML = '<div style="text-align:center;padding:2rem;color:var(--text-secondary);">Не удалось загрузить</div>';
|
.catch(() => {
|
||||||
}
|
if (list && !list.querySelector('.activity-sidebar-item')) {
|
||||||
|
list.innerHTML = '<div style="text-align:center;padding:2rem;color:var(--text-secondary);font-size:0.85rem;">Не удалось загрузить</div>';
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
WS.on('online_count', (data) => {
|
WS.on('online_count', (data) => {
|
||||||
document.getElementById('onlineCount').textContent = data.online || 0;
|
const el = document.getElementById('onlineCount');
|
||||||
|
if (el) el.textContent = data.online || 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
WS.on('connected', () => {
|
|
||||||
loadActivities();
|
|
||||||
});
|
|
||||||
|
|
||||||
await loadActivities();
|
|
||||||
|
|
||||||
WS.on('activity', (data) => {
|
WS.on('activity', (data) => {
|
||||||
const act = data.activity;
|
const act = data.activity;
|
||||||
if (!act || !list) return;
|
if (!act || !list) return;
|
||||||
@@ -219,14 +216,12 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Periodic refresh every 30s to catch missed activities
|
WS.on('connected', loadActivities);
|
||||||
setInterval(loadActivities, 30000);
|
|
||||||
|
|
||||||
// Expose for manual refresh from other pages
|
loadActivities();
|
||||||
window.refreshActivityFeed = loadActivities;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const RARITY_COLORS = {
|
const ACTIVITY_RARITY_COLORS = {
|
||||||
'consumer-grade': '#b0b0b0', 'industrial-grade': '#5e98d9',
|
'consumer-grade': '#b0b0b0', 'industrial-grade': '#5e98d9',
|
||||||
'mil-spec': '#4b69ff', 'mil-spec-grade': '#4b69ff',
|
'mil-spec': '#4b69ff', 'mil-spec-grade': '#4b69ff',
|
||||||
'restricted': '#8847ff', 'classified': '#d32ce6',
|
'restricted': '#8847ff', 'classified': '#d32ce6',
|
||||||
@@ -245,7 +240,7 @@ function createActivityItem(act) {
|
|||||||
item.style.cursor = 'pointer';
|
item.style.cursor = 'pointer';
|
||||||
|
|
||||||
const raritySlug = (act.data && act.data.rarity || '').toLowerCase().replace(/ /g, '-').replace(/_/g, '-').replace(/™/g, '');
|
const raritySlug = (act.data && act.data.rarity || '').toLowerCase().replace(/ /g, '-').replace(/_/g, '-').replace(/™/g, '');
|
||||||
const rarityColor = RARITY_COLORS[raritySlug] || '';
|
const rarityColor = ACTIVITY_RARITY_COLORS[raritySlug] || '';
|
||||||
if (rarityColor) {
|
if (rarityColor) {
|
||||||
item.style.background = `rgba(${hexToRgb(rarityColor)}, 0.08)`;
|
item.style.background = `rgba(${hexToRgb(rarityColor)}, 0.08)`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<nav class="navbar">
|
||||||
|
<div class="container">
|
||||||
|
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
||||||
|
<div class="nav-links">
|
||||||
|
<a href="/cases" class="nav-link{% if active_page == 'cases' %} active{% endif %}">Кейсы</a>
|
||||||
|
<a href="/contracts" class="nav-link{% if active_page == 'contracts' %} active{% endif %}">Контракты</a>
|
||||||
|
<a href="/upgrade" class="nav-link{% if active_page == 'upgrade' %} active{% endif %}">🆙 Апгрейд</a>
|
||||||
|
{% if user %}
|
||||||
|
<a href="/crash" class="nav-link{% if active_page == 'crash' %} active{% endif %}">💥 Crash</a>
|
||||||
|
<a href="/profile" class="nav-link{% if active_page == 'profile' %} active{% endif %}">Профиль</a>
|
||||||
|
<a href="/activity" class="nav-link{% if active_page == 'activity' %} active{% endif %}">📰 Лента</a>
|
||||||
|
<a href="/achievements" class="nav-link{% if active_page == 'achievements' %} active{% endif %}">🏆 Достижения</a>
|
||||||
|
<span class="user-balance">{{ "{:,.0f}".format(user.balance).replace(',', ' ') }} ₽</span>
|
||||||
|
<button onclick="toggleSound()" class="btn btn-outline" id="soundToggle" title="Звук">🔊</button>
|
||||||
|
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
||||||
|
<button onclick="logout()" class="btn btn-outline">Выйти</button>
|
||||||
|
{% else %}
|
||||||
|
<a href="/activity" class="nav-link{% if active_page == 'activity' %} active{% endif %}">📰 Лента</a>
|
||||||
|
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
||||||
|
<a href="/login" class="btn btn-outline">Войти</a>
|
||||||
|
<a href="/register" class="btn btn-primary">Регистрация</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
@@ -183,25 +183,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar">
|
{% include "_nav.html" %}
|
||||||
<div class="container">
|
|
||||||
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
|
||||||
<div class="nav-links">
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/crash" class="nav-link">💥 Crash</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<a href="/achievements" class="nav-link active">🏆 Достижения</a>
|
|
||||||
<a href="/activity" class="nav-link">📰 Лента</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<span class="user-balance">{{ "%.0f"|format(user.balance) }} ₽</span>
|
|
||||||
<button onclick="toggleSound()" class="btn btn-outline" id="soundToggle" title="Звук">🔊</button>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<button onclick="logout()" class="btn btn-outline">Выйти</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main class="container" style="padding: 2rem 1rem;">
|
<main class="container" style="padding: 2rem 1rem;">
|
||||||
<div class="achievements-header">
|
<div class="achievements-header">
|
||||||
@@ -243,7 +225,7 @@
|
|||||||
<div class="achievement-title">{{ ach.title }}</div>
|
<div class="achievement-title">{{ ach.title }}</div>
|
||||||
<div class="achievement-desc">{{ ach.description }}</div>
|
<div class="achievement-desc">{{ ach.description }}</div>
|
||||||
{% if ach.reward_amount > 0 %}
|
{% if ach.reward_amount > 0 %}
|
||||||
<div class="achievement-reward">+{{ "%.0f"|format(ach.reward_amount) }} ₽</div>
|
<div class="achievement-reward">+{{ ach.reward_amount|money }} ₽</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="achievement-progress">
|
<div class="achievement-progress">
|
||||||
<div class="progress-bar">
|
<div class="progress-bar">
|
||||||
|
|||||||
+1
-34
@@ -97,35 +97,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar">
|
{% include "_nav.html" %}
|
||||||
<div class="container">
|
|
||||||
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
|
||||||
<div class="nav-links">
|
|
||||||
{% if user %}
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/crash" class="nav-link">💥 Crash</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<a href="/activity" class="nav-link active">📰 Лента</a>
|
|
||||||
<a href="/achievements" class="nav-link">🏆 Достижения</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<span class="user-balance">{{ "%.0f"|format(user.balance) }} ₽</span>
|
|
||||||
<button onclick="toggleSound()" class="btn btn-outline" id="soundToggle" title="Звук">🔊</button>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<button onclick="logout()" class="btn btn-outline">Выйти</button>
|
|
||||||
{% else %}
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/activity" class="nav-link active">📰 Лента</a>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<a href="/login" class="btn btn-outline">Войти</a>
|
|
||||||
<a href="/register" class="btn btn-primary">Регистрация</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main class="container" style="padding: 2rem 1rem;">
|
<main class="container" style="padding: 2rem 1rem;">
|
||||||
<div class="activity-header">
|
<div class="activity-header">
|
||||||
@@ -176,11 +148,6 @@
|
|||||||
<script src="/static/js/safemode.js"></script>
|
<script src="/static/js/safemode.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
// Reload activities when WS connects/reconnects
|
|
||||||
WS.on('connected', () => {
|
|
||||||
location.reload();
|
|
||||||
});
|
|
||||||
|
|
||||||
WS.on('activity', (data) => {
|
WS.on('activity', (data) => {
|
||||||
const act = data.activity;
|
const act = data.activity;
|
||||||
if (!act) return;
|
if (!act) return;
|
||||||
|
|||||||
@@ -356,6 +356,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% block modals %}{% endblock %}
|
{% block modals %}{% endblock %}
|
||||||
|
<script src="/static/js/notifications.js"></script>
|
||||||
{% block scripts %}{% endblock %}
|
{% block scripts %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -252,8 +252,6 @@ async function searchCaseItems() {
|
|||||||
document.getElementById('caseItemSearchResults').innerHTML = html;
|
document.getElementById('caseItemSearchResults').innerHTML = html;
|
||||||
}
|
}
|
||||||
|
|
||||||
<script>if(typeof Notify==="undefined"){window.Notify={toast:function(m){alert(m)},error:function(m){alert(m)},success:function(m){alert(m)},info:function(m){alert(m)},confirm:function(m,t,c){if(c&&confirm(m))c(!0)}}}</script>
|
|
||||||
<script>
|
|
||||||
async function saveCase() {
|
async function saveCase() {
|
||||||
const name = document.getElementById('caseNameInput').value.trim();
|
const name = document.getElementById('caseNameInput').value.trim();
|
||||||
if (!name) { Notify.error('Введите название кейса'); return; }
|
if (!name) { Notify.error('Введите название кейса'); return; }
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="a-stat">
|
<div class="a-stat">
|
||||||
<div class="as-label">Общий баланс</div>
|
<div class="as-label">Общий баланс</div>
|
||||||
<div class="as-value">{{ "%.0f"|format(total_balance) }} ₽</div>
|
<div class="as-value">{{ total_balance|money }} ₽</div>
|
||||||
<div class="as-bar" style="width:40%;background:#34d399"></div>
|
<div class="as-bar" style="width:40%;background:#34d399"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="a-stat">
|
<div class="a-stat">
|
||||||
@@ -47,7 +47,7 @@
|
|||||||
<div><div class="as-value" style="font-size:1.2rem" style="color:#22c55e">{{ lucky_count }}</div><div class="as-label" style="font-size:0.7rem">🍀 Везучих</div></div>
|
<div><div class="as-value" style="font-size:1.2rem" style="color:#22c55e">{{ lucky_count }}</div><div class="as-label" style="font-size:0.7rem">🍀 Везучих</div></div>
|
||||||
<div><div class="as-value" style="font-size:1.2rem" style="color:#f59e0b">{{ normal_count }}</div><div class="as-label" style="font-size:0.7rem">📊 Обычных</div></div>
|
<div><div class="as-value" style="font-size:1.2rem" style="color:#f59e0b">{{ normal_count }}</div><div class="as-label" style="font-size:0.7rem">📊 Обычных</div></div>
|
||||||
<div><div class="as-value" style="font-size:1.2rem" style="color:#ef4444">{{ unlucky_count }}</div><div class="as-label" style="font-size:0.7rem">🌧️ Невезучих</div></div>
|
<div><div class="as-value" style="font-size:1.2rem" style="color:#ef4444">{{ unlucky_count }}</div><div class="as-label" style="font-size:0.7rem">🌧️ Невезучих</div></div>
|
||||||
<div><div class="as-value" style="font-size:1.2rem">{{ "%.0f"|format(total_spent) }} ₽</div><div class="as-label" style="font-size:0.7rem">Потрачено всего</div></div>
|
<div><div class="as-value" style="font-size:1.2rem">{{ total_spent|money }} ₽</div><div class="as-label" style="font-size:0.7rem">Потрачено всего</div></div>
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="a-empty"><div class="a-empty-icon">📭</div>Нет данных RPU</div>
|
<div class="a-empty"><div class="a-empty-icon">📭</div>Нет данных RPU</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<div class="a-stats" style="grid-template-columns:repeat(4,1fr)">
|
<div class="a-stats" style="grid-template-columns:repeat(4,1fr)">
|
||||||
<div class="a-stat">
|
<div class="a-stat">
|
||||||
<div class="as-label">Баланс</div>
|
<div class="as-label">Баланс</div>
|
||||||
<div class="as-value">{{ "%.0f"|format(target_user.balance) }} ₽</div>
|
<div class="as-value">{{ target_user.balance|money }} ₽</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="a-stat">
|
<div class="a-stat">
|
||||||
<div class="as-label">Предметов</div>
|
<div class="as-label">Предметов</div>
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
<div class="a-modal">
|
<div class="a-modal">
|
||||||
<div class="a-modal-title">💰 Изменить баланс</div>
|
<div class="a-modal-title">💰 Изменить баланс</div>
|
||||||
<p style="font-size:0.85rem;color:rgba(255,255,255,0.5);margin-bottom:1rem">
|
<p style="font-size:0.85rem;color:rgba(255,255,255,0.5);margin-bottom:1rem">
|
||||||
Текущий: <strong style="color:#f59e0b">{{ "%.2f"|format(target_user.balance) }} ₽</strong>
|
Текущий: <strong style="color:#f59e0b">{{ target_user.balance|money }} ₽</strong>
|
||||||
</p>
|
</p>
|
||||||
<div class="a-mb">
|
<div class="a-mb">
|
||||||
<label class="a-label">Сумма</label>
|
<label class="a-label">Сумма</label>
|
||||||
@@ -172,7 +172,6 @@ let currentRPU = null;
|
|||||||
function closeModal(id) { document.getElementById(id).classList.remove('open'); }
|
function closeModal(id) { document.getElementById(id).classList.remove('open'); }
|
||||||
function openModal(id) { document.getElementById(id).classList.add('open'); }
|
function openModal(id) { document.getElementById(id).classList.add('open'); }
|
||||||
|
|
||||||
<script>if(typeof Notify==="undefined"){window.Notify={toast:function(m){alert(m)},error:function(m){alert(m)},success:function(m){alert(m)},info:function(m){alert(m)},confirm:function(m,t,c){if(c&&confirm(m))c(!0)}}}</script>
|
|
||||||
// ====== BALANCE ======
|
// ====== BALANCE ======
|
||||||
function openBalanceModal() { openModal('balanceModal'); }
|
function openBalanceModal() { openModal('balanceModal'); }
|
||||||
async function updateBalance() {
|
async function updateBalance() {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<th>Статус</th>
|
<th>Статус</th>
|
||||||
<th>Кейсов</th>
|
<th>Кейсов</th>
|
||||||
<th>Контрактов</th>
|
<th>Контрактов</th>
|
||||||
<th>Достижений</th>
|
<th>Предметов</th>
|
||||||
<th></th>
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -34,15 +34,15 @@
|
|||||||
{{ u.username }}
|
{{ u.username }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ "%.0f"|format(u.balance) }} ₽</td>
|
<td>{{ u.balance|money }} ₽</td>
|
||||||
<td>
|
<td>
|
||||||
{% if u.is_admin %}<span class="a-badge a-badge-admin">Админ</span>{% endif %}
|
{% if u.is_admin %}<span class="a-badge a-badge-admin">Админ</span>{% endif %}
|
||||||
{% if u.is_banned %}<span class="a-badge a-badge-banned">Забанен</span>{% endif %}
|
{% if u.is_banned %}<span class="a-badge a-badge-banned">Забанен</span>{% endif %}
|
||||||
{% if not u.is_admin and not u.is_banned %}<span class="a-badge a-badge-user">Игрок</span>{% endif %}
|
{% if not u.is_admin and not u.is_banned %}<span class="a-badge a-badge-user">Игрок</span>{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>{{ u.case_openings|default(0) }}</td>
|
<td>{{ u.case_openings }}</td>
|
||||||
<td>{{ u.contracts|default(0) }}</td>
|
<td>{{ u.contracts }}</td>
|
||||||
<td>{{ u.achievements|default(0) }}</td>
|
<td>{{ u.inventory_count }}</td>
|
||||||
<td class="td-actions">
|
<td class="td-actions">
|
||||||
<a href="/admin/user/{{ u.id }}" class="a-btn a-btn-sm">✏️</a>
|
<a href="/admin/user/{{ u.id }}" class="a-btn a-btn-sm">✏️</a>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
@@ -214,35 +214,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar">
|
{% include "_nav.html" %}
|
||||||
<div class="container">
|
|
||||||
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
|
||||||
<div class="nav-links">
|
|
||||||
{% if user %}
|
|
||||||
<a href="/cases" class="nav-link active">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/crash" class="nav-link">💥 Crash</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<a href="/activity" class="nav-link">📰 Лента</a>
|
|
||||||
<a href="/achievements" class="nav-link">🏆 Достижения</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<span class="user-balance">{{ "%.0f"|format(user.balance) }} ₽</span>
|
|
||||||
<button onclick="toggleSound()" class="btn btn-outline" id="soundToggle" title="Звук">🔊</button>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<button onclick="logout()" class="btn btn-outline">Выйти</button>
|
|
||||||
{% else %}
|
|
||||||
<a href="/cases" class="nav-link active">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/activity" class="nav-link">📰 Лента</a>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<a href="/login" class="btn btn-outline">Войти</a>
|
|
||||||
<a href="/register" class="btn btn-primary">Регистрация</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main class="case-detail-container">
|
<main class="case-detail-container">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -255,7 +227,7 @@
|
|||||||
<p class="case-subtitle">{{ total_items }} предметов в кейсе</p>
|
<p class="case-subtitle">{{ total_items }} предметов в кейсе</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="case-info-badge">
|
<div class="case-info-badge">
|
||||||
<span class="case-price-tag">🔑 {{ "%.0f"|format(case_price) }} ₽ за шт.</span>
|
<span class="case-price-tag">🔑 {{ case_price|money }} ₽ за шт.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -266,7 +238,7 @@
|
|||||||
<div class="count-buttons" id="countButtons"></div>
|
<div class="count-buttons" id="countButtons"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="total-price-display" id="totalPriceDisplay">
|
<div class="total-price-display" id="totalPriceDisplay">
|
||||||
<span>Итого:</span> {{ "%.0f"|format(case_price) }} ₽
|
<span>Итого:</span> {{ case_price|money }} ₽
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary btn-large" onclick="startOpening()" id="openButton">
|
<button class="btn btn-primary btn-large" onclick="startOpening()" id="openButton">
|
||||||
🎲 Открыть
|
🎲 Открыть
|
||||||
@@ -389,9 +361,9 @@
|
|||||||
{% if item.min_price and item.max_price %}
|
{% if item.min_price and item.max_price %}
|
||||||
<div class="item-price-compact">
|
<div class="item-price-compact">
|
||||||
{% if item.min_price == item.max_price %}
|
{% if item.min_price == item.max_price %}
|
||||||
{{ "%.0f"|format(item.min_price) }} ₽
|
{{ item.min_price|money }} ₽
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ "%.0f"|format(item.min_price) }} - {{ "%.0f"|format(item.max_price) }} ₽
|
{{ item.min_price|money }} - {{ item.max_price|money }} ₽
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|||||||
+3
-31
@@ -155,35 +155,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar">
|
{% include "_nav.html" %}
|
||||||
<div class="container">
|
|
||||||
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
|
||||||
<div class="nav-links">
|
|
||||||
{% if user %}
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/crash" class="nav-link">💥 Crash</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<a href="/activity" class="nav-link">📰 Лента</a>
|
|
||||||
<a href="/achievements" class="nav-link">🏆 Достижения</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<span class="user-balance">{{ "%.2f"|format(user.balance) }} ₽</span>
|
|
||||||
<button onclick="toggleSound()" class="btn btn-outline" id="soundToggle" title="Звук">🔊</button>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<button onclick="logout()" class="btn btn-outline">Выйти</button>
|
|
||||||
{% else %}
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/activity" class="nav-link">📰 Лента</a>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<a href="/login" class="btn btn-outline">Войти</a>
|
|
||||||
<a href="/register" class="btn btn-primary">Регистрация</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main class="cases-container">
|
<main class="cases-container">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -203,7 +175,7 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<span style="font-size: 3rem;">📦</span>
|
<span style="font-size: 3rem;">📦</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<span class="case-price-badge">{{ "%.0f"|format(case.price) }} ₽</span>
|
<span class="case-price-badge">{{ case.price|money }} ₽</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="case-info">
|
<div class="case-info">
|
||||||
<div class="case-title">{{ case.display_name }}</div>
|
<div class="case-title">{{ case.display_name }}</div>
|
||||||
@@ -222,7 +194,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="case-footer">
|
<div class="case-footer">
|
||||||
<span class="case-price">{{ "%.0f"|format(case.price) }} ₽</span>
|
<span class="case-price">{{ case.price|money }} ₽</span>
|
||||||
<span class="btn-open">Открыть</span>
|
<span class="btn-open">Открыть</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,33 +7,7 @@
|
|||||||
<link rel="stylesheet" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar">
|
{% include "_nav.html" %}
|
||||||
<div class="container">
|
|
||||||
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
|
||||||
<div class="nav-links">
|
|
||||||
{% if user %}
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link active">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/crash" class="nav-link">💥 Crash</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<a href="/achievements" class="nav-link">🏆 Достижения</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<span class="user-balance">{{ "%.0f"|format(user.balance) }} ₽</span>
|
|
||||||
<button onclick="toggleSound()" class="btn btn-outline" id="soundToggle">🔊</button>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle">🎙️</button>
|
|
||||||
<button onclick="logout()" class="btn btn-outline">Выйти</button>
|
|
||||||
{% else %}
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link active">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle">🎙️</button>
|
|
||||||
<a href="/login" class="btn btn-outline">Войти</a>
|
|
||||||
<a href="/register" class="btn btn-primary">Регистрация</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.ct-page { max-width: 1100px; margin: 0 auto; padding: 1rem 1.5rem 3rem; }
|
.ct-page { max-width: 1100px; margin: 0 auto; padding: 1rem 1.5rem 3rem; }
|
||||||
|
|||||||
+1
-19
@@ -65,25 +65,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar">
|
{% include "_nav.html" %}
|
||||||
<div class="container">
|
|
||||||
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
|
||||||
<div class="nav-links">
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/crash" class="nav-link active">💥 Crash</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<a href="/achievements" class="nav-link">🏆 Достижения</a>
|
|
||||||
<a href="/activity" class="nav-link">📰 Лента</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<span class="user-balance">{{ "%.0f"|format(user.balance) }} ₽</span>
|
|
||||||
<button onclick="toggleSound()" class="btn btn-outline" id="soundToggle">🔊</button>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle">🎙️</button>
|
|
||||||
<button onclick="logout()" class="btn btn-outline">Выйти</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main class="container" style="padding: 2rem 1rem;">
|
<main class="container" style="padding: 2rem 1rem;">
|
||||||
<div class="crash-layout">
|
<div class="crash-layout">
|
||||||
|
|||||||
+1
-14
@@ -7,20 +7,7 @@
|
|||||||
<link rel="stylesheet" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar">
|
{% include "_nav.html" %}
|
||||||
<div class="container">
|
|
||||||
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
|
||||||
<div class="nav-links">
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/activity" class="nav-link">📰 Лента</a>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<a href="/login" class="btn btn-outline">Войти</a>
|
|
||||||
<a href="/register" class="btn btn-primary">Регистрация</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main class="hero">
|
<main class="hero">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|||||||
@@ -7,15 +7,7 @@
|
|||||||
<link rel="stylesheet" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar">
|
{% include "_nav.html" %}
|
||||||
<div class="container">
|
|
||||||
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
|
||||||
<div class="nav-links">
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<a href="/register" class="btn btn-outline">Регистрация</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main class="auth-container">
|
<main class="auth-container">
|
||||||
<div class="auth-card">
|
<div class="auth-card">
|
||||||
|
|||||||
+15
-41
@@ -446,43 +446,7 @@
|
|||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar">
|
{% include "_nav.html" %}
|
||||||
<div class="container">
|
|
||||||
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
|
||||||
<div class="nav-links">
|
|
||||||
{% if user and not is_public %}
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/crash" class="nav-link">💥 Crash</a>
|
|
||||||
<a href="/profile" class="nav-link active">Профиль</a>
|
|
||||||
<a href="/activity" class="nav-link">📰 Лента</a>
|
|
||||||
<a href="/achievements" class="nav-link">🏆 Достижения</a>
|
|
||||||
<span class="user-balance">{{ "%.2f"|format(user.balance) }} ₽</span>
|
|
||||||
<button onclick="toggleSound()" class="btn btn-outline" id="soundToggle" title="Звук">🔊</button>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<button onclick="logout()" class="btn btn-outline">Выйти</button>
|
|
||||||
{% elif user %}
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/activity" class="nav-link">📰 Лента</a>
|
|
||||||
<span class="user-balance">{{ "%.2f"|format(user.balance) }} ₽</span>
|
|
||||||
<button onclick="toggleSound()" class="btn btn-outline" id="soundToggle" title="Звук">🔊</button>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<button onclick="logout()" class="btn btn-outline">Выйти</button>
|
|
||||||
{% else %}
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
|
||||||
<a href="/activity" class="nav-link">📰 Лента</a>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<a href="/login" class="btn btn-outline">Войти</a>
|
|
||||||
<a href="/register" class="btn btn-primary">Регистрация</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<div class="profile-layout">
|
<div class="profile-layout">
|
||||||
@@ -519,18 +483,17 @@
|
|||||||
</div>
|
</div>
|
||||||
{% if not is_public %}
|
{% if not is_public %}
|
||||||
<div class="stat-card">
|
<div class="stat-card">
|
||||||
<div class="stat-value">{{ "%.0f"|format(balance) }} ₽</div>
|
<div class="stat-value">{{ balance|money }} ₽</div>
|
||||||
<div class="stat-label">Баланс</div>
|
<div class="stat-label">Баланс</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if not is_public %}
|
|
||||||
<!-- Инвентарь -->
|
<!-- Инвентарь -->
|
||||||
<div>
|
<div>
|
||||||
<div class="section-title">🎒 Инвентарь <span style="font-size:0.78rem;font-weight:400;color:var(--text-dim);font-family:'Rajdhani',sans-serif;">— {{ inventory_items|length }} предметов</span></div>
|
<div class="section-title">🎒 Инвентарь <span style="font-size:0.78rem;font-weight:400;color:var(--text-dim);font-family:'Rajdhani',sans-serif;">— {{ inventory_items|length }} предметов</span></div>
|
||||||
|
|
||||||
{% if inventory_items %}
|
{% if not is_public and inventory_items %}
|
||||||
<div class="inventory-actions">
|
<div class="inventory-actions">
|
||||||
<div class="selection-controls">
|
<div class="selection-controls">
|
||||||
<label class="select-all-checkbox">
|
<label class="select-all-checkbox">
|
||||||
@@ -562,7 +525,9 @@
|
|||||||
<input type="text" id="searchInput" class="filter-select" style="min-width:150px;" placeholder="Поиск..." oninput="filterItems()">
|
<input type="text" id="searchInput" class="filter-select" style="min-width:150px;" placeholder="Поиск..." oninput="filterItems()">
|
||||||
<button onclick="resetFilters()" class="btn btn-outline" style="font-size:0.72rem;padding:0.35rem 0.8rem;">Сброс</button>
|
<button onclick="resetFilters()" class="btn btn-outline" style="font-size:0.72rem;padding:0.35rem 0.8rem;">Сброс</button>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if inventory_items %}
|
||||||
<div class="inventory-grid-detailed" id="inventoryGrid">
|
<div class="inventory-grid-detailed" id="inventoryGrid">
|
||||||
{% for item in inventory_items %}
|
{% for item in inventory_items %}
|
||||||
<div class="inventory-skin-card rarity-{{ item.rarity|lower|replace(' ', '-') }}"
|
<div class="inventory-skin-card rarity-{{ item.rarity|lower|replace(' ', '-') }}"
|
||||||
@@ -571,10 +536,12 @@
|
|||||||
data-type="{{ item.type }}"
|
data-type="{{ item.type }}"
|
||||||
data-name="{{ item.name|lower }}"
|
data-name="{{ item.name|lower }}"
|
||||||
data-price="{{ item.price_rub or 100 }}">
|
data-price="{{ item.price_rub or 100 }}">
|
||||||
|
{% if not is_public %}
|
||||||
<input type="checkbox" class="item-select-checkbox"
|
<input type="checkbox" class="item-select-checkbox"
|
||||||
onchange="onItemSelect(this)"
|
onchange="onItemSelect(this)"
|
||||||
data-id="{{ item.id }}"
|
data-id="{{ item.id }}"
|
||||||
data-price="{{ item.price_rub or 100 }}">
|
data-price="{{ item.price_rub or 100 }}">
|
||||||
|
{% endif %}
|
||||||
<div class="skin-image-container">
|
<div class="skin-image-container">
|
||||||
<img src="{{ item.image_url or '/static/placeholder.png' }}"
|
<img src="{{ item.image_url or '/static/placeholder.png' }}"
|
||||||
alt="{{ item.name }}"
|
alt="{{ item.name }}"
|
||||||
@@ -591,11 +558,13 @@
|
|||||||
<span>{{ item.wear }}</span>
|
<span>{{ item.wear }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="skin-details" style="margin-top:3px;color:var(--success-color);">
|
<div class="skin-details" style="margin-top:3px;color:var(--success-color);">
|
||||||
<span>💰 {{ "%.0f"|format(item.price_rub or 100) }} ₽</span>
|
<span>💰 {{ (item.price_rub or 100)|money }} ₽</span>
|
||||||
</div>
|
</div>
|
||||||
|
{% if not is_public %}
|
||||||
<div class="item-actions">
|
<div class="item-actions">
|
||||||
<button onclick="sellSingleItem({{ item.id }})" class="btn-sell-single">Продать</button>
|
<button onclick="sellSingleItem({{ item.id }})" class="btn-sell-single">Продать</button>
|
||||||
</div>
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@@ -604,12 +573,17 @@
|
|||||||
<div class="empty-state-large">
|
<div class="empty-state-large">
|
||||||
<div class="empty-icon">📭</div>
|
<div class="empty-icon">📭</div>
|
||||||
<h3>Инвентарь пуст</h3>
|
<h3>Инвентарь пуст</h3>
|
||||||
|
{% if is_public %}
|
||||||
|
<p>У пользователя пока нет предметов.</p>
|
||||||
|
{% else %}
|
||||||
<p>Откройте кейсы, чтобы получить первые предметы!</p>
|
<p>Откройте кейсы, чтобы получить первые предметы!</p>
|
||||||
<a href="/cases" class="btn btn-primary">Открыть кейсы</a>
|
<a href="/cases" class="btn btn-primary">Открыть кейсы</a>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if not is_public %}
|
||||||
<!-- Модалка массовой продажи -->
|
<!-- Модалка массовой продажи -->
|
||||||
<div id="bulkSellModal" class="bulk-sell-modal" style="display:none;">
|
<div id="bulkSellModal" class="bulk-sell-modal" style="display:none;">
|
||||||
<div class="bulk-sell-content">
|
<div class="bulk-sell-content">
|
||||||
|
|||||||
@@ -7,15 +7,7 @@
|
|||||||
<link rel="stylesheet" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<nav class="navbar">
|
{% include "_nav.html" %}
|
||||||
<div class="container">
|
|
||||||
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
|
||||||
<div class="nav-links">
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<a href="/login" class="btn btn-outline">Войти</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main class="auth-container">
|
<main class="auth-container">
|
||||||
<div class="auth-card">
|
<div class="auth-card">
|
||||||
|
|||||||
+3
-45
@@ -7,35 +7,7 @@
|
|||||||
<link rel="stylesheet" href="/static/css/style.css">
|
<link rel="stylesheet" href="/static/css/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body class="upgrade-page">
|
<body class="upgrade-page">
|
||||||
<nav class="navbar">
|
{% include "_nav.html" %}
|
||||||
<div class="container">
|
|
||||||
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
|
||||||
<div class="nav-links">
|
|
||||||
{% if user %}
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link active">🆙 Апгрейд</a>
|
|
||||||
<a href="/crash" class="nav-link">💥 Crash</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<a href="/activity" class="nav-link">📰 Лента</a>
|
|
||||||
<a href="/achievements" class="nav-link">🏆 Достижения</a>
|
|
||||||
<a href="/profile" class="nav-link">Профиль</a>
|
|
||||||
<span class="user-balance">{{ "%.0f"|format(user.balance) }} ₽</span>
|
|
||||||
<button onclick="toggleSound()" class="btn btn-outline" id="soundToggle" title="Звук">🔊</button>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<button onclick="logout()" class="btn btn-outline">Выйти</button>
|
|
||||||
{% else %}
|
|
||||||
<a href="/cases" class="nav-link">Кейсы</a>
|
|
||||||
<a href="/contracts" class="nav-link">Контракты</a>
|
|
||||||
<a href="/upgrade" class="nav-link active">🆙 Апгрейд</a>
|
|
||||||
<a href="/activity" class="nav-link">📰 Лента</a>
|
|
||||||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle" title="Режим записи">🎙️</button>
|
|
||||||
<a href="/login" class="btn btn-outline">Войти</a>
|
|
||||||
<a href="/register" class="btn btn-primary">Регистрация</a>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<main class="upgrade-container">
|
<main class="upgrade-container">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -109,7 +81,6 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="toastContainer" class="toast-container"></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Target -->
|
<!-- Target -->
|
||||||
@@ -596,19 +567,6 @@
|
|||||||
`).join('');
|
`).join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function showToast(message, type) {
|
|
||||||
const container = document.getElementById('toastContainer');
|
|
||||||
const toast = document.createElement('div');
|
|
||||||
toast.className = `toast ${type}`;
|
|
||||||
toast.textContent = message;
|
|
||||||
container.appendChild(toast);
|
|
||||||
requestAnimationFrame(() => toast.classList.add('show'));
|
|
||||||
setTimeout(() => {
|
|
||||||
toast.classList.remove('show');
|
|
||||||
setTimeout(() => toast.remove(), 300);
|
|
||||||
}, 3000);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function executeUpgrade() {
|
async function executeUpgrade() {
|
||||||
if (!selectedInput || !selectedTarget || isSpinning) return;
|
if (!selectedInput || !selectedTarget || isSpinning) return;
|
||||||
|
|
||||||
@@ -709,12 +667,12 @@
|
|||||||
document.getElementById('displayTargetPrice').textContent = `${Math.floor(data.received_item.price).toLocaleString()} ₽`;
|
document.getElementById('displayTargetPrice').textContent = `${Math.floor(data.received_item.price).toLocaleString()} ₽`;
|
||||||
document.getElementById('selectedTargetDisplay').style.borderColor = '#10b981';
|
document.getElementById('selectedTargetDisplay').style.borderColor = '#10b981';
|
||||||
document.getElementById('selectedTargetDisplay').style.boxShadow = '0 0 20px rgba(16,185,129,0.3)';
|
document.getElementById('selectedTargetDisplay').style.boxShadow = '0 0 20px rgba(16,185,129,0.3)';
|
||||||
showToast(`${getRandomPhrase('win')} ${data.received_item.name}`, 'success');
|
Notify.success(`${getRandomPhrase('win')} ${data.received_item.name}`);
|
||||||
SoundManager.wheelWin();
|
SoundManager.wheelWin();
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('selectedInputDisplay').style.borderColor = '#ef4444';
|
document.getElementById('selectedInputDisplay').style.borderColor = '#ef4444';
|
||||||
document.getElementById('selectedInputDisplay').style.boxShadow = '0 0 20px rgba(239,68,68,0.3)';
|
document.getElementById('selectedInputDisplay').style.boxShadow = '0 0 20px rgba(239,68,68,0.3)';
|
||||||
showToast(getRandomPhrase('lose'), 'fail');
|
Notify.error(getRandomPhrase('lose'));
|
||||||
SoundManager.wheelLose();
|
SoundManager.wheelLose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user