Járműkezelés majdnem kész frontend
This commit is contained in:
@@ -3,22 +3,6 @@
|
||||
<!-- Garage Background Image -->
|
||||
<div class="fixed inset-0 z-0">
|
||||
<div class="absolute inset-0 bg-[url('@/assets/garage-bg.png')] bg-cover bg-center bg-fixed"></div>
|
||||
|
||||
<!-- ── 3D Wall Text: Szerviznaptár (left wall calendar area) ── -->
|
||||
<div
|
||||
class="absolute top-[18%] left-[12%] text-2xl font-bold text-[#04151F] opacity-80 uppercase pointer-events-none"
|
||||
style="transform: perspective(500px) rotateY(2deg) rotateX(-1deg);"
|
||||
>
|
||||
{{ t('menu.calendar') }}
|
||||
</div>
|
||||
|
||||
<!-- ── 3D Wall Text: SERVICE FINDER (center wall logo area) ── -->
|
||||
<div
|
||||
class="absolute top-[35%] left-[45%] text-4xl font-extrabold text-[#418890] tracking-widest opacity-90 pointer-events-none"
|
||||
style="transform: perspective(800px) rotateY(-5deg);"
|
||||
>
|
||||
SERVICE FINDER
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Zen Mode FAB ── -->
|
||||
@@ -78,43 +62,21 @@
|
||||
<!-- Top header bar -->
|
||||
<div class="h-12 bg-slate-700 w-full shrink-0 flex items-center px-4">
|
||||
<span class="text-white font-bold text-sm tracking-wide">🚗 {{ t('dashboard.myVehicles') }}</span>
|
||||
<span class="ml-auto text-xs text-white/60">{{ vehicleStore.vehicles.length }} {{ t('dashboard.vehicles') }}</span>
|
||||
<span class="ml-auto text-xs text-white/60">{{ displayVehicles.length }} {{ t('dashboard.vehicles') }}</span>
|
||||
</div>
|
||||
<!-- Content -->
|
||||
<div class="p-4 flex-1 flex flex-col text-slate-800 overflow-hidden">
|
||||
<div class="p-4 flex-1 flex flex-col text-slate-800 min-h-0">
|
||||
<div class="flex-1 overflow-y-auto space-y-2">
|
||||
<div
|
||||
v-for="vehicle in vehicleStore.vehicles.slice(0, 4)"
|
||||
<VehicleCardCompact
|
||||
v-for="vehicle in displayVehicles.slice(0, 3)"
|
||||
:key="vehicle.id"
|
||||
class="rounded-xl border border-slate-200 bg-slate-50 p-3 transition-colors hover:bg-slate-100 cursor-pointer"
|
||||
>
|
||||
<!-- License Plate -->
|
||||
<div
|
||||
v-if="vehicle.license_plate"
|
||||
class="mb-1 inline-flex items-center overflow-hidden rounded-md border border-gray-400 shadow-sm"
|
||||
>
|
||||
<div class="flex h-6 w-6 items-center justify-center bg-[#003399] text-[7px] font-bold leading-tight text-white">
|
||||
<span class="block text-center leading-tight">SF</span>
|
||||
</div>
|
||||
<div class="flex h-6 items-center bg-white px-2">
|
||||
<span class="font-mono text-xs font-bold tracking-widest text-gray-900">
|
||||
{{ vehicle.license_plate }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Brand + Model -->
|
||||
<h3 class="text-sm font-bold text-slate-800">
|
||||
{{ vehicle.brand || t('dashboard.statusUnknown') }}
|
||||
<span v-if="vehicle.model" class="font-normal text-slate-500"> {{ vehicle.model }}</span>
|
||||
</h3>
|
||||
<!-- Mileage -->
|
||||
<p class="mt-0.5 text-xs text-slate-400">
|
||||
{{ vehicle.current_mileage?.toLocaleString() || 0 }} {{ t('dashboard.mileage') }}
|
||||
</p>
|
||||
</div>
|
||||
<!-- Empty state -->
|
||||
:vehicle="vehicle"
|
||||
@click="openCard('vehicles')"
|
||||
@plate-click="openCard('vehicles', vehicle.license_plate)"
|
||||
/>
|
||||
<!-- Empty state (only when BOTH real and mock are empty) -->
|
||||
<div
|
||||
v-if="vehicleStore.vehicles.length === 0"
|
||||
v-if="displayVehicles.length === 0"
|
||||
class="flex flex-col items-center justify-center py-6 text-slate-400"
|
||||
>
|
||||
<svg class="w-10 h-10 mb-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -124,10 +86,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Bottom CTA button -->
|
||||
<button class="mt-auto mb-4 mx-auto px-8 py-3 bg-slate-700 hover:bg-slate-800 text-white rounded-2xl font-medium text-sm transition-colors shadow-md">
|
||||
+ {{ t('dashboard.addVehicle') }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- ════════════════════════════════════════════════════════════
|
||||
@@ -319,41 +277,52 @@
|
||||
</Transition>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════════════
|
||||
Flip Modal – 3D átforduló részletes nézet
|
||||
Flip Modal – 3D átforduló részletes nézet (Teleport to body)
|
||||
═══════════════════════════════════════════════════════════════════ -->
|
||||
<div
|
||||
v-if="activeCard"
|
||||
class="fixed inset-0 z-[100] flex items-center justify-center bg-slate-900/85 backdrop-blur-md"
|
||||
@click.self="activeCard = null"
|
||||
>
|
||||
<div class="bg-white w-11/12 max-w-6xl h-[85vh] rounded-3xl shadow-2xl flex flex-col overflow-hidden animate-flip-in relative">
|
||||
<!-- Close button (X) -->
|
||||
<button
|
||||
class="absolute top-4 right-4 z-10 flex h-10 w-10 items-center justify-center rounded-full bg-slate-200/80 text-slate-600 hover:bg-slate-300 hover:text-slate-800 transition-colors text-xl font-bold"
|
||||
@click="activeCard = null"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="activeCard"
|
||||
class="fixed inset-0 z-[9999] flex items-center justify-center bg-slate-900/85 backdrop-blur-md"
|
||||
@click.self="activeCard = null"
|
||||
>
|
||||
<div class="bg-white w-11/12 max-w-5xl h-auto min-h-[500px] pb-6 rounded-3xl shadow-2xl flex flex-col overflow-hidden animate-flip-in relative">
|
||||
<!-- Close button (X) -->
|
||||
<button
|
||||
class="absolute top-4 right-4 z-10 flex h-10 w-10 items-center justify-center rounded-full bg-slate-200/80 text-slate-600 hover:bg-slate-300 hover:text-slate-800 transition-colors text-xl font-bold"
|
||||
@click="activeCard = null"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
|
||||
<!-- Dynamic content area -->
|
||||
<div class="flex-1 p-8 overflow-y-auto text-slate-800">
|
||||
<h2 class="text-2xl font-bold mb-6">
|
||||
{{ t('dashboard.detailedView') }}: <span class="text-slate-500">{{ activeCard }}</span>
|
||||
</h2>
|
||||
<p class="text-slate-500">
|
||||
{{ t('dashboard.modalPlaceholder') }}
|
||||
</p>
|
||||
<!-- Dynamic content area -->
|
||||
<div class="flex-1 p-8 overflow-y-auto text-slate-800">
|
||||
<!-- ── Private Vehicle Manager ── -->
|
||||
<PrivateVehicleManager v-if="activeCard === 'vehicles'" :target-vehicle-id="selectedTargetId" />
|
||||
|
||||
<!-- ── Fallback for other cards ── -->
|
||||
<template v-if="activeCard && activeCard !== 'vehicles'">
|
||||
<h2 class="text-2xl font-bold mb-6">
|
||||
{{ t('dashboard.detailedView') }}: <span class="text-slate-500">{{ activeCard }}</span>
|
||||
</h2>
|
||||
<p class="text-slate-500">
|
||||
{{ t('dashboard.modalPlaceholder') }}
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</div><!-- end min-h-screen -->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import { useVehicleStore } from '../stores/vehicle'
|
||||
import PrivateVehicleManager from '../components/dashboard/PrivateVehicleManager.vue'
|
||||
import VehicleCardCompact from '../components/vehicle/VehicleCardCompact.vue'
|
||||
import { mockVehicles } from '../data/mockVehicles'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -365,10 +334,30 @@ const isUiVisible = ref(true)
|
||||
|
||||
// ── Flip Modal State ──
|
||||
const activeCard = ref<string | null>(null)
|
||||
const openCard = (cardId: string) => {
|
||||
const selectedTargetId = ref<string | null>(null)
|
||||
const openCard = (cardId: string, targetId: string | null = null) => {
|
||||
activeCard.value = cardId
|
||||
selectedTargetId.value = targetId
|
||||
}
|
||||
|
||||
/**
|
||||
* Smart vehicle display: always show at least 3 vehicles for testing.
|
||||
* - Real vehicles from the backend come first (sorted by is_primary).
|
||||
* - If fewer than 3 real vehicles exist, fill the gap with mock data
|
||||
* (excluding any mock that has the same license plate as a real vehicle).
|
||||
*/
|
||||
const displayVehicles = computed(() => {
|
||||
const real = vehicleStore.vehicles
|
||||
if (real.length >= 3) return real.slice(0, 3)
|
||||
|
||||
const needed = 3 - real.length
|
||||
const realPlates = new Set(real.map(v => v.license_plate || v.licensePlate))
|
||||
const mocksToUse = mockVehicles
|
||||
.filter(mv => !real.some(rv => (rv.license_plate || rv.licensePlate) === (mv.license_plate || mv.licensePlate)))
|
||||
.slice(0, needed)
|
||||
return [...real, ...mocksToUse]
|
||||
})
|
||||
|
||||
// ── Placeholder notifications ──
|
||||
const notifications = ref([
|
||||
{ icon: '🔧', title: 'Service due for BMW X5', time: '2 hours ago' },
|
||||
|
||||
Reference in New Issue
Block a user