276 lines
11 KiB
Markdown
276 lines
11 KiB
Markdown
# P0 Végrehajtási Terv: Vehicle Components & Modal Javítások
|
|
|
|
**Státusz:** Tervezés alatt
|
|
**Dátum:** 2026-06-21
|
|
**Architect:** Rendszer-Architect
|
|
|
|
---
|
|
|
|
## 1. Áttekintés
|
|
|
|
A P0 feladat a meglévő jármű komponensek 4 problémás területének javítása az Architect modul elemzése alapján. A terv 3 fájl módosítását írja elő, új komponensek létrehozása NÉLKÜL.
|
|
|
|
### Érintett fájlok:
|
|
| # | Fájl | Művelet | Hatás |
|
|
|---|------|---------|-------|
|
|
| 1 | `frontend/src/types/vehicle.ts` | `image_url` mező hozzáadása a `VehicleData` interfészhez | Type safety |
|
|
| 2 | `frontend/src/components/vehicle/VehicleCardStandard.vue` | 5 adatkötés javítás | UI adatok helyes megjelenítése |
|
|
| 3 | `frontend/src/components/vehicle/VehicleDetailModal.vue` | 5. tab (TechData) integráció + Pénzügyek tab bővítés | Teljes TCO láthatóság |
|
|
|
|
---
|
|
|
|
## 2. Részletes Módosítási Terv
|
|
|
|
### 2.1 `VehicleData` típus bővítése
|
|
|
|
**Fájl:** [`frontend/src/types/vehicle.ts`](frontend/src/types/vehicle.ts:10)
|
|
|
|
```typescript
|
|
export interface VehicleData {
|
|
// ... existing fields ...
|
|
image_url?: string | null; // ÚJ mező a kép URL tárolására
|
|
}
|
|
```
|
|
|
|
**Indoklás:** A `VehicleCardStandard.vue` `:src="vehicle.image_url"`-t használna, de a `VehicleData` interfészből hiányzik ez a mező. A backend már visszaadhat `image_url`-t az `AssetResponse`-ben (lásd [`assets.py:386`](backend/app/api/v1/endpoints/assets.py:386)).
|
|
|
|
---
|
|
|
|
### 2.2 `VehicleCardStandard.vue` - 5 adatkötés javítás
|
|
|
|
**Fájl:** [`frontend/src/components/vehicle/VehicleCardStandard.vue`](frontend/src/components/vehicle/VehicleCardStandard.vue)
|
|
|
|
| # | Probléma | Javítás | Sor |
|
|
|---|----------|---------|-----|
|
|
| 1 | `is_primary` csillag gomb (megvan, de nincs szöveges badge) | **Nem változtatunk** - a meglévő star toggle működik, a feladat csak a data bindings-okról szól | 119-131 |
|
|
| 2 | `color` mező: `vehicle.individual_equipment?.color \|\| vehicle.color \|\| '—'` | Lecserélés erre: `vehicle.individual_equipment?.color \|\| 'N/A'` | 103 |
|
|
| 3 | `engine` mező: `vehicle.engine \|\| vehicle.fuel_type \|\| '—'` | Lecserélés erre: `` `${vehicle.power_kw || '?'} kW / ${vehicle.engine_capacity || '?'} cm³` `` | 95 |
|
|
| 4 | `country_code` mező: `vehicle.countryCode \|\| vehicle.country_code` | **Hozzáadás** `registration_country` használatával | 62 |
|
|
| 5 | `image` mező: `vehicle.image` | Lecserélés `vehicle.image_url`-re, fallback SVG placeholder | 9-10 |
|
|
|
|
**Részletes módosítások:**
|
|
|
|
**2.2.a - Image (9-10. sor):**
|
|
```html
|
|
<img v-if="vehicle.image_url" :src="vehicle.image_url" alt="" class="..." />
|
|
<template v-else>
|
|
<svg ... ></svg> <!-- Meglévő placeholder SVG változatlan -->
|
|
</template>
|
|
```
|
|
|
|
**2.2.b - Country (62. sor):**
|
|
```html
|
|
<span class="...">{{ vehicle.registration_country || vehicle.countryCode || vehicle.country_code || '—' }}</span>
|
|
```
|
|
|
|
**2.2.c - Engine (95. sor):**
|
|
```html
|
|
<span class="...">{{ vehicle.power_kw ? vehicle.power_kw + ' kW' : '?' }} / {{ vehicle.engine_capacity ? vehicle.engine_capacity + ' cm³' : '?' }}</span>
|
|
```
|
|
|
|
**2.2.d - Color (103. sor):**
|
|
```html
|
|
<span class="...">{{ vehicle.individual_equipment?.color || 'N/A' }}</span>
|
|
```
|
|
|
|
---
|
|
|
|
### 2.3 `VehicleDetailModal.vue` - TechData Tab integráció
|
|
|
|
**Fájl:** [`frontend/src/components/vehicle/VehicleDetailModal.vue`](frontend/src/components/vehicle/VehicleDetailModal.vue)
|
|
|
|
#### Háttér
|
|
A [`TechDataTab.vue`](frontend/src/components/vehicles/tabs/TechDataTab.vue) LÉTEZIK (279 sor), de **NINCS integrálva** a modálba. Jelenleg 4 tab van (`basics`, `finance`, `alerts`, `servicebook`).
|
|
|
|
#### Megoldás: 5. tab hozzáadása
|
|
|
|
**a) Tab gomb hozzáadása (a meglévő tab gombok után, ~866-873. sor):**
|
|
```html
|
|
<button @click="activeTab = 'techdata'" :class="activeTab === 'techdata' ? '...' : '...'">
|
|
{{ $t('vehicle.techData') || 'Műszaki adatok' }}
|
|
</button>
|
|
```
|
|
|
|
**b) Tab content blokk hozzáadása (a servicebook tab után, ~736. sor után):**
|
|
```html
|
|
<div v-if="activeTab === 'techdata'" class="space-y-5">
|
|
<TechDataTab />
|
|
</div>
|
|
```
|
|
|
|
**c) Import hozzáadása (script section):**
|
|
```typescript
|
|
import TechDataTab from '@/components/vehicles/tabs/TechDataTab.vue';
|
|
```
|
|
|
|
**d) `provide/inject` kompatibilitás biztosítása:**
|
|
A [`TechDataTab.vue`](frontend/src/components/vehicles/tabs/TechDataTab.vue:107) `inject<computed<Vehicle | null>>('vehicle')`-t használ. A `VehicleDetailModal.vue`-ban a `vehicle` computed property már létezik. A provide-ot a modal szülőjének (vagy a modal-nak magának) kell biztosítania.
|
|
|
|
**Megvizsgálandó:** A `VehicleDetailModal.vue` jelenleg `props`-on keresztül kapja a vehicle adatokat. Ha a `TechDataTab.vue` `inject('vehicle')`-t vár, akkor vagy:
|
|
- Biztosítani kell a `provide('vehicle', computed(...))`-t a modalban, VAGY
|
|
- Át kell alakítani a TechDataTab-et, hogy elfogadjon egy prop-ot is.
|
|
|
|
**Ajánlás:** Mivel a [`OverviewTab.vue`](frontend/src/components/vehicles/tabs/OverviewTab.vue) is ugyanezt az `inject`-es mintát használja, a legegyszerűbb megoldás a `provide` hozzáadása a `VehicleDetailModal.vue`-hoz:
|
|
|
|
```typescript
|
|
// A script setup blokkban
|
|
import { provide, computed } from 'vue';
|
|
|
|
const vehicle = computed(() => props.vehicle || null);
|
|
provide('vehicle', vehicle);
|
|
```
|
|
|
|
---
|
|
|
|
### 2.4 `VehicleDetailModal.vue` - Pénzügyek Tab bővítés
|
|
|
|
#### Jelenlegi állapot
|
|
A `finance` tab (282-436. sor) csak `GET /assets/{id}/costs`-t hív (944-957. sor) és két részből áll: non-premium esetén egyszerű lista, premium esetén chart.
|
|
|
|
#### Bővítés: 3 új API hívás párhuzamosan
|
|
|
|
**Backend endpointok (már léteznek, VERIFIKÁLVA):**
|
|
| Endpoint | Visszatérési típus | Helye |
|
|
|----------|-------------------|-------|
|
|
| `GET /api/v1/assets/vehicles/{id}/financials` | `AssetFinancialsResponse` | [`assets.py:1245`](backend/app/api/v1/endpoints/assets.py:1245) |
|
|
| `GET /api/v1/assets/vehicles/{id}/insurance` | `List[VehicleInsurancePolicyResponse]` | [`assets.py:1314`](backend/app/api/v1/endpoints/assets.py:1314) |
|
|
| `GET /api/v1/assets/vehicles/{id}/tax` | `List[VehicleTaxObligationResponse]` | [`assets.py:1376`](backend/app/api/v1/endpoints/assets.py:1376) |
|
|
|
|
**Store metódusok (már léteznek, VERIFIKÁLVA):**
|
|
| Metódus | Param | Visszatérés |
|
|
|---------|-------|-------------|
|
|
| `fetchAssetFinancials(assetId)` | `string` | `Promise<AssetFinancials \| null>` | [`vehicle.ts:318`](frontend/src/stores/vehicle.ts:318) |
|
|
| `fetchInsurancePolicies(assetId)` | `string` | `Promise<VehicleInsurancePolicy[]>` | [`vehicle.ts:346`](frontend/src/stores/vehicle.ts:346) |
|
|
| `fetchTaxObligations(assetId)` | `string` | `Promise<VehicleTaxObligation[]>` | [`vehicle.ts:388`](frontend/src/stores/vehicle.ts:388) |
|
|
|
|
#### Tervezett módosítások a `VehicleDetailModal.vue`-ban:
|
|
|
|
**a) Új state változók (script setup):**
|
|
```typescript
|
|
const assetFinancials = ref<AssetFinancials | null>(null);
|
|
const insurancePolicies = ref<VehicleInsurancePolicy[]>([]);
|
|
const taxObligations = ref<VehicleTaxObligation[]>([]);
|
|
const financeLoading = ref(false);
|
|
```
|
|
|
|
**b) Párhuzamos API hívás a `loadFinanceData` függvényben (~944. sor környékén):**
|
|
```typescript
|
|
async function loadFinanceData() {
|
|
financeLoading.value = true;
|
|
try {
|
|
const vehicleId = props.vehicle?.id?.toString();
|
|
if (!vehicleId) return;
|
|
|
|
// Promise.all a 3 új híváshoz
|
|
const [financials, policies, taxes] = await Promise.all([
|
|
vehicleStore.fetchAssetFinancials(vehicleId),
|
|
vehicleStore.fetchInsurancePolicies(vehicleId),
|
|
vehicleStore.fetchTaxObligations(vehicleId),
|
|
]);
|
|
|
|
assetFinancials.value = financials;
|
|
insurancePolicies.value = policies;
|
|
taxObligations.value = taxes;
|
|
|
|
// Meglévő costs betöltés változatlan
|
|
await loadCosts();
|
|
} catch (err) {
|
|
console.error('Failed to load finance data:', err);
|
|
} finally {
|
|
financeLoading.value = false;
|
|
}
|
|
}
|
|
```
|
|
|
|
**c) Template bővítés - új szekciók a meglévő "Költségek" szekció ELÉ:**
|
|
|
|
```html
|
|
<div v-if="activeTab === 'finance'" class="space-y-5">
|
|
|
|
<!-- 1. ÚJ: Beszerzés & Lízing szekció -->
|
|
<div v-if="assetFinancials" class="...">
|
|
<h3>{{ $t('vehicle.financials.title') }}</h3>
|
|
<div class="grid grid-cols-2 gap-3">
|
|
<div>...purchase_price...</div>
|
|
<div>...leasing...residual_value...</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 2. ÚJ: Biztosítások szekció -->
|
|
<div v-if="insurancePolicies.length" class="...">
|
|
<h3>{{ $t('vehicle.insurance.title') }}</h3>
|
|
<div v-for="policy in insurancePolicies" :key="policy.id">
|
|
{{ policy.insurance_type }} - {{ policy.premium_amount }} - {{ policy.expiry_date }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 3. ÚJ: Adók szekció -->
|
|
<div v-if="taxObligations.length" class="...">
|
|
<h3>{{ $t('vehicle.tax.title') }}</h3>
|
|
<div v-for="tax in taxObligations" :key="tax.id">
|
|
{{ tax.tax_type }} - {{ tax.amount }} - {{ tax.payment_status }}
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 4. MEGLÉVŐ: Költségek szekció (változatlan) -->
|
|
<div v-if="!costsLoading">
|
|
<!-- ... existing costs content ... -->
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
**d) Importok hozzáadása:**
|
|
```typescript
|
|
import type { AssetFinancials, VehicleInsurancePolicy, VehicleTaxObligation } from '@/types/vehicle';
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Függőségi Térkép
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[vehicle.ts - VehicleData] -->|image_url mező| B[VehicleCardStandard.vue]
|
|
A -->|Vehicle típus| C[VehicleDetailModal.vue]
|
|
C -->|provide vehicle| D[TechDataTab.vue]
|
|
C -->|Promise.all| E[vehicleStore: fetchAssetFinancials]
|
|
C -->|Promise.all| F[vehicleStore: fetchInsurancePolicies]
|
|
C -->|Promise.all| G[vehicleStore: fetchTaxObligations]
|
|
E --> H[assets.py: GET /vehicles/{id}/financials]
|
|
F --> I[assets.py: GET /vehicles/{id}/insurance]
|
|
G --> J[assets.py: GET /vehicles/{id}/tax]
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Végrehajtási Sorrend
|
|
|
|
| Lépés | Fájl | Művelet | Függőség |
|
|
|-------|------|---------|----------|
|
|
| 1 | `types/vehicle.ts` | `image_url` hozzáadása | Nincs |
|
|
| 2 | `VehicleCardStandard.vue` | 5 data binding javítás | 1. lépés (`image_url`) |
|
|
| 3 | `VehicleDetailModal.vue` | TechData tab + provide hozzáadás | Nincs |
|
|
| 4 | `VehicleDetailModal.vue` | Finance tab bővítés (Promise.all) | Nincs |
|
|
| 5 | - | Vite build futtatás | 1-4. lépés |
|
|
|
|
---
|
|
|
|
## 5. Kockázatok és Megjegyzések
|
|
|
|
1. **TechDataTab inject függőség:** Ha a `provide` nem működik a modalban (pl. scope issue), alternatív megoldás a `TechDataTab.vue` átírása prop-alapúra. Ez azonban többletkockázat, ezért először a `provide`-os megoldást próbáljuk.
|
|
|
|
2. **`FinancialsTab.vue` átvételének elvetése:** A `FinancialsTab.vue` (709 sor) már tartalmazza a 3 API hívást, de sötét témát (bg-slate-900) használ, ami nem kompatibilis a modal világos témájával. A kód lemásolása helyett a logikát közvetlenül a `VehicleDetailModal.vue`-ba építjük be.
|
|
|
|
3. **Nincs Compact variáns:** A keresés nem talált Compact változatot, így csak a Standard kártyát kell javítani.
|
|
|
|
4. **i18n kulcsok:** A `hu.ts` és `en.ts` fájlokba új fordítási kulcsokat kell felvenni (pl. `vehicle.financials.title`, `vehicle.insurance.title`, `vehicle.tax.title`, `vehicle.techData`).
|
|
|
|
---
|
|
|
|
## 6. Jóváhagyás
|
|
|
|
**Architect:** ✅ Terv elkészítve
|
|
**Fejlesztő:** ⏳ Jóváhagyásra vár
|
|
|
|
A terv végrehajtásához **Code módba** kell váltani.
|