Files
grepo-remote/templates/farm.html
2026-04-24 21:29:55 +03:00

510 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="el">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Farm Manager — Grepolis Remote</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&display=swap" rel="stylesheet">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: #0f0f1a;
font-family: 'Inter', 'Segoe UI', sans-serif;
color: #e0e0e0;
min-height: 100vh;
padding: 1.5rem;
}
/* ---- Top bar ---- */
.topbar {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 2rem;
}
.topbar a {
color: #666;
text-decoration: none;
font-size: 0.85rem;
transition: color 0.2s;
}
.topbar a:hover { color: #c8a44a; }
.topbar h1 {
font-size: 1.5rem;
font-weight: 800;
color: #4acc64;
}
.online-dot {
width: 9px; height: 9px;
border-radius: 50%;
background: #cc7b7b;
box-shadow: 0 0 6px #cc7b7b;
display: inline-block;
margin-left: auto;
}
.online-dot.live { background: #7bcc7b; box-shadow: 0 0 6px #7bcc7b; }
.sync-btn {
background: transparent;
border: 1px solid #4acc64;
color: #4acc64;
border-radius: 6px;
padding: 0.3rem 0.6rem;
font-size: 0.8rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
margin-left: 0.5rem;
}
.sync-btn:hover {
background: rgba(74, 204, 100, 0.1);
}
/* ---- Control Panel ---- */
.panel {
background: #1a1a28;
border: 1px solid #2a2a40;
border-radius: 16px;
padding: 1.75rem;
margin-bottom: 1.5rem;
}
.panel h2 {
font-size: 1rem;
font-weight: 700;
color: #aaa;
text-transform: uppercase;
letter-spacing: 0.07em;
margin-bottom: 1.25rem;
}
/* Toggle switch */
.toggle-row {
display: flex;
align-items: center;
gap: 1rem;
margin-bottom: 1.5rem;
}
.toggle-label { font-size: 1rem; font-weight: 600; }
.toggle {
position: relative;
width: 56px;
height: 30px;
cursor: pointer;
}
.toggle input { opacity: 0; width: 0; height: 0; }
.slider {
position: absolute;
inset: 0;
background: #2a2a3a;
border-radius: 30px;
transition: background 0.3s;
border: 1px solid #3a3a50;
}
.slider::before {
content: '';
position: absolute;
width: 22px; height: 22px;
left: 4px; top: 3px;
background: #666;
border-radius: 50%;
transition: transform 0.3s, background 0.3s;
}
input:checked + .slider { background: rgba(74,204,100,0.25); border-color: #4acc64; }
input:checked + .slider::before { transform: translateX(26px); background: #4acc64; }
/* Loot option selector */
.option-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.75rem;
margin-bottom: 1.5rem;
}
.option-btn {
background: #12121e;
border: 2px solid #2a2a40;
border-radius: 10px;
padding: 0.75rem 0.5rem;
cursor: pointer;
text-align: center;
transition: border-color 0.2s, background 0.2s;
color: #888;
font-family: inherit;
font-size: 0.9rem;
}
.option-btn .opt-time { font-size: 1.1rem; font-weight: 700; color: #ccc; display: block; }
.option-btn .opt-name { font-size: 0.72rem; color: #666; margin-top: 2px; display: block; }
.option-btn:hover { border-color: #4acc64; background: rgba(74,204,100,0.05); }
.option-btn.selected { border-color: #4acc64; background: rgba(74,204,100,0.12); color: #4acc64; }
.option-btn.selected .opt-time { color: #4acc64; }
.option-btn.selected .opt-name { color: #4acc64bb; }
.save-btn {
background: linear-gradient(135deg, #2a7a3a, #4acc64);
color: #fff;
border: none;
border-radius: 10px;
padding: 0.7rem 2rem;
font-family: inherit;
font-size: 0.95rem;
font-weight: 700;
cursor: pointer;
transition: opacity 0.2s, transform 0.1s;
}
.save-btn:hover { opacity: 0.9; transform: translateY(-1px); }
.save-btn:active { transform: translateY(0); }
.save-status {
margin-left: 1rem;
font-size: 0.85rem;
color: #4acc64;
opacity: 0;
transition: opacity 0.3s;
}
.save-status.visible { opacity: 1; }
/* Input field */
.input-field {
background: #12121e;
border: 1px solid #2a2a40;
color: #e0e0e0;
border-radius: 6px;
padding: 0.6rem;
width: 100px;
font-family: inherit;
font-size: 0.95rem;
outline: none;
transition: border-color 0.2s;
}
.input-field:focus { border-color: #4acc64; }
/* ---- Farm Table ---- */
.farm-table-wrap { overflow-x: auto; }
table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
thead th {
text-align: left;
padding: 0.6rem 1rem;
color: #666;
font-size: 0.78rem;
text-transform: uppercase;
letter-spacing: 0.06em;
border-bottom: 1px solid #2a2a40;
}
tbody tr { border-bottom: 1px solid #1e1e2e; transition: background 0.15s; }
tbody tr:hover { background: rgba(255,255,255,0.03); }
tbody td { padding: 0.75rem 1rem; }
.badge {
display: inline-block;
padding: 2px 10px;
border-radius: 20px;
font-size: 0.78rem;
font-weight: 600;
}
.badge.ready { background: rgba(74,204,100,0.2); color: #4acc64; border: 1px solid rgba(74,204,100,0.35); }
.badge.waiting { background: rgba(150,150,200,0.1); color: #888; border: 1px solid #2a2a40; }
.countdown { font-size: 0.82rem; color: #6fcfcf; }
/* Status bar */
.status-bar {
background: rgba(74,204,100,0.08);
border: 1px solid rgba(74,204,100,0.2);
border-radius: 10px;
padding: 0.75rem 1.25rem;
font-size: 0.875rem;
color: #4acc64;
margin-bottom: 1.5rem;
display: none;
}
.status-bar.visible { display: block; }
.status-bar.off {
background: rgba(200,100,100,0.08);
border-color: rgba(200,100,100,0.2);
color: #cc7b7b;
}
/* Empty state */
.empty-state { text-align: center; padding: 3rem 1rem; color: #555; }
.empty-state p { margin-top: 0.5rem; font-size: 0.9rem; }
</style>
</head>
<body>
<div class="topbar">
<a href="/player/{{ player_id }}">← Πίσω</a>
<h1>🌾 Farm Manager</h1>
<span class="online-dot" id="online-dot" title="Κατάσταση Script"></span>
<button class="sync-btn" onclick="requestSync()">Live Sync</button>
</div>
<!-- Status banner -->
<div class="status-bar" id="status-bar"></div>
<!-- Control Panel -->
<div class="panel">
<h2>⚙️ Ρυθμίσεις</h2>
<div class="toggle-row">
<span class="toggle-label">Αυτόματη Λεηλασία</span>
<label class="toggle">
<input type="checkbox" id="farm-enabled">
<span class="slider"></span>
</label>
<span style="color:#888; font-size:0.85rem;" id="toggle-hint">Ανενεργό</span>
</div>
<div class="toggle-row">
<span class="toggle-label">Στρατόπεδο Ληστών (Auto)</span>
<label class="toggle">
<input type="checkbox" id="bandit-camp-enabled">
<span class="slider"></span>
</label>
<span style="color:#888; font-size:0.85rem;" id="bandit-toggle-hint">Ανενεργό</span>
</div>
<div style="margin-bottom: 0.75rem; font-size: 0.85rem; color: #888;">Επίπεδο Λεηλασίας:</div>
<div class="option-grid">
<button class="option-btn selected" data-option="1">
<span class="opt-time">5'</span>
<span class="opt-name">Γρήγορο</span>
</button>
<button class="option-btn" data-option="2">
<span class="opt-time">20'</span>
<span class="opt-name">Κανονικό</span>
</button>
<button class="option-btn" data-option="3">
<span class="opt-time">90'</span>
<span class="opt-name">Αργό</span>
</button>
<button class="option-btn" data-option="4">
<span class="opt-time">4h</span>
<span class="opt-name">Ύπνος</span>
</button>
</div>
<button class="save-btn" id="save-btn" onclick="saveSettings()">💾 Αποθήκευση</button>
<span class="save-status" id="save-status">✓ Αποθηκεύτηκε</span>
</div>
<!-- Farm Upgrade Panel -->
<div class="panel">
<h2>🏰 Αυτόματη Αναβάθμιση Χωριών</h2>
<p style="font-size: 0.85rem; color: #888; margin-bottom: 1rem;">
Το script θα χρησιμοποιήσει αυτόματα τους πόντους μάχης σας για να ξεκλειδώσει νέα χωριά και να τα αναβαθμίσει μέχρι το επίπεδο 6.<br>
Επιλέξτε πόσους πόντους μάχης θέλετε να κρατήσετε ως <strong>όριο ασφαλείας</strong>. Το script θα ξοδέψει μόνο τους πόντους ΠΑΝΩ από αυτό το όριο.
</p>
<div style="display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem;">
<label for="kp-threshold" style="font-size: 0.95rem; font-weight: 600;">Ελάχιστοι Πόντοι (Όριο):</label>
<input type="number" id="kp-threshold" class="input-field" value="0" min="0" step="10">
</div>
<div style="display: flex; gap: 1rem;">
<button class="save-btn" id="unlock-btn" onclick="triggerUpgrade('unlock')" style="flex: 1; background: linear-gradient(135deg, #7a5c2a, #cca04a);">🔓 Μόνο Ξεκλείδωμα</button>
<button class="save-btn" id="upgrade-btn" onclick="triggerUpgrade('upgrade')" style="flex: 1;">🚀 Μόνο Αναβάθμιση</button>
</div>
<div style="margin-top: 10px;">
<span class="save-status" id="upgrade-status">Εντολή εστάλη!</span>
</div>
</div>
<!-- Farm Status Table -->
<div class="panel">
<h2>🏘️ Κατάσταση Χωριών</h2>
<div class="farm-table-wrap">
<table>
<thead>
<tr>
<th>Πόλη</th>
<th>Έτοιμα</th>
<th>Σύνολο</th>
<th>Επόμενο</th>
</tr>
</thead>
<tbody id="farm-table-body">
<tr><td colspan="4"><div class="empty-state"><p>Φόρτωση δεδομένων...</p></div></td></tr>
</tbody>
</table>
</div>
</div>
<script>
const PLAYER_ID = '{{ player_id }}';
let selectedOption = 1;
// -- Loot option buttons --
document.querySelectorAll('.option-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.option-btn').forEach(b => b.classList.remove('selected'));
btn.classList.add('selected');
selectedOption = parseInt(btn.dataset.option);
});
});
// -- Toggle hint text --
document.getElementById('farm-enabled').addEventListener('change', function () {
document.getElementById('toggle-hint').textContent = this.checked ? '🟢 Ενεργό' : 'Ανενεργό';
updateStatusBar(this.checked, document.getElementById('bandit-camp-enabled').checked);
});
document.getElementById('bandit-camp-enabled').addEventListener('change', function () {
document.getElementById('bandit-toggle-hint').textContent = this.checked ? '🟢 Ενεργό' : 'Ανενεργό';
updateStatusBar(document.getElementById('farm-enabled').checked, this.checked);
});
function updateStatusBar(farmEnabled, banditEnabled) {
const bar = document.getElementById('status-bar');
if (farmEnabled || banditEnabled) {
bar.className = 'status-bar visible';
let msg = [];
if (farmEnabled) msg.push('Ο αυτόματος farmer είναι ενεργός.');
if (banditEnabled) msg.push('Το στρατόπεδο ληστών είναι ενεργό.');
bar.textContent = '🤖 ' + msg.join(' ') + ' Το script θα εκτελεί δράσεις με τυχαίες καθυστερήσεις.';
} else {
bar.className = 'status-bar visible off';
bar.textContent = '⏸ Οι αυτόματες ενέργειες είναι ανενεργές.';
}
}
// -- Save settings --
function saveSettings() {
const enabled = document.getElementById('farm-enabled').checked;
const bandit_camp_enabled = document.getElementById('bandit-camp-enabled').checked;
fetch('/dashboard/farm-settings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ player_id: PLAYER_ID, enabled, bandit_camp_enabled, loot_option: selectedOption })
})
.then(r => r.json())
.then(() => {
const s = document.getElementById('save-status');
s.classList.add('visible');
setTimeout(() => s.classList.remove('visible'), 2500);
});
}
// -- Load current settings --
function loadSettings() {
fetch(`/dashboard/farm-settings?player_id=${PLAYER_ID}`)
.then(r => r.json())
.then(cfg => {
document.getElementById('farm-enabled').checked = cfg.enabled;
document.getElementById('bandit-camp-enabled').checked = cfg.bandit_camp_enabled || false;
document.getElementById('toggle-hint').textContent = cfg.enabled ? '🟢 Ενεργό' : 'Ανενεργό';
document.getElementById('bandit-toggle-hint').textContent = cfg.bandit_camp_enabled ? '🟢 Ενεργό' : 'Ανενεργό';
updateStatusBar(cfg.enabled, cfg.bandit_camp_enabled);
selectedOption = cfg.loot_option || 1;
document.querySelectorAll('.option-btn').forEach(b => {
b.classList.toggle('selected', parseInt(b.dataset.option) === selectedOption);
});
});
}
// -- Load farm data table --
function loadFarmData() {
fetch(`/dashboard/farm-data?player_id=${PLAYER_ID}`)
.then(r => r.json())
.then(data => {
const tbody = document.getElementById('farm-table-body');
if (!data || data.length === 0) {
tbody.innerHTML = '<tr><td colspan="4"><div class="empty-state">🌱 <p>Δεν υπάρχουν δεδομένα χωριών ακόμη.<br>Βεβαιώσου ότι το script v3.3+ τρέχει στο παιχνίδι.</p></div></td></tr>';
return;
}
const now = Math.floor(Date.now() / 1000);
tbody.innerHTML = data.map(t => {
const readyBadge = t.ready_farms > 0
? `<span class="badge ready">✓ ${t.ready_farms} Έτοιμα</span>`
: `<span class="badge waiting">Αναμονή</span>`;
let nextStr = '—';
if (t.next_ready_at) {
const diff = t.next_ready_at - now;
if (diff > 0) {
const m = Math.floor(diff / 60);
const s = diff % 60;
nextStr = `<span class="countdown">${m}λ ${s}δ</span>`;
} else {
nextStr = '<span class="countdown">Τώρα</span>';
}
}
return `<tr>
<td><strong>${t.town_name}</strong></td>
<td>${readyBadge}</td>
<td><span style="color:#888">${t.total_farms}</span></td>
<td>${nextStr}</td>
</tr>`;
}).join('');
});
}
// -- Online status check --
function checkOnline() {
fetch(`/dashboard/client-status?player_id=${PLAYER_ID}`)
.then(r => r.json())
.then(d => {
const dot = document.getElementById('online-dot');
dot.className = 'online-dot' + (d.online ? ' live' : '');
dot.title = d.online ? 'Script Online' : 'Script Offline';
});
}
// -- Live Sync --
function requestSync() {
const btn = document.querySelector('.sync-btn');
const originalText = btn.innerText;
btn.innerText = '⏳ Syncing...';
btn.disabled = true;
fetch(`/api/sync-request?player_id=${PLAYER_ID}`, { method: 'POST' })
.then(r => r.json())
.then(data => {
setTimeout(() => {
loadFarmData();
btn.innerText = originalText;
btn.disabled = false;
}, 1500);
});
}
// -- Trigger Upgrade / Unlock --
function triggerUpgrade(actionType) {
const threshold = document.getElementById('kp-threshold').value;
const btn = actionType === 'unlock' ? document.getElementById('unlock-btn') : document.getElementById('upgrade-btn');
const originalText = btn.innerText;
btn.innerText = '⏳ Αποστολή...';
btn.disabled = true;
fetch('/dashboard/commands', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
player_id: PLAYER_ID,
town_id: 0,
type: 'farm_upgrade',
payload: { threshold: threshold, action_type: actionType }
})
})
.then(r => r.json())
.then(() => {
btn.innerText = originalText;
btn.disabled = false;
const s = document.getElementById('upgrade-status');
s.classList.add('visible');
setTimeout(() => s.classList.remove('visible'), 3000);
});
}
// -- Boot --
loadSettings();
loadFarmData();
checkOnline();
setInterval(loadFarmData, 15000);
setInterval(checkOnline, 20000);
</script>
</body>
</html>