darabolás előtti állapot

This commit is contained in:
Roo
2026-06-18 07:58:30 +00:00
parent 404d04c56b
commit 611307a24b
8 changed files with 120 additions and 40 deletions

View File

@@ -10,7 +10,7 @@
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg> </svg>
<span>{{ t('header.garageSelector') }}</span> <span class="truncate max-w-[120px]">{{ activeGarageName }}</span>
<!-- Chevron down --> <!-- Chevron down -->
<svg <svg
class="w-3 h-3 transition-transform" class="w-3 h-3 transition-transform"
@@ -155,6 +155,21 @@ const isOnCompanyGarage = computed(() => {
return route.path.startsWith('/organization/') return route.path.startsWith('/organization/')
}) })
// ── Active garage name for the button label ────────────────────────
const activeGarageName = computed(() => {
// If on a company garage route, show the active org name
if (isOnCompanyGarage.value && authStore.user?.active_organization_id) {
const activeOrg = authStore.myOrganizations.find(
(org) => org.organization_id === authStore.user?.active_organization_id
)
if (activeOrg) {
return activeOrg.display_name || activeOrg.name || `${t('header.companyPrefix')} #${activeOrg.organization_id}`
}
}
// Default: show personal garage label
return t('header.garageSelector')
})
// ── Check if org is the active one ───────────────────────────────── // ── Check if org is the active one ─────────────────────────────────
function isActiveOrg(org: OrganizationItem): boolean { function isActiveOrg(org: OrganizationItem): boolean {
return authStore.user?.active_organization_id === org.organization_id return authStore.user?.active_organization_id === org.organization_id

View File

@@ -3,13 +3,14 @@
class="flex items-center gap-3 p-2 hover:bg-slate-50 rounded-lg cursor-pointer transition-colors" class="flex items-center gap-3 p-2 hover:bg-slate-50 rounded-lg cursor-pointer transition-colors"
@click="$emit('click', vehicle)" @click="$emit('click', vehicle)"
> >
<!-- Mini EU License Plate (clickable) --> <!-- Mini EU License Plate (clickable) with brand inside -->
<div <div
v-if="vehicle.license_plate" v-if="vehicle.license_plate"
@click.stop="$emit('plate-click', vehicle)" @click.stop="$emit('plate-click', vehicle)"
> >
<VehiclePlateBadge <VehiclePlateBadge
:license-plate="vehicle.license_plate" :license-plate="vehicle.license_plate"
:brand="vehicle.brand"
:country-code="vehicle.countryCode || vehicle.country_code" :country-code="vehicle.countryCode || vehicle.country_code"
size="sm" size="sm"
/> />

View File

@@ -27,7 +27,7 @@
<circle cx="7" cy="17" r="2" /> <circle cx="7" cy="17" r="2" />
<circle cx="17" cy="17" r="2" /> <circle cx="17" cy="17" r="2" />
</svg> </svg>
<span class="text-xs text-slate-400 mt-1">Nincs feltöltött fotó</span> <span class="text-xs text-slate-400 mt-1">{{ t('vehicle.noPhoto') }}</span>
</div> </div>
</template> </template>
</div> </div>
@@ -55,9 +55,10 @@
</p> </p>
</div> </div>
<!-- Dynamic EU License Plate Badge --> <!-- Dynamic EU License Plate Badge (brand inside plate box) -->
<VehiclePlateBadge <VehiclePlateBadge
:license-plate="vehicle.license_plate" :license-plate="vehicle.license_plate"
:brand="vehicle.brand"
:country-code="vehicle.countryCode || vehicle.country_code" :country-code="vehicle.countryCode || vehicle.country_code"
size="md" size="md"
/> />
@@ -110,7 +111,7 @@
<svg class="h-3.5 w-3.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="h-3.5 w-3.5 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg> </svg>
<span class="font-semibold">Következő Szerviz: {{ vehicle.nextService || 'Nincs adat' }}</span> <span class="font-semibold">{{ t('vehicle.nextService') }}: {{ vehicle.nextService || t('vehicle.noData') }}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -122,7 +123,7 @@
? 'bg-amber-400 text-white hover:bg-amber-500' ? 'bg-amber-400 text-white hover:bg-amber-500'
: 'bg-white/60 text-slate-300 opacity-0 group-hover:opacity-100 hover:text-amber-400 hover:bg-white/90'" : 'bg-white/60 text-slate-300 opacity-0 group-hover:opacity-100 hover:text-amber-400 hover:bg-white/90'"
@click.stop="$emit('set-primary', vehicle)" @click.stop="$emit('set-primary', vehicle)"
:title="vehicle.is_primary ? 'Elsődleges jármű' : 'Beállítás elsődlegessé'" :title="vehicle.is_primary ? t('vehicle.primaryVehicle') : t('vehicle.setPrimary')"
> >
<svg class="h-4 w-4" fill="currentColor" viewBox="0 0 24 24"> <svg class="h-4 w-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" /> <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
@@ -143,9 +144,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed } from 'vue' import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import type { VehicleData } from '../../types/vehicle' import type { VehicleData } from '../../types/vehicle'
import VehiclePlateBadge from './VehiclePlateBadge.vue' import VehiclePlateBadge from './VehiclePlateBadge.vue'
const { t } = useI18n()
const props = defineProps<{ const props = defineProps<{
vehicle: VehicleData vehicle: VehicleData
}>() }>()

View File

@@ -1,11 +1,11 @@
<template> <template>
<div <div
class="inline-flex items-center overflow-hidden rounded border border-gray-400 shadow-sm shrink-0 transition-transform duration-200 hover:-translate-y-0.5" class="inline-flex items-stretch overflow-hidden rounded border border-gray-400 shadow-sm shrink-0 transition-transform duration-200 hover:-translate-y-0.5"
:class="containerClasses" :class="containerClasses"
> >
<!-- EU blue band --> <!-- EU blue band -->
<div <div
class="flex flex-col items-center justify-center bg-[#003399] text-white" class="flex flex-col items-center justify-center bg-[#003399] text-white shrink-0"
:class="bandClasses" :class="bandClasses"
> >
<div class="flex flex-wrap justify-center gap-[1px] px-0.5"> <div class="flex flex-wrap justify-center gap-[1px] px-0.5">
@@ -20,12 +20,24 @@
{{ countryCode || 'H' }} {{ countryCode || 'H' }}
</span> </span>
</div> </div>
<!-- Plate number -->
<!-- Plate content: brand + license number -->
<div <div
class="flex items-center justify-center bg-white font-mono font-bold tracking-widest text-gray-900" class="flex flex-col items-center justify-center bg-white font-mono font-bold tracking-widest text-gray-900 min-w-0"
:class="plateClasses" :class="plateClasses"
> >
{{ licensePlate }} <!-- Brand text (discreet, inside the plate box) -->
<span
v-if="brand"
class="text-[9px] leading-tight font-sans font-semibold text-gray-500 uppercase tracking-[0.15em] max-w-full truncate px-0.5"
:class="brandTextClasses"
>
{{ brand }}
</span>
<!-- License plate number -->
<span class="leading-none tracking-widest" :class="numberClasses">
{{ licensePlate }}
</span>
</div> </div>
</div> </div>
</template> </template>
@@ -35,6 +47,7 @@ import { computed } from 'vue'
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
licensePlate: string licensePlate: string
brand?: string
countryCode?: string countryCode?: string
size?: 'sm' | 'md' | 'lg' size?: 'sm' | 'md' | 'lg'
}>(), { }>(), {
@@ -52,9 +65,9 @@ const containerClasses = computed(() => {
const bandClasses = computed(() => { const bandClasses = computed(() => {
switch (props.size) { switch (props.size) {
case 'sm': return 'w-5 h-6 py-0.5' case 'sm': return 'w-5 py-0.5'
case 'lg': return 'w-10 h-14 py-1' case 'lg': return 'w-10 py-1'
default: return 'w-8 h-10 py-0.5' default: return 'w-8 py-0.5'
} }
}) })
@@ -76,9 +89,25 @@ const codeClasses = computed(() => {
const plateClasses = computed(() => { const plateClasses = computed(() => {
switch (props.size) { switch (props.size) {
case 'sm': return 'h-6 px-1.5 text-[10px]' case 'sm': return 'h-6 px-1.5 pt-0 pb-0.5'
case 'lg': return 'h-14 px-2 text-base' case 'lg': return 'h-14 px-2 pt-1 pb-1'
default: return 'h-10 px-1 text-sm' default: return 'h-10 px-1.5 pt-0.5 pb-0.5'
}
})
const brandTextClasses = computed(() => {
switch (props.size) {
case 'sm': return 'text-[7px]'
case 'lg': return 'text-[11px]'
default: return 'text-[9px]'
}
})
const numberClasses = computed(() => {
switch (props.size) {
case 'sm': return 'text-[10px]'
case 'lg': return 'text-base'
default: return 'text-sm'
} }
}) })
</script> </script>

View File

@@ -94,6 +94,21 @@ export default {
achievements: 'Achievements', achievements: 'Achievements',
detailedView: 'Detailed View', detailedView: 'Detailed View',
modalPlaceholder: 'Content for this section will be available soon.', modalPlaceholder: 'Content for this section will be available soon.',
// ── Dashboard Card 2: Costs & Quick Actions ──
costsTitle: 'Costs & Quick Actions',
vehicle: 'Vehicle',
selectVehicle: 'Select a vehicle',
noVehicleSelected: 'No vehicle selected. Add a vehicle first!',
recordFueling: 'Record Fueling',
parkingToll: 'Parking / Toll',
additionalCosts: 'Additional Costs',
findServicePartners: 'Find reliable service partners',
allVehicles: 'All Vehicles',
// ── Dashboard Card 5: Profile & Trust ──
viewProfile: 'View Profile',
trustScore: 'Trust Score',
notifications: 'Notifications',
settings: 'Settings',
}, },
finance: { finance: {
wallet: 'Wallet', wallet: 'Wallet',
@@ -498,6 +513,12 @@ export default {
edit_title: 'Edit Vehicle', edit_title: 'Edit Vehicle',
add_title: 'Add New Vehicle', add_title: 'Add New Vehicle',
features_hint: 'Select the vehicle equipment and extra options.', features_hint: 'Select the vehicle equipment and extra options.',
// ── Vehicle Card ──
noPhoto: 'No photo uploaded',
nextService: 'Next Service',
noData: 'No data',
primaryVehicle: 'Primary vehicle',
setPrimary: 'Set as primary',
// Fuel types // Fuel types
fuelTypes: { fuelTypes: {
benzin: 'Petrol', benzin: 'Petrol',

View File

@@ -94,6 +94,21 @@ export default {
achievements: 'Eredmények', achievements: 'Eredmények',
detailedView: 'Részletes Nézet', detailedView: 'Részletes Nézet',
modalPlaceholder: 'A szekció tartalma hamarosan elérhető lesz.', modalPlaceholder: 'A szekció tartalma hamarosan elérhető lesz.',
// ── Dashboard Card 2: Costs & Quick Actions ──
costsTitle: 'Költségek & Gyorsműveletek',
vehicle: 'Jármű',
selectVehicle: 'Válassz járművet',
noVehicleSelected: 'Nincs kiválasztva jármű. Adj hozzá egy járművet először!',
recordFueling: 'Tankolás rögzítése',
parkingToll: 'Parkolás / Útdíj',
additionalCosts: 'További költségek',
findServicePartners: 'Keress megbízható szervizpartnereket',
allVehicles: 'Összes jármű',
// ── Dashboard Card 5: Profile & Trust ──
viewProfile: 'Profil megtekintése',
trustScore: 'Bizalmi pontszám',
notifications: 'Értesítések',
settings: 'Beállítások',
}, },
finance: { finance: {
wallet: 'Pénztárca', wallet: 'Pénztárca',
@@ -498,6 +513,12 @@ export default {
edit_title: 'Jármű szerkesztése', edit_title: 'Jármű szerkesztése',
add_title: 'Új jármű hozzáadása', add_title: 'Új jármű hozzáadása',
features_hint: 'Válaszd ki a jármű felszereltségét és extra opcióit.', features_hint: 'Válaszd ki a jármű felszereltségét és extra opcióit.',
// ── Vehicle Card ──
noPhoto: 'Nincs feltöltött fotó',
nextService: 'Következő Szerviz',
noData: 'Nincs adat',
primaryVehicle: 'Elsődleges jármű',
setPrimary: 'Beállítás elsődlegessé',
// Fuel types // Fuel types
fuelTypes: { fuelTypes: {
benzin: 'Benzin', benzin: 'Benzin',

View File

@@ -7,10 +7,7 @@
</template> </template>
<template #center> <template #center>
<!-- Center label: {firstName}_garage --> <!-- Center slot: kept empty for breathing space -->
<span class="text-sm font-semibold text-white/70 tracking-wide">
{{ centerLabel }}
</span>
</template> </template>
<template #right> <template #right>
@@ -117,7 +114,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue' import { ref, onMounted, onUnmounted } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { useAuthStore } from '../stores/auth' import { useAuthStore } from '../stores/auth'
@@ -135,14 +132,6 @@ const authStore = useAuthStore()
// ── Hamburger Menu State ────────────────────────────────────────── // ── Hamburger Menu State ──────────────────────────────────────────
const isHamburgerOpen = ref(false) const isHamburgerOpen = ref(false)
// ── Center label: shows "{firstName}_garage" ───────────────────────
const centerLabel = computed(() => {
const firstName = authStore.user?.first_name ||
authStore.user?.email?.split('@')[0] ||
t('header.user')
return t('header.personalGarageLabel', { name: firstName })
})
// ── Navigate to dashboard with card query param ──────────────────── // ── Navigate to dashboard with card query param ────────────────────
function navigateToCard(cardId: string) { function navigateToCard(cardId: string) {
isHamburgerOpen.value = false isHamburgerOpen.value = false

View File

@@ -89,20 +89,20 @@
</div> </div>
<!-- <!--
Card 2: 💰 Költségek & Gyorsműveletek (Action Center) Card 2: 💰 Costs & Quick Actions (Action Center)
--> -->
<div <div
class="bg-white/95 rounded-2xl shadow-[0_8px_30px_rgb(0,0,0,0.12)] overflow-hidden flex flex-col h-[350px] relative transition-all duration-300 ease-out transform hover:-translate-y-3 hover:scale-[1.02] hover:shadow-2xl" class="bg-white/95 rounded-2xl shadow-[0_8px_30px_rgb(0,0,0,0.12)] overflow-hidden flex flex-col h-[350px] relative transition-all duration-300 ease-out transform hover:-translate-y-3 hover:scale-[1.02] hover:shadow-2xl"
> >
<!-- Top header bar --> <!-- Top header bar -->
<div class="h-12 bg-slate-700 w-full shrink-0 flex items-center px-4 gap-2"> <div class="h-12 bg-slate-700 w-full shrink-0 flex items-center px-4 gap-2">
<span class="text-white font-bold text-sm tracking-wide">💰 Költségek & Gyorsműveletek</span> <span class="text-white font-bold text-sm tracking-wide">💰 {{ t('dashboard.costsTitle') }}</span>
</div> </div>
<!-- Content --> <!-- 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 overflow-hidden">
<!-- Vehicle Selector Dropdown --> <!-- Vehicle Selector Dropdown -->
<div class="mb-3"> <div class="mb-3">
<label class="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-1 block">Jármű</label> <label class="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-1 block">{{ t('dashboard.vehicle') }}</label>
<select <select
v-model="selectedActionVehicleId" v-model="selectedActionVehicleId"
class="w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm text-slate-800 focus:border-sf-accent focus:ring-2 focus:ring-sf-accent/20 outline-none transition-all" class="w-full rounded-xl border border-slate-300 bg-white px-3 py-2 text-sm text-slate-800 focus:border-sf-accent focus:ring-2 focus:ring-sf-accent/20 outline-none transition-all"
@@ -124,39 +124,39 @@
v-if="!selectedActionVehicle" v-if="!selectedActionVehicle"
class="flex items-center justify-center rounded-xl bg-amber-50 border border-amber-200 px-4 py-3 text-sm text-amber-700" class="flex items-center justify-center rounded-xl bg-amber-50 border border-amber-200 px-4 py-3 text-sm text-amber-700"
> >
<span> Nincs kiválasztva jármű. Adj hozzá egy járművet először!</span> <span> {{ t('dashboard.noVehicleSelected') }}</span>
</div> </div>
<!-- Tankolás (Big, prominent) --> <!-- Record Fueling (Big, prominent) -->
<button <button
v-if="selectedActionVehicle" v-if="selectedActionVehicle"
@click="openFuelModal" @click="openFuelModal"
class="flex items-center gap-3 rounded-xl bg-gradient-to-r from-sf-accent to-emerald-500 px-4 py-3 text-sm font-bold text-white shadow-md transition-all hover:shadow-lg hover:scale-[1.02] active:scale-[0.98]" class="flex items-center gap-3 rounded-xl bg-gradient-to-r from-sf-accent to-emerald-500 px-4 py-3 text-sm font-bold text-white shadow-md transition-all hover:shadow-lg hover:scale-[1.02] active:scale-[0.98]"
> >
<span class="flex h-9 w-9 items-center justify-center rounded-lg bg-white/20 text-lg"></span> <span class="flex h-9 w-9 items-center justify-center rounded-lg bg-white/20 text-lg"></span>
<span>Tankolás rögzítése</span> <span>{{ t('dashboard.recordFueling') }}</span>
<span class="ml-auto text-white/60 text-xs"></span> <span class="ml-auto text-white/60 text-xs"></span>
</button> </button>
<!-- 🅿 Parkolás / Útdíj (Medium) --> <!-- 🅿 Parking / Toll (Medium) -->
<button <button
v-if="selectedActionVehicle" v-if="selectedActionVehicle"
@click="openFeeModal" @click="openFeeModal"
class="flex items-center gap-3 rounded-xl border border-slate-200 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-700 transition-all hover:bg-slate-100 hover:shadow-md active:scale-[0.98]" class="flex items-center gap-3 rounded-xl border border-slate-200 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-700 transition-all hover:bg-slate-100 hover:shadow-md active:scale-[0.98]"
> >
<span class="flex h-8 w-8 items-center justify-center rounded-lg bg-slate-200 text-base">🅿</span> <span class="flex h-8 w-8 items-center justify-center rounded-lg bg-slate-200 text-base">🅿</span>
<span>Parkolás / Útdíj</span> <span>{{ t('dashboard.parkingToll') }}</span>
<span class="ml-auto text-slate-400 text-xs"></span> <span class="ml-auto text-slate-400 text-xs"></span>
</button> </button>
<!-- További költségek (Medium) --> <!-- Additional Costs (Medium) -->
<button <button
v-if="selectedActionVehicle" v-if="selectedActionVehicle"
@click="openComplexModal" @click="openComplexModal"
class="flex items-center gap-3 rounded-xl border border-slate-200 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-700 transition-all hover:bg-slate-100 hover:shadow-md active:scale-[0.98]" class="flex items-center gap-3 rounded-xl border border-slate-200 bg-slate-50 px-4 py-2.5 text-sm font-semibold text-slate-700 transition-all hover:bg-slate-100 hover:shadow-md active:scale-[0.98]"
> >
<span class="flex h-8 w-8 items-center justify-center rounded-lg bg-slate-200 text-base"></span> <span class="flex h-8 w-8 items-center justify-center rounded-lg bg-slate-200 text-base"></span>
<span>További költségek</span> <span>{{ t('dashboard.additionalCosts') }}</span>
<span class="ml-auto text-slate-400 text-xs"></span> <span class="ml-auto text-slate-400 text-xs"></span>
</button> </button>
</div> </div>