- market.php: Buy new aircraft (6 types: Dash8 to A380) - aircraft.php: Fleet management, crew assignment, route planning - finances.php: Account balance, transactions, loans - Database.php: Extended with loan/transaction/aircraft methods - schema.sql: Aircraft types table, 10M starting balance, INSERT OR IGNORE - dashboard.php: Updated with fleet/financial overview - Fixed: duplicate function declarations in Database.php - Fixed: initializeSchema() with idempotent error handling
372 lines
18 KiB
PHP
372 lines
18 KiB
PHP
<?php
|
||
/**
|
||
* Airline Tycoon - Flotten-Übersicht
|
||
* Zeigt alle Flugzeuge des Spielers mit Zustand und Wartungsstatus
|
||
*/
|
||
|
||
require_once __DIR__ . '/../src/bootstrap.php';
|
||
|
||
// Auth Check
|
||
if (!Session::isLoggedIn()) {
|
||
header('Location: login.php');
|
||
exit;
|
||
}
|
||
|
||
$userId = Session::getUserId();
|
||
$user = $db->getUserById($userId);
|
||
$aircraft = $db->getUserAircraft($userId);
|
||
$config = $db->getConfig();
|
||
|
||
// Wartungsstatus prüfen
|
||
$maintenanceDue = $db->checkMaintenanceDue($userId);
|
||
?>
|
||
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Flotte - <?= htmlspecialchars($user['airline_name']) ?> - <?= $config['app']['name'] ?></title>
|
||
<link rel="stylesheet" href="<?= $basePath ?>assets/css/style.css">
|
||
<link href="https://fonts.googleapis.com/css2?family=Bangers&family=Patrick+Hand&family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
|
||
</head>
|
||
<body data-theme="<?= $theme ?? 'light' ?>">
|
||
<!-- Navbar -->
|
||
<nav class="navbar">
|
||
<div class="navbar-brand">
|
||
<a href="<?= $basePath ?>dashboard.php" class="navbar-logo">✈️ <?= $config['app']['name'] ?></a>
|
||
</div>
|
||
<button class="navbar-toggle" aria-label="Menü">☰</button>
|
||
<div class="navbar-menu">
|
||
<a href="<?= $basePath ?>dashboard.php" class="navbar-item">Dashboard</a>
|
||
<a href="<?= $basePath ?>airports.php" class="navbar-item">Flughäfen</a>
|
||
<a href="<?= $basePath ?>aircraft.php" class="navbar-item active">Flotte</a>
|
||
<a href="<?= $basePath ?>finances.php" class="navbar-item">Finanzen</a>
|
||
<a href="<?= $basePath ?>market.php" class="navbar-item">Markt</a>
|
||
<button class="theme-toggle" id="themeToggle" title="Theme wechseln">🌙</button>
|
||
</div>
|
||
</nav>
|
||
|
||
<!-- Main Content -->
|
||
<main class="container">
|
||
<!-- Header -->
|
||
<section class="hero hero-sm">
|
||
<div class="hero-content">
|
||
<h1 class="hero-title">🛫 Deine Flotte</h1>
|
||
<p class="hero-subtitle">Verwalte deine Flugzeuge, prüfe den Zustand und buche Wartungen</p>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Wartungshinweis -->
|
||
<?php if (!empty($maintenanceDue)): ?>
|
||
<div class="alert alert-warning mb-lg">
|
||
<div class="alert-icon">⚠️</div>
|
||
<div class="alert-content">
|
||
<strong>Wartung fällig!</strong>
|
||
<p>Bei <?= count($maintenanceDue) ?> Flugzeug(en) ist die Wartung überfällig.</p>
|
||
</div>
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<!-- Statistik-Grid -->
|
||
<div class="stats-grid mt-lg">
|
||
<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"><?= number_format(array_sum(array_column($aircraft, 'seat_capacity')), 0, ',', '.') ?></div>
|
||
<div class="stat-label">Gesamtkapazität</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon">🛡️</div>
|
||
<div class="stat-value"><?= count($maintenanceDue) ?></div>
|
||
<div class="stat-label">Wartung fällig</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-icon">📅</div>
|
||
<div class="stat-value"><?= $config['game']['maintenance_interval'] ?></div>
|
||
<div class="stat-label">Flüge bis Wartung</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Flugzeugliste -->
|
||
<section class="panel mt-xl">
|
||
<div class="panel-header">
|
||
<h2 class="panel-title">🛩️ Alle Flugzeuge</h2>
|
||
<a href="<?= $basePath ?>market.php" class="btn btn-primary">
|
||
➕ Flugzeug kaufen
|
||
</a>
|
||
</div>
|
||
|
||
<?php if (empty($aircraft)): ?>
|
||
<div class="empty-state">
|
||
<div class="empty-icon">✈️</div>
|
||
<h3>Keine Flugzeuge vorhanden</h3>
|
||
<p>Du besitzt noch keine Flugzeuge. Kaufe dein erstes Flugzeug im Markt!</p>
|
||
<a href="<?= $basePath ?>market.php" class="btn btn-primary btn-lg mt-md">Zum Markt</a>
|
||
</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>Nächste Wartung</th>
|
||
<th>Reichweite</th>
|
||
<th>Kapazität</th>
|
||
<th>Aktionen</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($aircraft as $plane): ?>
|
||
<?php
|
||
$conditionClass = $plane['condition'] >= 80 ? 'text-success' : ($plane['condition'] >= 50 ? 'text-warning' : 'text-danger');
|
||
$maintenanceProgress = min(100, ($plane['flights_count'] / $config['game']['maintenance_interval']) * 100);
|
||
$needsMaintenance = $plane['flights_count'] >= $config['game']['maintenance_interval'];
|
||
?>
|
||
<tr>
|
||
<td>
|
||
<strong><?= htmlspecialchars($plane['name']) ?></strong>
|
||
</td>
|
||
<td>
|
||
<span class="badge"><?= htmlspecialchars($plane['aircraft_type']) ?></span>
|
||
</td>
|
||
<td>
|
||
<div class="d-flex flex-col align-start gap-sm">
|
||
<span class="<?= $conditionClass ?>">
|
||
<?= number_format($plane['condition'], 1, ',', '.') ?>%
|
||
</span>
|
||
<div class="progress" style="width: 80px;">
|
||
<div class="progress-bar" style="width: <?= $plane['condition'] ?>%"></div>
|
||
</div>
|
||
</div>
|
||
</td>
|
||
<td><?= number_format($plane['flights_count'], 0, ',', '.') ?></td>
|
||
<td>
|
||
<?php if ($needsMaintenance): ?>
|
||
<span class="badge badge-danger">⚠️ Jetzt!</span>
|
||
<?php else: ?>
|
||
<span class="text-secondary">
|
||
<?= number_format($config['game']['maintenance_interval'] - $plane['flights_count'], 0, ',', '.') ?> Flüge
|
||
</span>
|
||
<?php endif; ?>
|
||
</td>
|
||
<td><?= number_format($plane['range_km'], 0, ',', '.') ?> km</td>
|
||
<td><?= number_format($plane['seat_capacity'], 0, ',', '.') ?> Pax</td>
|
||
<td>
|
||
<div class="d-flex gap-sm">
|
||
<?php if ($needsMaintenance): ?>
|
||
<button class="btn btn-sm btn-warning"
|
||
onclick="performMaintenance(<?= $plane['id'] ?>)"
|
||
data-modal="maintenanceModal">
|
||
🔧 Wartung
|
||
</button>
|
||
<?php endif; ?>
|
||
<button class="btn btn-sm btn-secondary"
|
||
onclick="showAircraftDetails(<?= $plane['id'] ?>)">
|
||
📋 Details
|
||
</button>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<?php endif; ?>
|
||
</section>
|
||
|
||
<!-- Flugzeug-Typen Übersicht -->
|
||
<section class="panel mt-xl">
|
||
<h2 class="panel-title">📊 Flugzeugtypen</h2>
|
||
<div class="aircraft-types-grid mt-md">
|
||
<?php foreach ($config['aircraft_types'] as $type => $specs): ?>
|
||
<div class="aircraft-type-card">
|
||
<h3 class="aircraft-type-name"><?= htmlspecialchars($type) ?></h3>
|
||
<div class="aircraft-type-stats">
|
||
<div class="aircraft-type-stat">
|
||
<span class="stat-label">Preis:</span>
|
||
<span class="stat-value"><?= number_format($specs['purchase_price'], 0, ',', '.') ?> €</span>
|
||
</div>
|
||
<div class="aircraft-type-stat">
|
||
<span class="stat-label">Reichweite:</span>
|
||
<span class="stat-value"><?= number_format($specs['range_km'], 0, ',', '.') ?> km</span>
|
||
</div>
|
||
<div class="aircraft-type-stat">
|
||
<span class="stat-label">Kapazität:</span>
|
||
<span class="stat-value"><?= $specs['seat_capacity'] ?> Pax</span>
|
||
</div>
|
||
<div class="aircraft-type-stat">
|
||
<span class="stat-label">Crew-Kosten:</span>
|
||
<span class="stat-value"><?= $specs['crew_cost_per_tick'] ?? 500 ?> €/Tick</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<!-- Wartungs-Modal -->
|
||
<div class="modal-overlay" id="maintenanceModal">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3 class="modal-title">🔧 Wartung durchführen</h3>
|
||
<button class="modal-close" onclick="closeModal('maintenanceModal')">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div id="maintenanceInfo">
|
||
<p>Flugzeug: <strong id="maintenanceAircraftName"></strong></p>
|
||
<p>Zustand: <span id="maintenanceCondition"></span></p>
|
||
<p>Geschätzte Kosten: <strong id="maintenanceCost"></strong></p>
|
||
</div>
|
||
<div class="alert alert-info mt-md">
|
||
<p>Nach der Wartung wird der Zustand auf 100% zurückgesetzt und der Flugzähler zurückgesetzt.</p>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-secondary" onclick="closeModal('maintenanceModal')">Abbrechen</button>
|
||
<button class="btn btn-warning" id="confirmMaintenanceBtn">Wartung durchführen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Details-Modal -->
|
||
<div class="modal-overlay" id="detailsModal">
|
||
<div class="modal">
|
||
<div class="modal-header">
|
||
<h3 class="modal-title">📋 Flugzeug-Details</h3>
|
||
<button class="modal-close" onclick="closeModal('detailsModal')">×</button>
|
||
</div>
|
||
<div class="modal-body" id="aircraftDetails">
|
||
<!-- Dynamisch geladen -->
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button class="btn btn-secondary" onclick="closeModal('detailsModal')">Schließen</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Toast Container -->
|
||
<div id="toastContainer" class="toast-container"></div>
|
||
|
||
<script src="<?= $basePath ?>assets/js/main.js"></script>
|
||
<script>
|
||
let selectedAircraftId = null;
|
||
|
||
async function performMaintenance(aircraftId) {
|
||
selectedAircraftId = aircraftId;
|
||
|
||
try {
|
||
const response = await fetch(`api/game.php?action=aircraft_details&id=${aircraftId}`);
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
document.getElementById('maintenanceAircraftName').textContent = data.aircraft.name;
|
||
document.getElementById('maintenanceCondition').textContent = data.aircraft.condition.toFixed(1) + '%';
|
||
document.getElementById('maintenanceCost').textContent = formatMoney(data.maintenance_cost);
|
||
openModal('maintenanceModal');
|
||
}
|
||
} catch (error) {
|
||
showToast('Fehler beim Laden der Daten', 'error');
|
||
}
|
||
}
|
||
|
||
document.getElementById('confirmMaintenanceBtn').addEventListener('click', async () => {
|
||
if (!selectedAircraftId) return;
|
||
|
||
try {
|
||
const formData = new FormData();
|
||
formData.append('action', 'maintenance');
|
||
formData.append('aircraft_id', selectedAircraftId);
|
||
|
||
const response = await fetch('api/game.php', {
|
||
method: 'POST',
|
||
body: formData
|
||
});
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
showToast('Wartung erfolgreich durchgeführt!', 'success');
|
||
closeModal('maintenanceModal');
|
||
setTimeout(() => location.reload(), 1000);
|
||
} else {
|
||
showToast(data.error || 'Wartung fehlgeschlagen', 'error');
|
||
}
|
||
} catch (error) {
|
||
showToast('Netzwerkfehler', 'error');
|
||
}
|
||
});
|
||
|
||
async function showAircraftDetails(aircraftId) {
|
||
try {
|
||
const response = await fetch(`api/game.php?action=aircraft_details&id=${aircraftId}`);
|
||
const data = await response.json();
|
||
|
||
if (data.success) {
|
||
const aircraft = data.aircraft;
|
||
const history = data.maintenance_history || [];
|
||
|
||
let html = `
|
||
<div class="aircraft-detail-header">
|
||
<h4>${aircraft.name}</h4>
|
||
<span class="badge">${aircraft.aircraft_type}</span>
|
||
</div>
|
||
<div class="aircraft-detail-grid">
|
||
<div class="detail-item">
|
||
<span class="detail-label">Zustand:</span>
|
||
<span class="detail-value ${aircraft.condition >= 80 ? 'text-success' : aircraft.condition >= 50 ? 'text-warning' : 'text-danger'}">
|
||
${aircraft.condition.toFixed(1)}%
|
||
</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Flüge gesamt:</span>
|
||
<span class="detail-value">${aircraft.flights_count}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Reichweite:</span>
|
||
<span class="detail-value">${aircraft.range_km.toLocaleString()} km</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Kapazität:</span>
|
||
<span class="detail-value">${aircraft.seat_capacity} Pax</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Kaufpreis:</span>
|
||
<span class="detail-value">${formatMoney(aircraft.purchase_price)}</span>
|
||
</div>
|
||
<div class="detail-item">
|
||
<span class="detail-label">Nächste Wartung in:</span>
|
||
<span class="detail-value">${50 - (aircraft.flights_count % 50)} Flügen</span>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
if (history.length > 0) {
|
||
html += `<h4 class="mt-lg">Wartungshistorie</h4><ul class="maintenance-history">`;
|
||
history.forEach(h => {
|
||
html += `<li>${h.created_at}: ${h.maintenance_type} - ${formatMoney(h.cost)} (${h.condition_before}% → ${h.condition_after}%)</li>`;
|
||
});
|
||
html += `</ul>`;
|
||
}
|
||
|
||
document.getElementById('aircraftDetails').innerHTML = html;
|
||
openModal('detailsModal');
|
||
}
|
||
} catch (error) {
|
||
showToast('Fehler beim Laden der Details', 'error');
|
||
}
|
||
}
|
||
|
||
function formatMoney(amount) {
|
||
return new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(amount);
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|