feat(phase2): aircraft market, fleet management, finances, loans
- 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
This commit is contained in:
371
www/public/aircraft.php
Normal file
371
www/public/aircraft.php
Normal file
@@ -0,0 +1,371 @@
|
||||
<?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>
|
||||
Reference in New Issue
Block a user