admin felület különválasztva

This commit is contained in:
Roo
2026-06-24 11:29:45 +00:00
parent 71ef33bb85
commit 80a5d67f79
462 changed files with 87873 additions and 312 deletions

View File

@@ -0,0 +1,453 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import api from '../api/axios'
import type { AssetFinancials, VehicleInsurancePolicy, VehicleTaxObligation } from '../types/vehicle'
/**
* Vehicle data shape returned by GET /assets/vehicles
* Based on the AssetResponse Pydantic schema from the backend.
*/
export interface Vehicle {
id: string
vin: string | null
license_plate: string | null
name: string | null
catalog_id: number | null
// Classification
vehicle_class: string | null
brand: string | null
model: string | null
trim_level: string | null
// Technical specs
fuel_type: string | null
engine_capacity: number | null
power_kw: number | null
torque_nm: number | null
cylinder_layout: string | null
transmission_type: string | null
drive_type: string | null
euro_classification: string | null
curb_weight: number | null
max_weight: number | null
door_count: number | null
seat_count: number | null
// Status
current_mileage: number
condition_score: number
status: string
is_verified: boolean
// Timeline
year_of_manufacture: number | null
created_at: string
updated_at: string | null
// Warranty
is_under_warranty: boolean
warranty_expiry_date: string | null
// Registration Documents
registration_certificate_number?: string | null
registration_certificate_validity?: string | null
vehicle_registration_document_number?: string | null
// ── New Admin Fields ────────────────────────────────────────────────
registration_country?: string | null
first_domestic_registration_date?: string | null
import_country?: string | null
title_document_number?: string | null
engine_number?: string | null
number_of_previous_owners?: number | null
// Sales
is_for_sale: boolean
price: number | null
currency: string
// Primary vehicle flag (from backend individual_equipment JSONB)
is_primary: boolean
// Organization association (from backend AssetResponse)
current_organization_id: number | null
owner_organization_id: number | null
// ── Finance / Leasing ───────────────────────────────────────────────
asset_financials?: AssetFinancials | null
insurance_policies?: VehicleInsurancePolicy[] | null
tax_obligations?: VehicleTaxObligation[] | null
// JSONB individual_equipment from backend (car_specs, ev_specs, dates, etc.)
individual_equipment?: {
car_specs?: {
body_type?: string
ac_type?: string
cylinder_layout?: string
features?: string[]
}
ev_specs?: {
battery_capacity_kwh?: number
battery_soh?: number
ac_connector?: string
ac_power_kw?: number
dc_connector?: string
dc_power_kw?: number
range_wltp?: number
range_highway?: number
range_winter?: number
fast_charge_support?: boolean
green_plate_eligible?: boolean
}
dates?: {
mot_expiry?: string
document_expiry?: string
}
[key: string]: any
}
}
export const useVehicleStore = defineStore('vehicle', () => {
// ── State ──────────────────────────────────────────────────────────
const vehicles = ref<Vehicle[]>([])
const isLoading = ref(false)
const error = ref<string | null>(null)
// ── Getters ────────────────────────────────────────────────────────
/**
* Sorted vehicles: primary/favorite vehicles first, then alphabetical by brand/model.
* Used across all vehicle list displays (dashboard card, manager, etc.).
*/
const sortedVehicles = computed<Vehicle[]>(() => {
return [...vehicles.value].sort((a, b) => {
// Primary vehicles first
if (a.is_primary && !b.is_primary) return -1
if (!a.is_primary && b.is_primary) return 1
// Then alphabetical by brand + model
const nameA = `${a.brand || ''} ${a.model || ''} ${a.license_plate || ''}`.trim().toLowerCase()
const nameB = `${b.brand || ''} ${b.model || ''} ${b.license_plate || ''}`.trim().toLowerCase()
return nameA.localeCompare(nameB)
})
})
// ── Actions ────────────────────────────────────────────────────────
/**
* Fetch all vehicles/assets for the current user or their organization.
* GET /assets/vehicles
*/
async function fetchVehicles() {
isLoading.value = true
error.value = null
try {
const res = await api.get('/assets/vehicles')
vehicles.value = res.data as Vehicle[]
} catch (err: any) {
const message =
err.response?.data?.detail ||
err.response?.data?.message ||
'Nem sikerült betölteni a járműveket. Ellenőrizd a kapcsolatot.'
error.value = message
console.error('[VehicleStore] fetchVehicles error:', err)
} finally {
isLoading.value = false
}
}
/**
* Fetch a single vehicle by its ID.
* GET /assets/vehicles/{id}
* Used to refresh vehicle data after odometer updates or other mutations.
*/
async function fetchVehicleById(id: string): Promise<Vehicle | null> {
try {
const res = await api.get(`/assets/vehicles/${id}`)
const updated = res.data as Vehicle
// Update the vehicle in local state
const index = vehicles.value.findIndex(v => v.id === id)
if (index !== -1) {
vehicles.value[index] = updated
}
return updated
} catch (err: any) {
console.error('[VehicleStore] fetchVehicleById error:', err)
return null
}
}
/**
* Create a new vehicle via POST /assets/vehicles.
* On success, adds the new vehicle to the local state and refreshes the list.
*/
async function createVehicle(vehicleData: Record<string, any>): Promise<Vehicle | null> {
isLoading.value = true
error.value = null
try {
const res = await api.post('/assets/vehicles', vehicleData)
const newVehicle = res.data as Vehicle
// Add the new vehicle to local state
vehicles.value.unshift(newVehicle)
return newVehicle
} catch (err: any) {
// Log full error response for debugging (422 Validation Error details, etc.)
if (err.response) {
console.error('[VehicleStore] createVehicle error response:', {
status: err.response.status,
data: err.response.data,
headers: err.response.headers,
})
} else {
console.error('[VehicleStore] createVehicle network error:', err.message)
}
const detail = err.response?.data?.detail
const message = Array.isArray(detail)
? detail.map((d: any) => `${d.loc?.join('.')}: ${d.msg}`).join('; ')
: detail || err.response?.data?.message || 'Nem sikerült létrehozni a járművet.'
error.value = message
throw err
} finally {
isLoading.value = false
}
}
/**
* Update an existing vehicle via PUT /assets/vehicles/{id}.
* On success, updates the vehicle in the local state.
*/
async function updateVehicle(id: string, vehicleData: Record<string, any>): Promise<Vehicle | null> {
isLoading.value = true
error.value = null
try {
const res = await api.put(`/assets/vehicles/${id}`, vehicleData)
const updatedVehicle = res.data as Vehicle
// Update the vehicle in local state
const index = vehicles.value.findIndex(v => v.id === id)
if (index !== -1) {
vehicles.value[index] = updatedVehicle
}
return updatedVehicle
} catch (err: any) {
// Log full error response for debugging (422 Validation Error details, etc.)
if (err.response) {
console.error('[VehicleStore] updateVehicle error response:', {
status: err.response.status,
data: err.response.data,
headers: err.response.headers,
})
} else {
console.error('[VehicleStore] updateVehicle network error:', err.message)
}
const detail = err.response?.data?.detail
const message = Array.isArray(detail)
? detail.map((d: any) => `${d.loc?.join('.')}: ${d.msg}`).join('; ')
: detail || err.response?.data?.message || 'Nem sikerült frissíteni a járművet.'
error.value = message
throw err
} finally {
isLoading.value = false
}
}
/**
* Optimistically set a vehicle as the primary vehicle.
* Unsets any other primary vehicle in the local state.
*/
function setPrimaryVehicle(vehicleId: string | number) {
vehicles.value = vehicles.value.map(v => ({
...v,
is_primary: v.id === vehicleId,
}))
console.log('[VehicleStore] setPrimaryVehicle:', vehicleId)
}
/**
* Archive (soft delete) a vehicle via POST /assets/vehicles/{id}/archive.
* On success, removes the vehicle from the local state.
*/
async function archiveVehicle(id: string, finalMileage: number, archiveReason: string): Promise<boolean> {
isLoading.value = true
error.value = null
try {
await api.post(`/assets/vehicles/${id}/archive`, {
final_mileage: finalMileage,
archive_reason: archiveReason,
})
// Remove the vehicle from local state
vehicles.value = vehicles.value.filter(v => v.id !== id)
return true
} catch (err: any) {
if (err.response) {
console.error('[VehicleStore] archiveVehicle error response:', {
status: err.response.status,
data: err.response.data,
})
} else {
console.error('[VehicleStore] archiveVehicle network error:', err.message)
}
const detail = err.response?.data?.detail
const message = Array.isArray(detail)
? detail.map((d: any) => `${d.loc?.join('.')}: ${d.msg}`).join('; ')
: detail || err.response?.data?.message || 'Nem sikerült eltávolítani a járművet.'
error.value = message
throw err
} finally {
isLoading.value = false
}
}
// ── Finance Query Methods ──────────────────────────────────────────
/**
* Fetch asset financials for a vehicle.
* GET /assets/vehicles/{id}/financials
*/
async function fetchAssetFinancials(assetId: string): Promise<AssetFinancials | null> {
try {
const res = await api.get(`/assets/vehicles/${assetId}/financials`)
return res.data as AssetFinancials
} catch (err: any) {
console.error('[VehicleStore] fetchAssetFinancials error:', err)
return null
}
}
/**
* Save asset financials for a vehicle (create or update).
* PUT /assets/vehicles/{id}/financials
*/
async function saveAssetFinancials(assetId: string, data: Partial<AssetFinancials>): Promise<AssetFinancials | null> {
try {
const res = await api.put(`/assets/vehicles/${assetId}/financials`, data)
return res.data as AssetFinancials
} catch (err: any) {
console.error('[VehicleStore] saveAssetFinancials error:', err)
throw err
}
}
/**
* Fetch insurance policies for a vehicle.
* GET /assets/vehicles/{id}/insurance
*/
async function fetchInsurancePolicies(assetId: string): Promise<VehicleInsurancePolicy[]> {
try {
const res = await api.get(`/assets/vehicles/${assetId}/insurance`)
return res.data as VehicleInsurancePolicy[]
} catch (err: any) {
console.error('[VehicleStore] fetchInsurancePolicies error:', err)
return []
}
}
/**
* Create a new insurance policy for a vehicle.
* POST /assets/vehicles/{id}/insurance
*/
async function createInsurancePolicy(assetId: string, data: Partial<VehicleInsurancePolicy>): Promise<VehicleInsurancePolicy | null> {
try {
const res = await api.post(`/assets/vehicles/${assetId}/insurance`, data)
return res.data as VehicleInsurancePolicy
} catch (err: any) {
console.error('[VehicleStore] createInsurancePolicy error:', err)
throw err
}
}
/**
* Delete an insurance policy.
* DELETE /assets/vehicles/{assetId}/insurance/{policyId}
*/
async function deleteInsurancePolicy(assetId: string, policyId: number): Promise<boolean> {
try {
await api.delete(`/assets/vehicles/${assetId}/insurance/${policyId}`)
return true
} catch (err: any) {
console.error('[VehicleStore] deleteInsurancePolicy error:', err)
return false
}
}
/**
* Fetch tax obligations for a vehicle.
* GET /assets/vehicles/{id}/taxes
*/
async function fetchTaxObligations(assetId: string): Promise<VehicleTaxObligation[]> {
try {
const res = await api.get(`/assets/vehicles/${assetId}/taxes`)
return res.data as VehicleTaxObligation[]
} catch (err: any) {
console.error('[VehicleStore] fetchTaxObligations error:', err)
return []
}
}
/**
* Create a new tax obligation for a vehicle.
* POST /assets/vehicles/{id}/taxes
*/
async function createTaxObligation(assetId: string, data: Partial<VehicleTaxObligation>): Promise<VehicleTaxObligation | null> {
try {
const res = await api.post(`/assets/vehicles/${assetId}/taxes`, data)
return res.data as VehicleTaxObligation
} catch (err: any) {
console.error('[VehicleStore] createTaxObligation error:', err)
throw err
}
}
/**
* Delete a tax obligation.
* DELETE /assets/vehicles/{assetId}/taxes/{taxId}
*/
async function deleteTaxObligation(assetId: string, taxId: number): Promise<boolean> {
try {
await api.delete(`/assets/vehicles/${assetId}/taxes/${taxId}`)
return true
} catch (err: any) {
console.error('[VehicleStore] deleteTaxObligation error:', err)
return false
}
}
return {
// State
vehicles,
isLoading,
error,
// Getters
sortedVehicles,
// Actions
fetchVehicles,
fetchVehicleById,
createVehicle,
updateVehicle,
setPrimaryVehicle,
archiveVehicle,
// Finance Actions
fetchAssetFinancials,
saveAssetFinancials,
fetchInsurancePolicies,
createInsurancePolicy,
deleteInsurancePolicy,
fetchTaxObligations,
createTaxObligation,
deleteTaxObligation,
}
})