208 lines
11 KiB
HTML
Executable File
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> |