frontend költség beállítások

This commit is contained in:
Roo
2026-06-23 21:11:21 +00:00
parent 5b437b220d
commit 71ef33bb85
90 changed files with 11850 additions and 1053 deletions

View File

@@ -0,0 +1,264 @@
# 📐 Logic Spec — Vehicle Registration Documents
## Modul célja és Masterbook 2 illeszkedés
**Cél:** A forgalmi engedély száma, érvényességi ideje és a törzskönyv száma mezők hiányának pótlása a teljes adatfolyamban (adatbázis → API szint → Frontend UI).
**Masterbook 2 illeszkedés:** A Thick Asset Digital Twin filozófiába illeszkedik — minden adminisztratív adatot közvetlenül az Asset modellben tárolunk, nem JSONB-ben.
---
## Adatmodell
### Új mezők az Asset modellben
| Mező | Típus | SQL típus | Kötelező | Alapértelmezett |
|------|-------|-----------|----------|-----------------|
| `registration_certificate_number` | `Optional[str]` | `VARCHAR(50)` | Nem | `None` |
| `registration_certificate_validity` | `Optional[datetime]` | `DateTime(timezone=True)` | Nem | `None` |
| `vehicle_registration_document_number` | `Optional[str]` | `VARCHAR(50)` | Nem | `None` |
### Miért nem JSONB?
Ezek strukturált, kereshető admin adatok, amelyek:
- Külön lekérdezhetők (WHERE registration_certificate_number = '...')
- Indexelhetők
- Érvényesíthetők (pl. string hossz, dátum formátum)
- Nincsenek elrejtve a szabad formátumú JSONB-ben
---
## 1. Lépés: Adatbázis modell bővítése
**Fájl:** `backend/app/models/vehicle/asset.py`
### Módosítás
Az `Asset` osztályhoz (a warranty mezők után, a `notes` előtt) hozzáadandó:
```python
# === REGISTRATION DOCUMENTS ===
registration_certificate_number: Mapped[Optional[str]] = mapped_column(
String(50), nullable=True, default=None,
comment="Forgalmi engedély száma"
)
registration_certificate_validity: Mapped[Optional[datetime]] = mapped_column(
DateTime(timezone=True), nullable=True, default=None,
comment="Forgalmi engedély érvényességi ideje"
)
vehicle_registration_document_number: Mapped[Optional[str]] = mapped_column(
String(50), nullable=True, default=None,
comment="Törzskönyv száma"
)
```
### Sync
```bash
docker exec -it sf_api python -m app.scripts.sync_engine
```
---
## 2. Lépés: Pydantic sémák bővítése
**Fájl:** `backend/app/schemas/asset.py`
### AssetResponse (32. sor után)
```python
# === REGISTRATION DOCUMENTS ===
registration_certificate_number: Optional[str] = Field(None, max_length=50, description="Forgalmi engedély száma")
registration_certificate_validity: Optional[datetime] = Field(None, description="Forgalmi engedély érvényességi ideje")
vehicle_registration_document_number: Optional[str] = Field(None, max_length=50, description="Törzskönyv száma")
```
### AssetCreate (a warranty után, 233. sor környékén)
```python
# === REGISTRATION DOCUMENTS ===
registration_certificate_number: Optional[str] = Field(None, max_length=50, description="Forgalmi engedély száma")
registration_certificate_validity: Optional[datetime] = Field(None, description="Forgalmi engedély érvényességi ideje")
vehicle_registration_document_number: Optional[str] = Field(None, max_length=50, description="Törzskönyv száma")
```
### AssetUpdate (a warranty után, 375. sor környékén)
```python
# === REGISTRATION DOCUMENTS ===
registration_certificate_number: Optional[str] = Field(None, max_length=50, description="Forgalmi engedély száma")
registration_certificate_validity: Optional[datetime] = Field(None, description="Forgalmi engedély érvényességi ideje")
vehicle_registration_document_number: Optional[str] = Field(None, max_length=50, description="Törzskönyv száma")
```
---
## 3. Lépés: Asset Service bővítése
**Fájl:** `backend/app/services/asset_service.py`
### Módosítás a create_or_claim_vehicle() metódusban
A 219-es sor előtt (a `first_registration_date` után) az `asset_fields` dict-be:
```python
# Registration Documents
'registration_certificate_number': asset_data.registration_certificate_number,
'registration_certificate_validity': asset_data.registration_certificate_validity,
'vehicle_registration_document_number': asset_data.vehicle_registration_document_number,
```
---
## 4. Lépés: Frontend — Vehicle Store bővítése
**Fájl:** `frontend/src/stores/vehicle.ts`
### Módosítás a Vehicle interfészben (a 88-89. sor környékén)
```typescript
// Registration Documents
registration_certificate_number?: string | null
registration_certificate_validity?: string | null
vehicle_registration_document_number?: string | null
```
---
## 5. Lépés: Frontend — VehicleFormModal bővítése
**Fájl:** `frontend/src/components/dashboard/VehicleFormModal.vue`
### 5a. Form state bővítése (a 1779-es sor után)
```typescript
// Registration Documents
registration_certificate_number: '',
registration_certificate_validity: '',
vehicle_registration_document_number: '',
```
### 5b. Admin Tab UI bővítése (a 1006-os sor előtt, a warranty szekció után)
```html
<!-- ════════════════════════════════════════════════════════════ -->
<!-- REGISTRATION DOCUMENTS SECTION -->
<!-- ════════════════════════════════════════════════════════════ -->
<div class="border-t border-slate-200 pt-4">
<h4 class="text-sm font-semibold text-slate-700 mb-3">{{ t('vehicle.registration_documents_title') || 'Forgalmi engedély és Törzskönyv' }}</h4>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div>
<label class="mb-1.5 block text-sm font-medium text-slate-700">{{ t('vehicle.label_registration_certificate_number') || 'Forgalmi engedély száma' }}</label>
<input
v-model="form.registration_certificate_number"
type="text"
class="w-full rounded-xl border border-slate-300 bg-white px-4 py-2.5 text-sm text-slate-800 placeholder-slate-400 focus:border-sf-accent focus:outline-none focus:ring-2 focus:ring-sf-accent/20"
:placeholder="t('vehicle.placeholder_registration_certificate_number') || 'Pl. AB-123456'"
/>
</div>
<div>
<label class="mb-1.5 block text-sm font-medium text-slate-700">{{ t('vehicle.label_registration_certificate_validity') || 'Forgalmi engedély érvényessége' }}</label>
<input
v-model="form.registration_certificate_validity"
type="date"
class="w-full rounded-xl border border-slate-300 bg-white px-4 py-2.5 text-sm text-slate-800 focus:border-sf-accent focus:outline-none focus:ring-2 focus:ring-sf-accent/20"
/>
</div>
</div>
<div class="mt-3">
<label class="mb-1.5 block text-sm font-medium text-slate-700">{{ t('vehicle.label_vehicle_registration_document_number') || 'Törzskönyv száma' }}</label>
<input
v-model="form.vehicle_registration_document_number"
type="text"
class="w-full rounded-xl border border-slate-300 bg-white px-4 py-2.5 text-sm text-slate-800 placeholder-slate-400 focus:border-sf-accent focus:outline-none focus:ring-2 focus:ring-sf-accent/20"
:placeholder="t('vehicle.placeholder_vehicle_registration_document_number') || 'Pl. 1234567890'"
/>
</div>
</div>
```
### 5c. handleSave payload bővítése (a 2159-es sor után)
```typescript
// Registration Documents
registration_certificate_number: form.registration_certificate_number || null,
registration_certificate_validity: form.registration_certificate_validity || null,
vehicle_registration_document_number: form.vehicle_registration_document_number || null,
```
### 5d. populateForm bővítése (a 2065-ös sor után)
```typescript
// Registration Documents
form.registration_certificate_number = vehicle.registration_certificate_number || ''
form.registration_certificate_validity = vehicle.registration_certificate_validity || ''
form.vehicle_registration_document_number = vehicle.vehicle_registration_document_number || ''
```
### 5e. resetForm bővítése (kb. 1987-2026 sorok között)
```typescript
form.registration_certificate_number = ''
form.registration_certificate_validity = ''
form.vehicle_registration_document_number = ''
```
---
## 6. Lépés: Frontend i18n fordítások
**Fájl:** `frontend/src/i18n/hu.ts`
```typescript
label_registration_certificate_number: 'Forgalmi engedély száma',
label_registration_certificate_validity: 'Forgalmi engedély érvényessége',
label_vehicle_registration_document_number: 'Törzskönyv száma',
placeholder_registration_certificate_number: 'Pl. AB-123456',
placeholder_vehicle_registration_document_number: 'Pl. 1234567890',
registration_documents_title: 'Forgalmi engedély és Törzskönyv',
```
**Fájl:** `frontend/src/i18n/en.ts`
```typescript
label_registration_certificate_number: 'Registration Certificate Number',
label_registration_certificate_validity: 'Registration Certificate Validity',
label_vehicle_registration_document_number: 'Vehicle Registration Document Number',
placeholder_registration_certificate_number: 'e.g. AB-123456',
placeholder_vehicle_registration_document_number: 'e.g. 1234567890',
registration_documents_title: 'Registration Certificate & Vehicle Document',
```
---
## 7. Lépés: Backend i18n (opcionális)
**Fájl:** `backend/static/locales/hu.json`
```json
"REGISTRATION_DOCUMENTS": {
"CERTIFICATE_NUMBER": "Forgalmi engedély száma",
"CERTIFICATE_VALIDITY": "Forgalmi engedély érvényessége",
"DOCUMENT_NUMBER": "Törzskönyv száma"
}
```
**Fájl:** `backend/static/locales/en.json`
```json
"REGISTRATION_DOCUMENTS": {
"CERTIFICATE_NUMBER": "Registration Certificate Number",
"CERTIFICATE_VALIDITY": "Registration Certificate Validity",
"DOCUMENT_NUMBER": "Vehicle Registration Document Number"
}
```
---
## Tesztelési terv
1. **Adatbázis:** `docker exec -it sf_api python -m app.scripts.sync_engine` futtatása után ellenőrizni, hogy a 3 új oszlop létrejött a `vehicle.assets` táblában
2. **API:** POST /vehicles hívás új mezőkkel → 201 Created + az új mezők visszaküldése
3. **API:** PUT /vehicles/{id} hívás új mezőkkel → 200 OK + az új mezők frissítése
4. **API:** GET /vehicles/{id} → az új mezők megjelennek a válaszban
5. **Frontend:** VehicleFormModal → Admin tabon megjelennek az új mezők, mentéskor elküldődnek, szerkesztéskor visszatöltődnek

