feat(phase1): complete base structure - landing page, auth, dashboard, airports, API, DB schema, 60 airports CSV
This commit is contained in:
461
www/public/dashboard.php
Normal file
461
www/public/dashboard.php
Normal file
@@ -0,0 +1,461 @@
|
||||
<?php
|
||||
/**
|
||||
* Airline Tycoon - Dashboard
|
||||
* Hauptseite für eingeloggte Spieler
|
||||
*/
|
||||
|
||||
require_once dirname(__DIR__) . '/src/bootstrap.php';
|
||||
requireLogin();
|
||||
|
||||
$userId = Session::getUserId();
|
||||
$user = $db->getUserById($userId);
|
||||
|
||||
if (!$user) {
|
||||
Session::logout();
|
||||
redirect('/login.php');
|
||||
}
|
||||
|
||||
Session::refreshUserData($db);
|
||||
|
||||
// Aktualisiere User-Daten
|
||||
$user = $db->getUserById($userId);
|
||||
$stats = $db->getUserStats($userId);
|
||||
$todayStats = $db->getTodayStats($userId);
|
||||
$aircraft = $db->getUserAircraft($userId);
|
||||
$routes = $db->getUserRoutes($userId);
|
||||
|
||||
// Level-Fortschritt berechnen
|
||||
$expForNextLevel = $config['game']['level_up_threshold'] * pow($config['game']['level_multiplier'], $user['level'] - 1);
|
||||
$levelProgress = ($user['experience'] / $expForNextLevel) * 100;
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" data-theme="light">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Airline Tycoon Dashboard - Verwalte deine Airline">
|
||||
<meta name="csrf-token" content="<?= h(csrf_token()) ?>">
|
||||
<title>Dashboard - Airline Tycoon</title>
|
||||
<link rel="stylesheet" href="<?= BASE_PATH ?>assets/css/style.css">
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>✈️</text></svg>">
|
||||
</head>
|
||||
<body>
|
||||
<!-- Navigation -->
|
||||
<nav class="navbar">
|
||||
<div class="navbar-container">
|
||||
<a href="<?= BASE_PATH ?>dashboard.php" class="navbar-brand">✈️ <?= h($user['airline_name']) ?></a>
|
||||
<button class="navbar-toggle" aria-label="Menü">☰</button>
|
||||
<ul class="navbar-menu">
|
||||
<li><a href="<?= BASE_PATH ?>dashboard.php" class="navbar-item active">📊 Dashboard</a></li>
|
||||
<li><a href="<?= BASE_PATH ?>airports.php" class="navbar-item">🌍 Weltkarte</a></li>
|
||||
<li><a href="<?= BASE_PATH ?>airports.php?action=fleet" class="navbar-item">✈️ Flotte</a></li>
|
||||
<li><a href="<?= BASE_PATH ?>airports.php?action=routes" class="navbar-item">🛫 Routen</a></li>
|
||||
<li>
|
||||
<span class="navbar-item">
|
||||
💰 <?= formatMoney($user['balance']) ?>
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
<a href="<?= BASE_PATH ?>logout.php" class="navbar-item">🚪 Abmelden</a>
|
||||
</li>
|
||||
<li>
|
||||
<button class="theme-toggle" id="themeToggle" aria-label="Design wechseln">🌙</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="container p-lg">
|
||||
<!-- Willkommensnachricht -->
|
||||
<div class="panel mb-lg">
|
||||
<h1>👋 Willkommen, <?= h($user['username']) ?>!</h1>
|
||||
<p class="mt-sm text-secondary">
|
||||
Level <?= h($user['level']) ?> | <?= h($user['airline_name']) ?>
|
||||
</p>
|
||||
|
||||
<!-- Level Fortschritt -->
|
||||
<div class="mt-md">
|
||||
<div class="d-flex flex-between mb-sm">
|
||||
<span>Erfahrung</span>
|
||||
<span><?= number_format($user['experience'], 0, ',', '.') ?> / <?= number_format($expForNextLevel, 0, ',', '.') ?> XP</span>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" style="width: <?= min(100, $levelProgress) ?>%">
|
||||
<?= number_format($levelProgress, 1, ',', '.') ?>%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats Grid -->
|
||||
<div class="stats-grid mb-lg">
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">💰</div>
|
||||
<div class="stat-value"><?= formatMoney($user['balance']) ?></div>
|
||||
<div class="stat-label">Kontostand</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">✈️</div>
|
||||
<div class="stat-value"><?= count($aircraft) ?></div>
|
||||
<div class="stat-label">Flugzeuge</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">🛫</div>
|
||||
<div class="stat-value"><?= count($routes) ?></div>
|
||||
<div class="stat-label">Routen</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">👥</div>
|
||||
<div class="stat-value"><?= formatNumber($todayStats['total_passengers'] ?? 0) ?></div>
|
||||
<div class="stat-label">Passagiere (heute)</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">📈</div>
|
||||
<div class="stat-value"><?= formatMoney($todayStats['total_profit'] ?? 0) ?></div>
|
||||
<div class="stat-label">Gewinn (heute)</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-card">
|
||||
<div class="stat-icon">🏆</div>
|
||||
<div class="stat-value"><?= formatNumber($stats['total_flights'] ?? 0) ?></div>
|
||||
<div class="stat-label">Flüge (gesamt)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick Actions -->
|
||||
<div class="panel mb-lg">
|
||||
<h2 class="panel-header">⚡ Schnellaktionen</h2>
|
||||
|
||||
<div class="d-flex flex-wrap gap-md mt-md">
|
||||
<a href="<?= BASE_PATH ?>airports.php" class="btn btn-primary">
|
||||
🛫 Neue Route erstellen
|
||||
</a>
|
||||
<button class="btn btn-secondary" onclick="openModal('purchaseAircraftModal')">
|
||||
🛒 Flugzeug kaufen
|
||||
</button>
|
||||
<a href="<?= BASE_PATH ?>airports.php" class="btn btn-outline">
|
||||
🌍 Weltkarte ansehen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Aircraft & Routes -->
|
||||
<div class="d-grid" style="grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: var(--space-lg);">
|
||||
<!-- Meine Flugzeuge -->
|
||||
<div class="panel">
|
||||
<h2 class="panel-header">✈️ Meine Flotte</h2>
|
||||
|
||||
<?php if (empty($aircraft)): ?>
|
||||
<div class="card text-center p-lg">
|
||||
<p class="text-muted">Du hast noch keine Flugzeuge.</p>
|
||||
<button class="btn btn-primary mt-md" onclick="openModal('purchaseAircraftModal')">
|
||||
🛒 Jetzt Flugzeug kaufen
|
||||
</button>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="table-container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Typ</th>
|
||||
<th>Zustand</th>
|
||||
<th>Flüge</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($aircraft as $plane): ?>
|
||||
<tr>
|
||||
<td><?= h($plane['name']) ?></td>
|
||||
<td><?= h($plane['aircraft_type']) ?></td>
|
||||
<td>
|
||||
<span class="badge <?= $plane['condition'] > 70 ? 'badge-success' : ($plane['condition'] > 30 ? 'badge-warning' : 'badge-danger') ?>">
|
||||
<?= number_format($plane['condition'], 1, ',', '.') ?>%
|
||||
</span>
|
||||
</td>
|
||||
<td><?= formatNumber($plane['flights_count']) ?></td>
|
||||
<td>
|
||||
<?php
|
||||
// Finde eine Route für dieses Flugzeug
|
||||
$hasRoute = false;
|
||||
foreach ($routes as $route) {
|
||||
$hasRoute = true;
|
||||
break;
|
||||
}
|
||||
if ($hasRoute): ?>
|
||||
<button class="btn btn-sm btn-success"
|
||||
onclick="startFlightFromAircraft(<?= $plane['id'] ?>)">
|
||||
▶️ Fliegen
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">Keine Route</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- Meine Routen -->
|
||||
<div class="panel">
|
||||
<h2 class="panel-header">🛫 Meine Routen</h2>
|
||||
|
||||
<?php if (empty($routes)): ?>
|
||||
<div class="card text-center p-lg">
|
||||
<p class="text-muted">Du hast noch keine Routen.</p>
|
||||
<a href="<?= BASE_PATH ?>airports.php" class="btn btn-primary mt-md">
|
||||
🛫 Jetzt Route erstellen
|
||||
</a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="table-container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Von</th>
|
||||
<th>Nach</th>
|
||||
<th>Distanz</th>
|
||||
<th>Nachfrage</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($routes as $route): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?= h($route['from_iata']) ?></strong>
|
||||
<br><small><?= h($route['from_city']) ?></small>
|
||||
</td>
|
||||
<td>
|
||||
<strong><?= h($route['to_iata']) ?></strong>
|
||||
<br><small><?= h($route['to_city']) ?></small>
|
||||
</td>
|
||||
<td><?= formatNumber($route['distance_km']) ?> km</td>
|
||||
<td>
|
||||
<div class="progress" style="width: 80px; height: 12px;">
|
||||
<div class="progress-bar" style="width: <?= $route['demand_level'] ?>%">
|
||||
<?= $route['demand_level'] ?>%
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Letzte Flüge -->
|
||||
<div class="panel mt-lg">
|
||||
<h2 class="panel-header">📜 Letzte Flüge</h2>
|
||||
|
||||
<?php
|
||||
$recentFlights = $db->select(
|
||||
"SELECT f.*, a1.iata_code as from_iata, a2.iata_code as to_iata,
|
||||
ac.name as aircraft_name, ac.aircraft_type
|
||||
FROM flights f
|
||||
JOIN routes r ON f.route_id = r.id
|
||||
JOIN airports a1 ON r.from_airport_id = a1.id
|
||||
JOIN airports a2 ON r.to_airport_id = a2.id
|
||||
JOIN aircraft ac ON f.aircraft_id = ac.id
|
||||
WHERE f.user_id = ?
|
||||
ORDER BY f.created_at DESC
|
||||
LIMIT 10",
|
||||
[$userId]
|
||||
);
|
||||
?>
|
||||
|
||||
<?php if (empty($recentFlights)): ?>
|
||||
<p class="text-center text-muted p-lg">Noch keine Flüge durchgeführt.</p>
|
||||
<?php else: ?>
|
||||
<div class="table-container">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Flug</th>
|
||||
<th>Flugzeug</th>
|
||||
<th>Passagiere</th>
|
||||
<th>Umsatz</th>
|
||||
<th>Gewinn</th>
|
||||
<th>Zeit</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($recentFlights as $flight): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<strong><?= h($flight['from_iata']) ?></strong> →
|
||||
<strong><?= h($flight['to_iata']) ?></strong>
|
||||
</td>
|
||||
<td><?= h($flight['aircraft_name']) ?></td>
|
||||
<td><?= formatNumber($flight['passengers']) ?></td>
|
||||
<td><?= formatMoney($flight['revenue']) ?></td>
|
||||
<td class="<?= $flight['profit'] >= 0 ? 'text-success' : 'text-danger' ?>">
|
||||
<?= formatMoney($flight['profit']) ?>
|
||||
</td>
|
||||
<td><?= timeAgo($flight['created_at']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Flugzeug kaufen Modal -->
|
||||
<div id="purchaseAircraftModal" class="modal-overlay">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">🛒 Flugzeug kaufen</h3>
|
||||
<button class="modal-close" onclick="closeModal('purchaseAircraftModal')">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="mb-md">Dein Kontostand: <strong><?= formatMoney($user['balance']) ?></strong></p>
|
||||
|
||||
<div class="d-grid gap-md">
|
||||
<?php foreach ($config['aircraft_types'] as $type => $specs): ?>
|
||||
<?php
|
||||
$canAfford = $user['balance'] >= $specs['purchase_price'];
|
||||
?>
|
||||
<div class="card <?= $canAfford ? '' : 'text-muted' ?>"
|
||||
style="<?= $canAfford ? '' : 'opacity: 0.6;' ?>">
|
||||
<div class="d-flex flex-between flex-center">
|
||||
<div>
|
||||
<h4><?= h($type) ?></h4>
|
||||
<p class="text-sm">
|
||||
Sitze: <?= $specs['seat_capacity'] ?> |
|
||||
Reichweite: <?= formatNumber($specs['range_km']) ?> km
|
||||
</p>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<div class="stat-value text-primary"><?= formatMoney($specs['purchase_price']) ?></div>
|
||||
<?php if ($canAfford): ?>
|
||||
<button class="btn btn-sm btn-success mt-sm"
|
||||
onclick="purchaseAircraft('<?= h($type) ?>')">
|
||||
Kaufen
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<span class="badge badge-danger">Zu teuer</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Flug starten Modal -->
|
||||
<div id="flightModal" class="modal-overlay">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">▶️ Flug starten</h3>
|
||||
<button class="modal-close" onclick="closeModal('flightModal')">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Wähle ein Flugzeug für diese Route:</p>
|
||||
|
||||
<div id="flightAircraftList" class="d-grid gap-md mt-md">
|
||||
<!-- Wird per JavaScript gefüllt -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast Container -->
|
||||
<div id="toastContainer" class="toast-container"></div>
|
||||
|
||||
<script>
|
||||
// Globale Daten für JavaScript
|
||||
const GAME_DATA = {
|
||||
routes: <?= json_encode($routes) ?>,
|
||||
aircraft: <?= json_encode($aircraft) ?>,
|
||||
userBalance: <?= $user['balance'] ?>
|
||||
};
|
||||
|
||||
function startFlightFromAircraft(aircraftId) {
|
||||
const modal = document.getElementById('flightModal');
|
||||
const list = document.getElementById('flightAircraftList');
|
||||
|
||||
// Gefilterte Liste erstellen
|
||||
const aircraftHtml = GAME_DATA.aircraft.map(ac => {
|
||||
if (ac.id == aircraftId) {
|
||||
return `
|
||||
<div class="card">
|
||||
<div class="d-flex flex-between flex-center">
|
||||
<div>
|
||||
<h4>${ac.name}</h4>
|
||||
<p class="text-sm">${ac.aircraft_type} | Sitze: ${ac.seat_capacity}</p>
|
||||
<p class="text-sm">Zustand: ${ac.condition}%</p>
|
||||
</div>
|
||||
<button class="btn btn-success" onclick="executeFlightForAircraft(${ac.id})">
|
||||
▶️ Flug starten!
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return '';
|
||||
}).join('');
|
||||
|
||||
list.innerHTML = aircraftHtml;
|
||||
openModal('flightModal');
|
||||
}
|
||||
|
||||
async function executeFlightForAircraft(aircraftId) {
|
||||
// Einfach die erste Route verwenden für Demo
|
||||
if (GAME_DATA.routes.length > 0) {
|
||||
closeModal('flightModal');
|
||||
|
||||
try {
|
||||
const result = await api.post('game.php?action=flight', {
|
||||
route_id: GAME_DATA.routes[0].id,
|
||||
aircraft_id: aircraftId
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
showToast(`Flug erfolgreich! +${formatMoney(result.profit)}`, 'success');
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
} else {
|
||||
showToast(result.error || 'Flug fehlgeschlagen', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('Netzwerkfehler', 'error');
|
||||
}
|
||||
} else {
|
||||
showToast('Keine Route verfügbar!', 'warning');
|
||||
}
|
||||
}
|
||||
|
||||
function purchaseAircraft(type) {
|
||||
api.post('game.php?action=purchase', { aircraft_type: type })
|
||||
.then(result => {
|
||||
if (result.success) {
|
||||
showToast(`${type} gekauft für ${formatMoney(result.price)}`, 'success');
|
||||
closeModal('purchaseAircraftModal');
|
||||
setTimeout(() => location.reload(), 1500);
|
||||
} else {
|
||||
showToast(result.error || 'Kauf fehlgeschlagen', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateDashboard() {
|
||||
// Dashboard aktualisieren (wird bei Bedarf erweitert)
|
||||
location.reload();
|
||||
}
|
||||
</script>
|
||||
<script src="<?= BASE_PATH ?>assets/js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user