frontend költség beállítások
This commit is contained in:
264
plans/logic_spec_vehicle_registration_documents.md
Normal file
264
plans/logic_spec_vehicle_registration_documents.md
Normal 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
|
||||
148
plans/p0_subscription_profile_menu_modal_plan.md
Normal file
148
plans/p0_subscription_profile_menu_modal_plan.md
Normal 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
|
||||
275
plans/p0_vehicle_card_fix_plan.md
Normal file
275
plans/p0_vehicle_card_fix_plan.md
Normal 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.
|
||||
Reference in New Issue
Block a user