Files

208 lines
11 KiB
HTML
Executable File

<!DOCTYPE html>
<html lang="hu">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Service Finder - Garázs</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<style>
body { background-color: #f0f2f5; font-family: 'Segoe UI', sans-serif; }
.main-container { max-width: 1000px; margin: 30px auto; }
/* KOMPAKT CSEMPE STÍLUSOK */
.garage-card {
border: none; border-radius: 12px; background: white;
box-shadow: 0 2px 8px rgba(0,0,0,0.04); transition: transform 0.2s, box-shadow 0.2s;
cursor: pointer; height: 100%; position: relative; overflow: hidden;
}
.garage-card:hover { transform: translateY(-3px); box-shadow: 0 5px 15px rgba(0,0,0,0.1); }
/* Fejléc sáv a kártyán (Márka színe lehetne később) */
.card-top-strip { height: 6px; background: #0d6efd; width: 100%; position: absolute; top: 0; left: 0; }
.car-icon { font-size: 2rem; color: #6c757d; } /* Kisebb ikon */
.plate-badge {
background: #ffcc00; color: black; border: 1px solid #e0b000;
font-family: 'Courier New', monospace; font-weight: bold; padding: 2px 6px;
font-size: 0.9rem; border-radius: 4px; display: inline-block; letter-spacing: 1px;
}
.role-badge { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.5px; }
/* Új Hozzáadása Csempe - Kompakt */
.add-card {
border: 2px dashed #cbd5e1; background: rgba(255,255,255,0.5);
display: flex; align-items: center; justify-content: center;
color: #64748b; min-height: 160px; /* Alacsonyabb */
}
.add-card:hover { border-color: #0d6efd; color: #0d6efd; background: white; }
.wizard-card { background: white; border-radius: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.05); padding: 30px; }
.step { width: 30px; height: 30px; background: #e9ecef; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; margin-right: 10px; font-weight: bold; }
.step.active { background: #0d6efd; color: white; }
</style>
</head>
<body>
<div id="app">
<nav class="navbar navbar-dark bg-primary mb-4 shadow-sm py-2">
<div class="container">
<span class="navbar-brand mb-0 h1 fs-5"><i class="bi bi-speedometer2 me-2"></i>Service Finder</span>
<span class="text-white small opacity-75">Demo User</span>
</div>
</nav>
<div class="container main-container">
<div v-if="view === 'dashboard'">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="fw-bold text-secondary mb-0">Saját Járművek</h4>
<span class="badge bg-secondary rounded-pill">{{ myCars.length }} db</span>
</div>
<div class="row g-3">
<div class="col-md-6 col-lg-4" v-for="car in myCars" :key="car.vehicle_id">
<div class="card garage-card p-3">
<div class="card-top-strip"></div>
<div class="d-flex justify-content-between align-items-start mb-2">
<div>
<h5 class="fw-bold mb-0 text-dark">{{ car.brand }}</h5>
<div class="text-muted small">{{ car.model }}</div>
</div>
<i v-if="car.category === 'Motor'" class="bi bi-bicycle car-icon"></i>
<i v-else class="bi bi-car-front car-icon"></i>
</div>
<div class="mt-2 d-flex justify-content-between align-items-end">
<div class="plate-badge">{{ car.plate }}</div>
<span class="badge bg-light text-dark border role-badge">
{{ translateRole(car.role) }}
</span>
</div>
<div class="text-end mt-3">
<small class="text-primary fw-bold" style="cursor: pointer;">Adatlap megnyitása <i class="bi bi-chevron-right"></i></small>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="garage-card add-card" @click="switchToWizard">
<div class="text-center">
<i class="bi bi-plus-lg fs-3 mb-1"></i>
<h6 class="fw-bold mb-0">Új jármű</h6>
</div>
</div>
</div>
</div>
</div>
<div v-if="view === 'wizard'" class="wizard-card">
<div class="d-flex justify-content-between mb-4">
<h4 class="fw-bold"><span class="step" :class="{active: step <= 3}">{{ step }}</span> Új rögzítése</h4>
<button class="btn btn-close" @click="cancelWizard"></button>
</div>
<div v-if="step === 1">
<div class="row g-3">
<div class="col-md-4"><label class="form-label small fw-bold">Kategória</label><select class="form-select" v-model="form.category"><option v-for="c in uniqueCategories" :value="c">{{ c }}</option></select></div>
<div class="col-md-4"><label class="form-label small fw-bold">Márka</label><select class="form-select" v-model="form.brand" :disabled="!form.category"><option v-for="b in filteredBrands" :value="b">{{ b }}</option></select></div>
<div class="col-md-4"><label class="form-label small fw-bold">Modell</label><select class="form-select" v-model="form.model" :disabled="!form.brand"><option v-for="m in filteredModels" :value="m">{{ m.model }}</option></select></div>
</div>
<div class="mt-4 text-end"><button class="btn btn-primary" :disabled="!form.model" @click="step++">Tovább</button></div>
</div>
<div v-if="step === 2">
<div class="alert alert-info py-2 small">Jármű: <strong>{{ form.brand }} {{ form.model.model }}</strong></div>
<div class="row g-3">
<div class="col-6"><label class="form-label small fw-bold">Alvázszám (VIN) *</label><input class="form-control text-uppercase" v-model="form.vin"></div>
<div class="col-6"><label class="form-label small fw-bold">Rendszám *</label><input class="form-control text-uppercase" v-model="form.plate"></div>
<div class="col-6"><label class="form-label small fw-bold">Km állás *</label><input type="number" class="form-control" v-model="form.mileage"></div>
<div class="col-6"><label class="form-label small fw-bold">Vásárlás dátuma</label><input type="date" class="form-control" v-model="form.purchaseDate"></div>
</div>
<div class="mt-4 d-flex justify-content-between"><button class="btn btn-secondary" @click="step--">Vissza</button><button class="btn btn-success" :disabled="!form.vin || !form.plate" @click="saveVehicle">Mentés</button></div>
</div>
<div v-if="step === 3" class="text-center py-5">
<i class="bi bi-check-circle-fill text-success display-3"></i>
<h4 class="mt-3">Sikeres rögzítés!</h4>
<button class="btn btn-primary mt-4" @click="finishWizard">Vissza a Garázsba</button>
</div>
</div>
</div>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<script>
const { createApp } = Vue
createApp({
data() {
return {
view: 'dashboard',
step: 1,
myCars: [],
catalog: [],
form: { category: "", brand: "", model: null, vin: "", plate: "", mileage: "", purchaseDate: "" }
}
},
computed: {
uniqueCategories() { return [...new Set(this.catalog.map(v => v.category).filter(c => c))].sort(); },
filteredBrands() { return this.form.category ? [...new Set(this.catalog.filter(v => v.category === this.form.category).map(v => v.brand))].sort() : []; },
filteredModels() { return this.form.brand ? this.catalog.filter(v => v.brand === this.form.brand && v.category === this.form.category).sort((a,b)=>a.model.localeCompare(b.model)) : []; }
},
methods: {
// --- FORDÍTÓ FÜGGVÉNY ---
translateRole(role) {
const map = {
'OWNER': 'Tulajdonos',
'DRIVER': 'Sofőr',
'LEASE': 'Lízingelő',
'COMPANY': 'Cég'
};
return map[role] || role; // Ha nincs a listában, marad az eredeti
},
async fetchMyGarage() {
const res = await fetch('/api/my_vehicles');
this.myCars = await res.json();
},
async fetchCatalog() {
const res = await fetch('/api/vehicles');
this.catalog = await res.json();
},
switchToWizard() {
this.step = 1;
this.form = { category: "", brand: "", model: null, vin: "", plate: "", mileage: "", purchaseDate: "" };
this.view = 'wizard';
},
cancelWizard() { this.view = 'dashboard'; },
async saveVehicle() {
const payload = {
model_id: this.form.model.id,
vin: this.form.vin,
plate: this.form.plate,
mileage: parseInt(this.form.mileage),
purchase_date: this.form.purchaseDate || new Date().toISOString().split('T')[0]
};
try {
const res = await fetch('/api/register', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload) });
if(res.ok) { this.step = 3; }
} catch(e) { alert(e); }
},
finishWizard() {
this.fetchMyGarage();
this.view = 'dashboard';
}
},
mounted() {
this.fetchMyGarage();
this.fetchCatalog();
}
}).mount('#app')
</script>
</body>
</html>