View File

@@ -0,0 +1,148 @@
# P0 Plan: Subscription Profile Menu & Modal
## 1. Overview
Add an "Előfizetés" (Subscription) menu item to the avatar dropdown in [`HeaderProfile.vue`](frontend/src/components/header/HeaderProfile.vue), which opens a modal showing **real subscription data** (package name, expiry, vehicle limits/usage, garage limits/usage) plus a "Renew" (Meghosszabbítás) button.
## 2. Files to Modify
| # | File | Action | Reason |
|---|------|--------|--------|
| 1 | [`frontend/src/components/header/HeaderProfile.vue`](frontend/src/components/header/HeaderProfile.vue) | **Modify** | Add "Előfizetés" menu item between Profile and Admin Center |
| 2 | [`frontend/src/components/subscription/SubscriptionInfoModal.vue`](frontend/src/components/subscription/SubscriptionInfoModal.vue) | **Create** | New modal component showing subscription details |
| 3 | [`frontend/src/i18n/en.ts`](frontend/src/i18n/en.ts) | **Modify** | Add i18n keys for the new modal |
| 4 | [`frontend/src/i18n/hu.ts`](frontend/src/i18n/hu.ts) | **Modify** | Add Hungarian translations |
## 3. Data Binding Paths
### 3.1 Current Plan Name
```
authStore.user?.subscription_plan (individual mode)
activeOrg?.subscription_plan (corporate mode)
```
### 3.2 Expiry Date
The backend `UserResponse` schema does NOT currently expose `subscription_expires_at`. However:
- The [`User` model](backend/app/models/identity/identity.py:142) has `subscription_expires_at`
- The [`Organization` model](backend/app/models/marketplace/organization.py:141-143) has `subscription_plan`, `base_asset_limit`, `purchased_extra_slots`, `subscription_tier_id`
- The [`OrganizationSubscription` model](backend/app/models/core_logic.py:25-47) has `valid_until`
**Current frontend approach** (from [`SubscriptionStatusWidget.vue`](frontend/src/components/dashboard/SubscriptionStatusWidget.vue:87-95)):
- For corporate mode: `activeOrg?.subscription_valid_until` (but this field is NOT in the backend `/organizations/my` response)
- For individual mode: `authStore.user?.subscription_valid_until` (but this field is NOT in the `UserResponse` schema)
**Gap:** The `subscription_expires_at` field exists on the backend `User` model but is not exposed in the `UserResponse` Pydantic schema. Similarly, `subscription_expires_at` exists on the `Organization` model but is not returned by the `/organizations/my` endpoint.
**Plan:** We will add `subscription_expires_at` to:
1. [`UserResponse`](backend/app/schemas/user.py:54-73) schema
2. The `/organizations/my` endpoint response dict (line 222 area)
### 3.3 Vehicle Limits & Usage
```
From SubscriptionTier.rules.allowances:
plan.rules.allowances.max_vehicles (max limit)
plan.rules.allowances.max_garages (max limit)
Current usage from:
vehicleStore.vehicles.length (current vehicles count)
authStore.myOrganizations.length (current garages count)
```
### 3.4 Resolving the Tier
The tier is resolved via `subscription_tier_id` on the org/user. We need a new API endpoint or we can use the existing [`/subscriptions/public`](frontend/src/views/SubscriptionPlansView.vue:309) endpoint to fetch all tiers and match by name.
**Better approach:** Add a new lightweight endpoint `GET /subscriptions/my` that returns the current user's/org's subscription details including:
- `tier_name` (the plan name)
- `tier_rules` (the full rules JSONB)
- `expires_at` (from `subscription_expires_at`)
- `current_vehicles` count
- `current_garages` count
This avoids multiple round-trips and complex client-side stitching.
### 3.5 Renewal Action
The "Meghosszabbítás" button should navigate to the existing [`SubscriptionPlansView`](frontend/src/views/SubscriptionPlansView.vue) which already shows plan cards with a "Details & Purchase" flow. The existing [`PlanDetailsModal`](frontend/src/components/subscription/PlanDetailsModal.vue) handles the purchase simulation.
## 4. Proposed Architecture
### 4.1 Backend Changes (Optional but Recommended)
Add `subscription_expires_at` to `UserResponse` schema and to the `/organizations/my` response.
### 4.2 New Component: `SubscriptionInfoModal.vue`
- Glassmorphism style matching the dropdown
- Shows:
- Package name (display_name from tier rules, or plan name)
- Expiry date with days-remaining countdown
- Progress bar (same as `SubscriptionStatusWidget`)
- Vehicle usage: "3 / 5 vehicles" with progress bar
- Garage usage: "1 / 2 garages" with progress bar
- "Meghosszabbítás" button → navigates to `/subscription-plans`
### 4.3 HeaderProfile.vue Changes
Add a new menu item between Profile and Admin Center:
```html
<button @click="openSubscriptionModal">
<svg>...</svg>
{{ t('header.subscription') }}
</button>
```
### 4.4 Data Flow
1. User clicks "Előfizetés" in dropdown
2. Modal opens, reads data from:
- `authStore.user.subscription_plan` / `authStore.user.subscription_expires_at`
- `adminPackagesStore.tiers` (already fetched) to find matching tier by name
- `vehicleStore.vehicles.length` for current vehicle count
- `authStore.myOrganizations.length` for current garage count
3. "Meghosszabbítás" button → `router.push('/subscription-plans')`
## 5. i18n Keys to Add
### English (`en.ts`)
```json
subscription: {
// ... existing keys ...
mySubscription: "My Subscription",
renew: "Renew",
expiresAt: "Expires at",
vehicleUsage: "Vehicle Usage",
garageUsage: "Garage Usage",
of: "of",
}
header: {
// ... existing keys ...
subscription: "Subscription",
}
```
### Hungarian (`hu.ts`)
```json
subscription: {
// ... existing keys ...
mySubscription: "Előfizetésem",
renew: "Meghosszabbítás",
expiresAt: "Lejárat",
vehicleUsage: "Járműhasználat",
garageUsage: "Garázshasználat",
of: "/",
}
header: {
// ... existing keys ...
subscription: "Előfizetés",
}
```
## 6. Risk Assessment
- **Data availability:** `subscription_expires_at` is NOT currently exposed in the frontend-accessible schemas. We need to either:
- (A) Add it to the backend schemas (recommended, clean)
- (B) Create a new `/subscriptions/my` endpoint
- (C) Use only the plan name and show "N/A" for expiry (fallback)
- **Tier rules resolution:** The frontend `adminPackagesStore` already fetches all tiers. We can match by `plan.name` to get the rules.
- **Vehicle/garage counts:** Already available in existing stores.
## 7. Implementation Order
1. Add `subscription_expires_at` to backend `UserResponse` schema
2. Add `subscription_expires_at` to `/organizations/my` response
3. Create `SubscriptionInfoModal.vue`
4. Add i18n keys
5. Modify `HeaderProfile.vue` to add menu item and wire up modal
6. Run sync_engine and verify

View File

@@ -0,0 +1,275 @@
# 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.