admin frontend elkezdése, járművek tisztázása, frontend fejleszts
This commit is contained in:
@@ -119,8 +119,17 @@
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<div class="flex-1 flex flex-col gap-2.5 justify-center">
|
||||
<!-- No vehicle warning -->
|
||||
<div
|
||||
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"
|
||||
>
|
||||
<span>⚠️ Nincs kiválasztva jármű. Adj hozzá egy járművet először!</span>
|
||||
</div>
|
||||
|
||||
<!-- ⛽ Tankolás (Big, prominent) -->
|
||||
<button
|
||||
v-if="selectedActionVehicle"
|
||||
@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]"
|
||||
>
|
||||
@@ -131,6 +140,7 @@
|
||||
|
||||
<!-- 🅿️ Parkolás / Útdíj (Medium) -->
|
||||
<button
|
||||
v-if="selectedActionVehicle"
|
||||
@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]"
|
||||
>
|
||||
@@ -141,6 +151,7 @@
|
||||
|
||||
<!-- ➕ További költségek (Medium) -->
|
||||
<button
|
||||
v-if="selectedActionVehicle"
|
||||
@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]"
|
||||
>
|
||||
@@ -318,8 +329,8 @@
|
||||
<PrivateVehicleManager
|
||||
v-if="activeCard === 'vehicles'"
|
||||
:target-vehicle-id="selectedTargetId"
|
||||
:edit-target-vehicle="editTargetVehicle"
|
||||
@clear-edit-target="editTargetVehicle = null"
|
||||
@add-new="handleAddNew"
|
||||
@edit-vehicle="handleEditVehicle"
|
||||
/>
|
||||
|
||||
<!-- ── Fallback for other cards ── -->
|
||||
@@ -370,23 +381,49 @@
|
||||
@edit="handleEditFromDetail"
|
||||
@set-primary="handleSetPrimaryFromDetail"
|
||||
/>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════════════
|
||||
Vehicle Form Modal (Add) — standalone, decoupled from Manager
|
||||
═══════════════════════════════════════════════════════════════════ -->
|
||||
<VehicleFormModal
|
||||
:is-open="isAddFormOpen"
|
||||
@close="isAddFormOpen = false"
|
||||
@saved="onAddVehicleSaved"
|
||||
/>
|
||||
|
||||
<!-- ═══════════════════════════════════════════════════════════════════
|
||||
Vehicle Form Modal (Edit) — standalone, decoupled from Manager
|
||||
═══════════════════════════════════════════════════════════════════ -->
|
||||
<VehicleFormModal
|
||||
:is-open="isEditFormOpen"
|
||||
:vehicle="editTargetVehicle"
|
||||
@close="closeEditForm"
|
||||
@saved="onEditVehicleSaved"
|
||||
@deleted="onVehicleDeleted"
|
||||
/>
|
||||
</div><!-- end min-h-screen -->
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, watch } from 'vue'
|
||||
import { ref, computed, onMounted, watch, nextTick } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import { useVehicleStore } from '../stores/vehicle'
|
||||
import type { VehicleData } from '../types/vehicle'
|
||||
import api from '../api/axios'
|
||||
import PrivateVehicleManager from '../components/dashboard/PrivateVehicleManager.vue'
|
||||
import VehicleCardCompact from '../components/vehicle/VehicleCardCompact.vue'
|
||||
import VehicleDetailModal from '../components/vehicle/VehicleDetailModal.vue'
|
||||
import VehicleFormModal from '../components/dashboard/VehicleFormModal.vue'
|
||||
import SimpleFuelModal from '../components/dashboard/SimpleFuelModal.vue'
|
||||
import ComplexExpenseModal from '../components/dashboard/ComplexExpenseModal.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const vehicleStore = useVehicleStore()
|
||||
|
||||
@@ -405,9 +442,6 @@ const openCard = (cardId: string, targetId: string | null = null) => {
|
||||
const isVehicleDetailOpen = ref(false)
|
||||
const detailVehicle = ref<VehicleData | null>(null)
|
||||
|
||||
// ── Task 3: Edit target for direct VehicleFormModal open ──
|
||||
const editTargetVehicle = ref<VehicleData | null>(null)
|
||||
|
||||
function openVehicleDetail(vehicle: VehicleData) {
|
||||
detailVehicle.value = vehicle
|
||||
isVehicleDetailOpen.value = true
|
||||
@@ -418,11 +452,115 @@ function closeVehicleDetail() {
|
||||
detailVehicle.value = null
|
||||
}
|
||||
|
||||
// ── Vehicle Form Modal State (standalone, decoupled from Manager) ──
|
||||
const isAddFormOpen = ref(false)
|
||||
const isEditFormOpen = ref(false)
|
||||
const editTargetVehicle = ref<VehicleData | null>(null)
|
||||
const isCheckingQuota = ref(false)
|
||||
|
||||
/**
|
||||
* Return-State flag: when true, closing the edit form should re-open
|
||||
* the VehicleDetailModal instead of returning to the dashboard.
|
||||
*/
|
||||
const returnToVehicleDetail = ref(false)
|
||||
|
||||
/**
|
||||
* Preemptive quota check before opening the add-vehicle modal.
|
||||
* Calls GET /api/v1/assets/vehicles/quota-status.
|
||||
* If the user has reached their limit, shows a blocking toast/alert instead of opening the form.
|
||||
*/
|
||||
async function handleAddNew() {
|
||||
if (isCheckingQuota.value) return
|
||||
isCheckingQuota.value = true
|
||||
|
||||
try {
|
||||
const res = await api.get('/assets/vehicles/quota-status')
|
||||
const { can_add, current_count, limit } = res.data
|
||||
|
||||
if (!can_add) {
|
||||
alert(
|
||||
`Elérted a csomagodhoz tartozó járműlimitet (${current_count}/${limit})! ` +
|
||||
'Kérjük, válts magasabb csomagra az új jármű rögzítéséhez.'
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
isAddFormOpen.value = true
|
||||
} catch (err: any) {
|
||||
console.error('[DashboardView] Quota check failed:', err)
|
||||
isAddFormOpen.value = true
|
||||
} finally {
|
||||
isCheckingQuota.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function onAddVehicleSaved() {
|
||||
isAddFormOpen.value = false
|
||||
}
|
||||
|
||||
function handleEditVehicle(vehicle: VehicleData) {
|
||||
editTargetVehicle.value = vehicle ? { ...vehicle } : null
|
||||
isEditFormOpen.value = true
|
||||
}
|
||||
|
||||
function closeEditForm() {
|
||||
isEditFormOpen.value = false
|
||||
// If we came from the detail modal, return to it
|
||||
if (returnToVehicleDetail.value) {
|
||||
returnToVehicleDetail.value = false
|
||||
const vehicle = editTargetVehicle.value
|
||||
editTargetVehicle.value = null
|
||||
if (vehicle) {
|
||||
nextTick(() => {
|
||||
detailVehicle.value = vehicle
|
||||
isVehicleDetailOpen.value = true
|
||||
})
|
||||
}
|
||||
} else {
|
||||
editTargetVehicle.value = null
|
||||
}
|
||||
}
|
||||
|
||||
function onEditVehicleSaved(savedVehicle?: VehicleData) {
|
||||
isEditFormOpen.value = false
|
||||
// If we came from the detail modal, return to it
|
||||
if (returnToVehicleDetail.value) {
|
||||
returnToVehicleDetail.value = false
|
||||
// Use the saved vehicle (with updated data) if available, otherwise fallback
|
||||
const vehicle = savedVehicle || editTargetVehicle.value
|
||||
editTargetVehicle.value = null
|
||||
if (vehicle) {
|
||||
nextTick(() => {
|
||||
detailVehicle.value = vehicle
|
||||
isVehicleDetailOpen.value = true
|
||||
})
|
||||
}
|
||||
} else {
|
||||
editTargetVehicle.value = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the edit VehicleFormModal emits 'deleted'.
|
||||
* Completely resets the return-state and closes the edit form,
|
||||
* then refreshes the vehicle list so the deleted vehicle disappears.
|
||||
*/
|
||||
function onVehicleDeleted() {
|
||||
returnToVehicleDetail.value = false
|
||||
isEditFormOpen.value = false
|
||||
editTargetVehicle.value = null
|
||||
vehicleStore.fetchVehicles()
|
||||
}
|
||||
|
||||
// ── Bridge: Detail → Edit (opens edit modal directly, no Manager card) ──
|
||||
function handleEditFromDetail(vehicle: VehicleData) {
|
||||
returnToVehicleDetail.value = true
|
||||
closeVehicleDetail()
|
||||
// Task 3: Open the vehicles card AND pass the vehicle to edit directly
|
||||
editTargetVehicle.value = vehicle
|
||||
activeCard.value = 'vehicles'
|
||||
// Use nextTick so detail modal closes before edit opens
|
||||
nextTick(() => {
|
||||
editTargetVehicle.value = vehicle ? { ...vehicle } : null
|
||||
isEditFormOpen.value = true
|
||||
})
|
||||
}
|
||||
|
||||
function handleSetPrimaryFromDetail(vehicle: VehicleData) {
|
||||
@@ -497,12 +635,28 @@ const badges = ref([
|
||||
onMounted(() => {
|
||||
vehicleStore.fetchVehicles()
|
||||
authStore.fetchMyOrganizations()
|
||||
|
||||
// ── Handle query param card navigation ──────────────────────────
|
||||
const cardParam = route.query.card as string | undefined
|
||||
if (cardParam) {
|
||||
openCard(cardParam)
|
||||
// Clean up the query param so it doesn't re-trigger
|
||||
router.replace({ query: {} })
|
||||
}
|
||||
})
|
||||
|
||||
// Watch for vehicles to load, then auto-select
|
||||
watch(() => vehicleStore.vehicles.length, () => {
|
||||
autoSelectVehicle()
|
||||
}, { immediate: true })
|
||||
|
||||
// ── Watch for query param changes (hamburger menu navigation) ────
|
||||
watch(() => route.query.card, (newCard) => {
|
||||
if (newCard && typeof newCard === 'string') {
|
||||
openCard(newCard)
|
||||
router.replace({ query: {} })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user