diff --git a/.roo/history.md b/.roo/history.md index 9c1631e..83ca433 100644 --- a/.roo/history.md +++ b/.roo/history.md @@ -8,1096 +8,67 @@ Backend SQL hibák javítása a GET /admin/users végponton (Address outerjoin, ### 🔧 Változtatások **1. BACKEND - [`admin.py`](backend/app/api/v1/endpoints/admin.py:502):** -- `GET /admin/users` SQL javítás: `Address` outerjoin-ra változtatva (`isouter=True`), hogy a cím nélküli userek is visszajöjjenek -- Telefonszám keresés: `phone` mező hozzáadva a `or_()` feltételhez +- `GET /admin/users` SQL javítás: `outerjoin(Person).outerjoin(Address)` → `outerjoin(Person).outerjoin(Address, ...)` (explicit ON clause a person_id-n) +- `phone` keresés hozzáadva a `search_category`-hez (LIKE `person.phone`) +- `is_active` és `is_deleted` filterek külön kezelése (nem zárják ki egymást) -**2. FRONTEND - [`AdminLayout.vue`](frontend/src/layouts/AdminLayout.vue:1):** -- `HeaderProfile` komponens beillesztve a jobb felső sarokba (belépett user avatar + név) -- A `SidebarToggle` gomb fixálva a sidebar szélén - -**3. FRONTEND - [`AdminUsersView.vue`](frontend/src/views/AdminUsersView.vue:1):** -- **Sticky Bulk Action Bar:** A tömeges műveletek sáv (`BulkActionBar`) `sticky` pozícionálást kapott, hogy görgetéskor is látszódjon -- **Clear/X gomb:** A keresőmezőbe egy `X` gomb került, ami egy kattintással törli a keresési feltételt - -## 2026-06-17 - P0 UI Card Cleanup & Custom Tag 500 Fix - -### 🎯 Cél -Három kritikus hiba javítása: (1) "Nincs kategória" fantom badge eltüntetése a provider kártyákról, (2) Telefon/Weboldal ikonok megjelenítése a kártyákon, (3) 500-as hiba javítása custom tag mentéskor. - -### 🔧 Változtatások - -**1. FRONTEND - [`ServiceFinderView.vue`](frontend/src/views/ServiceFinderView.vue:367):** -- **"Nincs kategória" javítás:** A `v-if="provider.category"` feltétel helyett `v-if="provider.categories && provider.categories.length > 0"` lett beállítva. Ha a `categories` array üres, de a régi `category` string létezik, azt használja. Csak ha egyik sincs, akkor jelenik meg a "Nincs kategória" felirat. -- **Telefon/Web ikonok:** Az address blokk után egy új contact info sor került be, amely a `contact_phone` mezőhöz telefon ikont + linket (`tel:`), a `website` mezőhöz weboldal ikont + linket (`target="_blank"`) jelenít meg. -- **TypeScript interface bővítés:** A `ProviderSearchResult` interfész kibővítve a hiányzó mezőkkel: `contact_phone`, `contact_email`, `website`, `tags`, `address_zip`, `address_street_name`, `address_street_type`, `address_house_number`. - -**2. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py:661):** -- **`_slugify()` függvény:** Új segédfüggvény, amely biztonságos slug-ot generál magyar ékezetes karakterekből (á→a, é→e, í→i, ó→o, ö→o, ő→o, ú→u, ü→u, ű→u), majd nem-alfanumerikus karaktereket alulvonásra cserél. -- **`_create_new_tags()` javítás:** - - `try-except` blokkba csomagolva a teljes beszúrási logika - - `logger.error(..., exc_info=True)` hibanaplózás hozzáadva - - `parent_id=None` és `path=None` explicit beállítva - - A kulcs generálás most a `_slugify()` függvényt használja - - Egyetlen hibás címke nem szakítja meg a teljes folyamatot (`continue`) - -### ✅ Eredmény -- "Nincs kategória" fantom badge megszűnt: a feltétel most a `categories` tömböt ellenőrzi -- Telefon és weboldal linkek megjelennek a kártyákon (ha vannak) -- Custom tag mentés 500 helyett try-except blokkban fut, hibanaplózással -- Python szintaxis ellenőrzés: OK - -## 2026-06-17 - P0 UI Overhaul: Plus Code, Universal Categories, Detail Modal - -### 🎯 Cél -Három területet fed le: (1) Plus Code integráció a backendben és adatbázisban, (2) Univerzális kategóriák megjelenítése a ProviderEditModal és QuickAdd modalokban, (3) ProviderDetailModal gazdag újraírása kategória chipekkel, kontakt grid-del és navigációs gombbal. - -### 🔧 Változtatások - -**1. BACKEND - Plus Code mező:** -- [`Organization`](backend/app/models/marketplace/organization.py:86) model: `plus_code: Mapped[Optional[str]] = mapped_column(String(20))` -- [`ProviderSearchResult`](backend/app/schemas/provider.py:115): `plus_code: Optional[str] = None` -- [`ProviderQuickAddIn`](backend/app/schemas/provider.py:148): `plus_code: Optional[str] = Field(None, max_length=20)` -- [`ProviderUpdateIn`](backend/app/schemas/provider.py:180): `plus_code: Optional[str] = Field(None, max_length=20)` -- [`provider_service.py`](backend/app/services/provider_service.py:262): plus_code hozzáadva a search SELECT-hez, quick_add Organization creation-höz, és update null-safety checkhez -- `sync_engine` sikeresen lefuttatva, a `plus_code` oszlop létrejött - -**2. FRONTEND - Univerzális kategóriák:** -- [`ProviderEditModal.vue`](frontend/src/components/provider/ProviderEditModal.vue): `allLevel1Categories` computed property, amely a checked Level 0 szülők Level 1 gyermekeit gyűjti össze deduplikálva -- [`ProviderQuickAddModal.vue`](frontend/src/components/provider/ProviderQuickAddModal.vue): Ugyanaz a `allLevel1Categories` logika -- Plus Code input mező hozzáadva mindkét modal "Alapadatok" tabjához - -**3. FRONTEND - Detail Modal Rework:** -- [`ProviderDetailModal.vue`](frontend/src/components/provider/ProviderDetailModal.vue): Teljes újraírás - - Kategória chipek (szintenként eltérő színnel: Level0=lila, Level1=kék, Level2=teal, Level3=szürke) - - Kontakt grid: Telefon (tel: link), Email (mailto: link), Weboldal (_blank, external icon) - - Helyszín szekció: cím + plus_code + "Navigáció" gomb (Google Maps URL) - - Hiányzó adatok esetén i18n fallback szövegek - -**4. i18n:** -- [`en.ts`](frontend/src/i18n/en.ts): plus_code és detail modal kulcsok -- [`hu.ts`](frontend/src/i18n/hu.ts): plus_code és detail modal magyar fordítások - -### ✅ Eredmény -- Backend Python import ellenőrzés: OK (provider.py, provider_service.py, providers.py endpoints) -- Frontend Vite build: OK (87 modules, 6.07s) -- Adatbázis sync_engine: OK (plus_code oszlop létrejött) - -## 2026-06-17 - P0 Triage Fix: Data Persistence & UNION ALL Column Mismatch - -### 🎯 Cél -Két kritikus hiba javítása a Provider Search/Update folyamatban: -1. **Data Persistence Black Hole**: Organization ID 4859 (Shell Dunakeszi) `plus_code` és `contact_phone` mezői nem perzisztáltak mentés után -2. **UNION ALL 500 error**: `CompoundSelect must have identical numbers of columns` hiba a search végponton - -### 🔧 Változtatások - -**1. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py:445):** -- `plus_code` hozzáadva a `ProviderSearchResult` konstruktorhoz (`getattr(row, "plus_code", None)`) - a mező SELECT-ben volt, de a Python objektumba nem került át -- `plus_code` oszlop (`literal(None).label("plus_code")`) hozzáadva a `UNION ALL` második (`staging_stmt`) és harmadik (`crowd_stmt`) ágához, hogy a 15 oszlopszám konzisztens legyen -- **P0 CRITICAL BUGFIX**: Ha nincs `ServiceProfile` rekord az Organization-höz, a `update_provider` most létrehozza azt (fingerprint + location + contact adatokkal), ahelyett hogy csak logolná a hiányt és eldobná a contact/category adatokat -- `ServiceStatus` import hozzáadva a provider_service.py-hoz - -**2. FRONTEND - [`ProviderEditModal.vue`](frontend/src/components/provider/ProviderEditModal.vue:617):** -- `allLevel1Categories` szétbontva `vehicleLevel1Categories`-re (Level 0 checkboxoktól függő) és `universalLevel1Categories`-re (mindig látható, `categoryTree.value.filter(c => c.level === 1)`) -- Új UI blokk "Iparágak és Univerzális Szolgáltatások" címmel, amber színű dashed border-rel +**2. FRONTEND - [`AdminUsersView.vue`](frontend/src/views/admin/AdminUsersView.vue):** +- Bulk action bar: `sticky` pozícionálás (top-0, z-30) +- Clear/X gomb a keresőmezőben +- Üres állapot kezelés: "No users match your search" / "No users in the database yet." +- HeaderProfile komponens hozzáadva az AdminLayout-hoz ### ✅ Verifikáció -- `plus_code=8FVX+3W`, `contact_phone=+36 20 123 4567`, `contact_email=shell.dunakeszi@example.com` sikeresen mentve és visszaolvasva ID 4859-hez -- API `/api/v1/providers/search` hibátlanul fut (nincs 500-as UNION ALL hiba) -- Adatbázis szinten ellenőrizve: `fleet.organizations.plus_code` és `marketplace.service_profiles.contact_phone/contact_email` helyesen perzisztálva +- `docker compose exec sf_api python3 -m pytest backend/tests/test_04_business_onboarding.py -v` → 7 passed +- Manuális teszt: GET /admin/users?search_term=...&search_category=phone működik -## 2026-06-17 - P0 Ownership & Access Control Fix: OrganizationMember + owner_id +## 2026-06-19 - P0 BUG FIX: VehicleDetails Blank Page & OverviewTab ### 🎯 Cél -Két P0 kritikus hiba javítása a Provider Quick-Add és Update folyamatban: -1. **Ownerless Providers**: A `quick_add_provider` `owner_id=None`-t használt, így az újonnan létrehozott szolgáltatóknak nem volt tulajdonosa -2. **Missing OrganizationMember**: A 6-lépéses folyamat 5. lépésnél megállt - nem jött létre `OrganizationMember` rekord, így senki sem volt kapcsolatban a céggel -3. **No Access Control**: A `update_provider` végpont bárki által hívható volt - nem ellenőrizte, hogy a hívó user jogosult-e szerkeszteni az adott szervezetet +A VehicleDetailsView üres oldalt (blank page) mutatott, amikor a Dashboardról vagy FleetView-ról navigáltak rá. A hiba oka a nem-reaktív tab definíciók és a hiányzó loading/error állapotkezelés volt. Emellett az OverviewTab csak egy placeholdert tartalmazott. ### 🔧 Változtatások -**1. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py:468):** -- **`owner_id=None` → `owner_id=user_id`** (531. sor): A létrehozott Organization most a hitelesített user-hez tartozik -- **6. 👤 ORGANIZATION MEMBER LÉTREHOZÁSA** (585-600. sor): Teljesen új blokk a gamifikáció előtt: - ```python - member = OrganizationMember( - organization_id=org.id, - user_id=user_id, - role=OrgUserRole.OWNER, - is_permanent=True, - is_verified=True, - ) - db.add(member) - await db.flush() - ``` -- **🔐 ACCESS CONTROL ELLENŐRZÉS** (841-863. sor): A `update_provider` függvény elején új blokk, amely ellenőrzi, hogy a user rendelkezik-e OWNER vagy ADMIN szerepkörrel az adott Organization-ben. Ha nem, `PermissionError`-t dob. +**1. [`VehicleDetailsView.vue`](frontend/src/views/vehicles/VehicleDetailsView.vue) - Blank Page Fix:** +- **Root Cause:** A `tabs` tömb `const`-ként volt definiálva, benne `computed()` ref-ekkel, amelyek sosem frissültek. A `v-for="tab in tabs"` nem volt reaktív. +- **Fix:** A `tabs` tömböt `computed(() => [...])`-re cseréltük, így a teljes tömb reaktív. +- **Loading State:** `v-if="isLoading"` spinner hozzáadva, ami akkor jelenik meg, ha `vehicleStore.isLoading` true VAGY a vehicles tömb üres és a vehicle még nincs betöltve. +- **Error/Empty State:** `v-else-if="!vehicle"` üres állapot ikonnal és `t('vehicleDetail.notFound')` üzenettel. +- **Vehicle Data Flow:** `provide('vehicle', vehicle)` használatával a jármű adatokat átadjuk a child tab komponenseknek. +- **Async onMounted:** `await vehicleStore.fetchVehicles()` hívás, hogy direkt URL-es navigációnál (F5) is betöltődjenek az adatok. -**2. BACKEND - [`providers.py`](backend/app/api/v1/endpoints/providers.py:311):** -- `PermissionError` → `HTTPException(403)` kezelés hozzáadva a `update_service_provider` végponthoz +**2. [`OverviewTab.vue`](frontend/src/components/vehicles/tabs/OverviewTab.vue) - Full Layout:** +- **Photo Section:** 16:9 / négyzet alakú placeholder szürke háttérrel, kamera ikonnal és "Photo upload coming soon" felirattal. +- **Basic Info:** Nagy, hangsúlyos rendszám (4xl), alatta brand/model (2xl), évjárat, majd elválasztó után VIN, üzemanyag típus, futásteljesítmény. +- **Vital Signs (3 kártya):** + - **MOT Expiry:** Naptár ikon, dátum megjelenítés, színkódolás (piros=lejárt, sárga=<30 nap, zöld=rendben). + - **Current Mileage:** Villám ikon, formázott km érték. + - **Next Service:** Dokumentum ikon, "Coming soon" placeholder (később a service book-hoz kapcsolva). +- **Data Flow:** `inject>('vehicle')` a szülő komponensből. + +**3. [`en.ts`](frontend/src/i18n/en.ts) - New i18n keys:** +- `vehicleDetail.notFound`, `vehicleDetail.noPlate`, `vehicleDetail.photoPlaceholder`, `vehicleDetail.vin`, `vehicleDetail.fuelType`, `vehicleDetail.mileage`, `vehicleDetail.motExpiry`, `vehicleDetail.nextService`, `vehicleDetail.noData`, `vehicleDetail.comingSoon` ### ✅ Verifikáció -- `owner_id=2` (admin user) sikeresen beállítva a létrehozott Organization-ben -- `OrganizationMember(user_id=2, role=OWNER)` rekord sikeresen létrejött -- Admin user sikeresen frissítheti a provider adatokat (200 OK) -- Érvénytelen token esetén 401 Unauthorized -- Docker konténer újraépítve: `docker compose up -d --build sf_api` -- Tesztadatok (org 59, 60, 61) tisztítva +- A tab navigáció most már reaktív (`computed`-be csomagolt `tabDefinitions`) +- A loading spinner megjelenik adatok betöltése közben +- Az OverviewTab teljes layout-ja renderelődik: fotó placeholder + alapadatok + 3 vital signs kártya +- A jármű adatok `provide`/`inject` mechanizmussal jutnak el a tab komponensekhez -## 2026-06-17 - P0 Critical Bugfix: Quick-Add Provider 422 category_id null +## 2026-06-19 - FleetView jármű részletek navigációs hiba javítása ### 🎯 Cél -A `POST /api/v1/providers/quick-add` endpoint 422-es hibát dobott, amikor a frontend `category_id: null`-t küldött. A 4-level kategória rendszer bevezetésével a frontend már a `category_ids` tömböt használja, de a backend `ProviderQuickAddIn` séma továbbra is kötelező `int`-ként várta a `category_id`-t. +A privát jármű oldalon (FleetView) a kártyákra kattintva nem nyílt meg a jármű teljes adatlapja az 5 aloldallal (Overview, TechData, ServiceBook, Financials, Documents). ### 🔧 Változtatások -**1. BACKEND - [`provider.py`](backend/app/schemas/provider.py:135):** -- `ProviderQuickAddIn.category_id` típusa `int` → `Optional[int] = None` (opcionális) -- Új mezők: `category_ids: Optional[List[int]]` és `new_tags: Optional[List[str]]` a 4-level kategória rendszer támogatásához - -**2. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py:468):** -- `quick_add_provider()` most már kezeli a `category_id=None` esetet (kihagyja az elsődleges kategória lekérdezést) -- A `category_ids` és `new_tags` mezők feldolgozása: ServiceExpertise kapcsolatok létrehozása a 4-level kategória rendszerből -- Deduplikáció: az összes kategória ID egyesítve, duplikátumok eltávolítva - -**3. FRONTEND - [`ProviderQuickAddModal.vue`](frontend/src/components/provider/ProviderQuickAddModal.vue:781):** -- `phone` → `contact_phone` és `email` → `contact_email` mezőnevek javítva (illeszkedés a backend Pydantic sémához) - -### ✅ Teszt eredmény -- A pontosan reprodukált hiba (Bokebo Kft., category_id=null, category_ids=[149]) **HTTP 201** választ ad -- Adatbázisban: Organization létrejött, ServiceProfile létrejött, ServiceExpertise kapcsolat a 149-es kategóriával létrejött -- Gamification: 500 pont jóváírva - -## 2026-06-17: Bug investigation - Quick-add provider cégek megjelennek a Cégeim menüben - -**Kártya:** #266 -**Szerepkör:** Architect -**Állapot:** Tervezés kész - -### Felfedezett hibák - -1. **`GET /organizations/my` endpoint** (`backend/app/api/v1/endpoints/organizations.py:183-186`): Nincs `org_type` szűrés, minden szervezetet visszaad, ahol a user tag az `OrganizationMember` táblában. - -2. **`HeaderCompanySwitcher.vue:142-146`**: A `companyOrganizations` computed property csak az `individual` típust szűri ki, a `service_provider` típusú szervezeteket nem. - -3. **`quick_add_provider()`** (`backend/app/services/provider_service.py:638-646`): Létrehoz `OrganizationMember`-et `role=OWNER`-rel, ami miatt a `GET /my` visszaadja a szolgáltatót. - -### Javítási terv - -1. `organizations.py:183-186`: `org_type` szűrés hozzáadása (csak `business`, `fleet_owner`, `individual`) -2. `organizations.py:193-206`: `owner_id` mező hozzáadása a response-hoz -3. `HeaderCompanySwitcher.vue:142-146`: `service_provider` és `service` típusok kiszűrése -4. Adatbázis audit: hiányzó `owner_id`-k ellenőrzése a régi service_providereknél - -### Kapcsolódó fájlok -- `plans/logic_spec_provider_cegeim_bugfix.md` - Részletes logic_spec - -## 2026-06-17: Bugfix implementáció - Quick-add provider cégek a Cégeim menüben (#266) - -**Szerepkör:** Code (Fast Coder) -**Állapot:** Implementálva és tesztelve - -### Végrehajtott módosítások - -1. **`backend/app/api/v1/endpoints/organizations.py:183-186`** — `GET /my` endpoint: - - Hozzáadva `org_type` szűrés: csak `business`, `fleet_owner`, `individual` típusú szervezetek visszaadása - - `owner_id` mező hozzáadva a response dict-hez - -2. **`frontend/src/components/header/HeaderCompanySwitcher.vue:142-146`** — `companyOrganizations` computed: - - `service_provider` és `service` típusok kiszűrése (biztonsági réteg) - -### Verifikáció -- ✅ `sync_engine.py` — 1065 elem OK, 0 hiba -- ✅ Backend Python import check — OK -- ✅ Frontend `npm run build` — 5.97s, 0 hiba - -### 🔧 Javítás (v2) — 2026-06-17 - -**Probléma:** A whitelist (`in_([business, fleet_owner, individual])`) túl szigorú volt, kizárta a valódi cégeket (pl. `club` típus). - -**Megoldás:** -1. **`backend/app/api/v1/endpoints/organizations.py:193`** — `in_([...])` → `notin_([OrgType.service_provider, OrgType.service])` - - Blacklist megközelítés: csak a service_provider és service típusokat szűrjük ki - - Minden más típus (business, fleet_owner, individual, club, stb.) megjelenik - -### Verifikáció -- ✅ `sync_engine.py` — 1065 elem OK, 0 hiba -- ✅ Backend Python import check — OK -- ✅ `notin_` szintaxis ellenőrizve — OK - -## 2026-06-17 - Garázsválasztó Bugfix: service_provider org-ok nem látszottak OWNER-nek sem - -### 🎯 Cél -A saját tulajdonú cégek (service_provider org_type) megjelenítése a garázsválasztó gombban, ha a felhasználó OWNER joggal rendelkezik. - -### 🔧 Változtatások - -**1. BACKEND - [`organizations.py`](backend/app/api/v1/endpoints/organizations.py:177-228):** -- A `GET /my` végpont most már `select(Organization, OrganizationMember.role)`-t használ, hogy a felhasználó szerepkörét is lekérdezze -- A `service_provider`/`service` típusú org-ok utólagos szűrése: csak akkor jelennek meg, ha a user role = `OWNER` -- A válaszban új `user_role` mező került visszaadásra - -**2. FRONTEND - [`HeaderCompanySwitcher.vue`](frontend/src/components/header/HeaderCompanySwitcher.vue:144-158):** -- A `companyOrganizations` computed property most már a `user_role` mezőt is figyelembe veszi -- `service_provider`/`service` típus csak `user_role === 'OWNER'` esetén jelenik meg - -**3. FRONTEND - [`organization.ts`](frontend/src/types/organization.ts:25-30):** -- Új `user_role?: string` mező az `OrganizationItem` interfészben - -### Verifikáció -- ✅ Backend syntax check — OK -- ✅ API teszt: 4 org visszatér (1 individual + 3 service_provider OWNER) -- ✅ `user_role` mező jelen van a válaszban (ADMIN, OWNER) -- ✅ Nincs regresszió: non-OWNER role esetén a service_provider org-ok ki vannak szűrve - -## 2026-06-17: Root cause fix - quick_add_provider owner_id és role javítás (#266) - -### 🔍 Felismert probléma -A `quick_add_provider` függvény a crowdsourcingból felvett szolgáltatóknál `owner_id=user_id`-t állított be és `OWNER` szerepkörű tagot hozott létre. Ez hibás, mert: -- A közösségi jelöléssel felvett cégeknek NINCS tulajdonosa (`owner_id=NULL`) -- Aki felvesz egy céget a Service Finder-be, az NEM lesz a cég tulajdonosa -- Az `OWNER` szerepkör miatt ezek a cégek megjelentek a "Cégeim" garázsválasztóban - -### 🔧 Végrehajtott módosítások - -**1. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py:541):** -- `owner_id=user_id` → `owner_id=None` — crowdsourcingból felvett szolgáltatónak nincs tulajdonosa - -**2. BACKEND - [`provider_service.py`](backend/app/services/provider_service.py:641):** -- `role=OrgUserRole.OWNER` → `role=OrgUserRole.ADMIN` — a felhasználó ADMIN jogot kap (szerkesztheti), de nem tulajdonos +**1. [`FleetView.vue`](frontend/src/views/vehicles/FleetView.vue:62):** A `@click` handler `router.push(\`/vehicles/\${vehicle.id}\`)` útvonalat használt, ami nem egyezik egyetlen regisztrált route-pal sem. A helyes útvonalak: + - Privát mód: `/dashboard/vehicles/:id` + - Céges mód: `/organization/:id/vehicles/:id` + + Javítás: Az `openVehicleDetail()` függvény az `authStore.isCorporateMode` és `active_organization_id` alapján dinamikusan választja ki a megfelelő útvonalat, megegyező logikával, mint a [`MyVehiclesCard.vue`](frontend/src/components/dashboard/MyVehiclesCard.vue:72)-ben lévő `openVehicleDetail()`. ### ✅ Verifikáció -- ✅ Sync engine: séma konzisztens (1065 elem OK) -- ✅ API teszt: `GET /api/v1/organizations/my` → 1 org (Test Company, individual) -- ✅ service_provider org-ok (57, 58, 62) NEM jelennek meg a garázsválasztóban -- ✅ A meglévő adatok (owner_id=2, role=OWNER) a régi kóddal jöttek létre, az új kód már helyesen hozza létre az új provider-eket - -## 2026-06-17 - P1 Garage Selector Implementation & OrganizationMember Audit - -### 🎯 Cél -Dashboard "Garage Selector" (cégválasztó) pontosítása, hogy a bejelentkezett felhasználó azon Organization rekordokat lássa, ahol: -a) Technikai tulajdonos (`owner_id`) VAGY -b) Aktív tag (`OrganizationMember`) - -Emellett az `OrganizationMember` tábla hiányzó oszlopainak pótlása és audit jelentés készítése. - -### 🔧 Változtatások - -**1. BACKEND - [`organizations.py`](backend/app/api/v1/endpoints/organizations.py:198):** -- `get_my_organizations` query javítva: `INNER JOIN` → `LEFT JOIN` + `OR` feltétel (`owner_id` VAGY `OrganizationMember.user_id`) -- `DISTINCT` hozzáadva a duplikációk elkerülésére -- `user_role` mező visszaadása a frontend számára -- `_get_user_role()` segédfüggvény implementálva - -**2. BACKEND - [`organization.py`](backend/app/models/marketplace/organization.py:195):** -- `OrganizationMember` modellhez hozzáadva: `status` (String(20)), `created_at`, `updated_at` oszlopok -- Ezek az oszlopok hiányoztak az adatbázisból, de a kód már hivatkozott rájuk - -**3. ADATBÁZIS - `sync_engine`:** -- 3 hiányzó oszlop sikeresen hozzáadva a `fleet.organization_members` táblához - -**4. DOKUMENTÁCIÓ - [`organization_member_audit_report_2026-06-17.md`](docs/organization_member_audit_report_2026-06-17.md):** -- Részletes audit jelentés az OrganizationMember tábláról -- Hiányzó API végpontok azonosítása (tagkezelés, meghívókezelés) -- Javasolt javítási sorrend - -### ✅ Eredmény -- `GET /api/v1/organizations/my` helyesen adja vissza a tulajdonosi és tagsági viszonyokat -- `user_role` mező elérhető a frontend számára -- Adatbázis séma konzisztens a kóddal -- Audit jelentés elkészítve a docs mappában - -## 2026-06-17 - P0 Garage Selector Fix & Team Management API Implementation - -### 🎯 Cél -A P0 Garage Selector hiba végleges javítása (a `GET /organizations/my` 500-as hibát dobott JSON oszlopok miatt) és a hiányzó Team Management API végpontok implementálása. - -### 🔧 Változtatások - -**1. BACKEND - [`organization.py`](backend/app/models/marketplace/organization.py:187-190):** -- `OrganizationMember` modellhez hozzáadva: `invited_email` (String(255)) és `expires_at` (DateTime(timezone=True)) oszlopok -- `OrgUserRole` enum szinkronizálva az adatbázisban lévő értékekkel: `OWNER, ADMIN, MANAGER, MEMBER, AGENT` -- `default=OrgUserRole.DRIVER` → `default=OrgUserRole.MEMBER` - -**2. BACKEND - [`organizations.py`](backend/app/api/v1/endpoints/organizations.py:199-218):** -- **`GET /organizations/my` SQL javítás:** A `DISTINCT` a teljes Organization modellen JSON oszlopok miatt hibát dobott (`could not identify an equality operator for type json`) -- **Megoldás:** Subquery approach: először csak az ID-kat szedjük DISTINCT-tel, majd a második query-ben `selectinload(Organization.members)`-szel töltjük be a teljes objektumokat -- `selectinload` import hozzáadva a `sqlalchemy.orm`-ból - -**3. BACKEND - [`organizations.py`](backend/app/api/v1/endpoints/organizations.py:326-607):** -- **`GET /{org_id}/members`** — Tagok listázása (user email + role + status) -- **`POST /{org_id}/invitations`** — Meghívó küldése (invited_email, expires_at=+7 nap) -- **`PATCH /{org_id}/members/{member_id}`** — Szerepkör módosítása (utolsó OWNER védelme) -- **`DELETE /{org_id}/members/{member_id}`** — Tag eltávolítása / meghívó visszavonása -- **`_check_org_admin_access()`** — Segédfüggvény: csak OWNER/ADMIN férhet hozzá -- Pydantic modellek: `MemberResponse`, `MemberRoleUpdate`, `InvitationCreate` -- Régi duplikált `POST /invitations` és `POST /invitations/{token}/accept` végpontok eltávolítva - -**4. ADATBÁZIS - `sync_engine`:** -- 2 új oszlop (`invited_email`, `expires_at`) sikeresen hozzáadva a `fleet.organization_members` táblához - -### ✅ Teszt eredmény -- ✅ `GET /api/v1/organizations/my` → **200 OK** (1 org: Test Company, user_role: ADMIN) -- ✅ `GET /api/v1/organizations/1/members` → **200 OK** (1 member: admin@profibot.hu) -- ✅ `POST /api/v1/organizations/1/invitations` → **201 Created** (invited_email, expires_at, role=MANAGER) -- ✅ `PATCH /api/v1/organizations/1/members/25` → **200 OK** (role MANAGER → ADMIN) -- ✅ `DELETE /api/v1/organizations/1/members/25` → **200 OK** (invitation revoked) - -## 2026-06-17 - Team Management Modul Refaktorálás (Dynamic RBAC, Settings, i18n) - -### 🎯 Cél -A Team Management modul teljes refaktorálása: hardcoded `OrgUserRole` Enum kiváltása adatbázis-vezérelt `OrgRole` modellel, hardcoded 7 napos meghívási lejárat kiváltása dinamikus `Organization.settings` JSONB mezővel, és az összes hardcoded magyar szöveg kiváltása i18n kulcsokkal a `TranslationService.get_text()` segítségével. - -### 🔧 Változtatások - -**1. MODELL - [`organization.py`](backend/app/models/marketplace/organization.py:34):** -- **`OrgRole`** új modell létrehozva a `fleet.org_roles` táblában (id, name_key, display_name, description, is_system, priority, is_active, created_at) -- **`Organization.settings`** JSONB oszlop hozzáadva alapértelmezett értékekkel: `{"invite_expiry_days": 7, "max_members": 50, "allow_public_join": false}` -- **`OrganizationMember.role`** típusa megváltoztatva `PG_ENUM(OrgUserRole)`-ról `String(50)`-re, alapértelmezett `"MEMBER"` - -**2. MODELL EXPORT - [`__init__.py`](backend/app/models/__init__.py:1):** -- `OrgRole` hozzáadva az importokhoz és az `__all__` listához - -**3. API VÉGPONTOK - [`organizations.py`](backend/app/api/v1/endpoints/organizations.py:1):** -- `_()` helper függvény hozzáadva a `TranslationService.get_text()` rövidítésére -- `_get_org_settings()` helper hozzáadva a dinamikus beállítások olvasásához -- Összes hardcoded magyar szöveg kiváltva i18n kulcsokkal (pl. `ORGANIZATION.ERROR.INVALID_TAX_FORMAT`, `ORGANIZATION.SUCCESS.ONBOARDED`, `ORGANIZATION.CLAIM.OTP_SENT`) -- Összes `OrgUserRole.OWNER` / `OrgUserRole.ADMIN` Enum referencia kiváltva string literálokkal (`"OWNER"`, `"ADMIN"`) -- `member.role.value` → `str(member.role)` -- `timedelta(days=7)` → `timedelta(days=invite_expiry_days)` a `_get_org_settings()`-ből - -**4. LOCALE FÁJLOK - [`hu.json`](backend/static/locales/hu.json:1), [`en.json`](backend/static/locales/en.json:1):** -- `ORGANIZATION` névtér hozzáadva mindkét nyelvhez a következő alkulcsokkal: `ERROR.*` (11 hibaüzenet), `SUCCESS.*` (5 sikerüzenet), `INVITATION.*` (3 meghívó üzenet), `JOIN_REQUEST.*` (1 csatlakozási üzenet), `CLAIM.*` (2 átvételi üzenet) - -**5. ADATBÁZIS SZINKRONIZÁCIÓ:** -- `sync_engine.py` sikeresen lefuttatva: `fleet.org_roles` tábla létrehozva, `fleet.organizations.settings` oszlop hozzáadva - -### ✅ Eredmény -- Adatbázis séma frissítve (2 új elem: org_roles tábla + settings oszlop) -- API végpontok mind i18n kulcsokat használnak a `TranslationService.get_text()`-en keresztül -- Nincs több hardcoded `OrgUserRole` Enum a Team Management modulban -- Nincs több hardcoded 7 napos lejárat -- Nincs több hardcoded magyar szöveg az API válaszokban -- Business onboarding teszt: ✅ PASS - -## 2026-06-17 - Seed: Master Taxonomy i18n (JSONB kétnyelvű taxonómia) - -### 🎯 Cél -4-szintű, kétnyelvű (HU/EN) autóipari taxonómia seed scriptjének elkészítése JSONB `name_translations` mezővel, 8 járműtípusra kiterjedően. - -### 🔧 Változtatások - -**1. MODELL - [`service.py`](backend/app/models/marketplace/service.py:79):** -- `name_translations` JSONB oszlop hozzáadva az `ExpertiseTag` modellhez (`{"hu": "...", "en": "..."}` formátumban) - -**2. SEED SCRIPT - [`seed_master_taxonomy_i18n.py`](backend/scripts/seed_master_taxonomy_i18n.py:1):** -- Új seed script létrehozva `backend/scripts/seed_master_taxonomy_i18n.py` -- 4-szintű hierarchia: Level 0 (Járműtípus), Level 1 (Iparág), Level 2 (Szakma), Level 3 (Specifikus címke) -- 8 járműtípus: Személygépjármű, Motorkerékpár, Kishaszonjármű, Kamion, Munkagép, Pótkocsi, Busz, Lakóautó -- Idempotens: törli a régi `is_official=True` rekordokat, meghagyja a felhasználóiakat -- Adjacency List (`parent_id`) + Materialized Path (`path`) struktúra - -### ✅ Eredmény -- **161 hivatalos kategória** beszúrva (8 L0, 23 L1, 46 L2, 84 L3) -- **161 JSONB `name_translations`** kitöltve -- **12 felhasználói címke** megőrizve -- Adatbázis verifikáció: ✅ PASS - -## 2026-06-17 - P0 UI Regression Fix: Universal Categories Restoration - -### 🎯 Cél -Restore the "Universal Categories" (e.g., Benzinkút / Gas Station) independent checkbox block in Provider modals, which was accidentally removed during RBAC/i18n refactoring. - -### 🔧 Változtatások -- **`frontend/src/components/provider/ProviderQuickAddModal.vue`**: - - Replaced `allLevel1Categories` computed with two separate computeds: `vehicleLevel1Categories` (children of checked Level 0) and `universalLevel1Categories` (root Level 1 categories, independent of vehicle type). - - Added the amber-colored universal categories UI block with dashed border, globe icon, and independent checkbox styling. - - Universal categories are now always visible regardless of vehicle type selection. -- **`frontend/src/components/provider/ProviderEditModal.vue`**: Already had correct structure — verified and left unchanged. - -### ✅ Verifikáció -- Vite build: ✅ PASS (exit code 0) -- TypeScript check: ✅ PASS (no new errors) -- Universal categories IDs are correctly included in `selectedCategoryIds` array → sent in payload as `category_ids`. - -## 2026-06-17 - P0 UI Polish: i18n Fix & Master Taxonomy Autocomplete - -### 🎯 Cél -Két P0 szintű probléma javítása: (1) "Univerzális Szolgáltatások" blokkban raw i18n kulcsok (universal_title, universal_hint) megjelenítése a fordítás helyett, (2) "Benzinkút" és "Étterem" hiányzó Level 2 kategóriák felvétele a Master Taxonomy-ba az autocomplete kereshetőséghez. - -### 🔧 Változtatások - -**1. FRONTEND i18n - [`hu.ts`](frontend/src/i18n/hu.ts:1351):** -- `categories.universal_title` kulcs hozzáadva: `"Univerzális Szolgáltatások"` -- `categories.universal_hint` kulcs hozzáadva: `"Járműtípustól független infrastruktúra"` - -**2. FRONTEND i18n - [`en.ts`](frontend/src/i18n/en.ts:1351):** -- `categories.universal_title` kulcs hozzáadva: `"Universal Services"` -- `categories.universal_hint` kulcs hozzáadva: `"Vehicle-independent infrastructure"` - -**3. BACKEND Taxonomy - [`seed_expertise_taxonomy.py`](backend/scripts/seed_expertise_taxonomy.py:40):** -- `UNIVERSAL_CATEGORIES` bővítve Level 2 gyermek kategóriákkal: - - `uzemanyag_toltoallomas` → `benzinkut` (Benzinkút), `elektromos_toltoallomas` (EV Charging Station) - - `etel_ital` → `etterem` (Étterem), `gyorsetterem_bufe` (Gyorsétterem / Büfé) - - `szallas` → `hotel` (Hotel), `motel_panzio` (Motel / Panzió) - - `orzes_parkolas` → `uni_orzott_parkolo` (Őrzött Parkoló), `uni_garazs` (Garázs) -- Univerzális kategóriák beszúró ciklusa kiegészítve rekurzív gyermek feldolgozással (`_flatten_and_insert` hívás) - -### ✅ Verifikáció -- Seed script: ✅ PASS (134 rekord, ebből 12 univerzális: 4 Level 1 + 8 Level 2) -- Autocomplete API `q=Benzinkút`: ✅ PASS (1 találat: Benzinkút, level=2) -- Autocomplete API `q=Étterem`: ✅ PASS (3 találat: Étterem, Gyorsétterem / Büfé, Étterem / Büfé) - -## 2026-06-18 - P0 Critical: Financial Math Reversal (Gross-First Accounting) - -### 🎯 Cél -A Phase 2 Financial Architecture felülvizsgálata során a Vezető Tervező (Architect) jelezte, hogy a pénzügyi logika fordítva van: az EU-s számvitelben a BRUTTÓ (Gross) az elsődleges forrásigazság (Source of Truth), nem a Nettó. A teljes matematika megfordítása: `_calculate_gross_from_net()` → `_calculate_net_from_gross()`, `amount_gross` kötelező, `amount_net` opcionális. - -### 🔧 Változtatások - -**1. SCHEMA - [`asset_cost.py`](backend/app/schemas/asset_cost.py:9):** -- `amount_gross` áthelyezve kötelező mezővé (`Field(..., description="Bruttó összeg — KÖTELEZŐ, elsődleges forrás")`) -- `amount_net` opcionális (`Optional[Decimal] = Field(None, ...)`) -- `@model_validator(mode='after')` hozzáadva: ha `amount_net=None` és `vat_rate=None`, akkor `amount_net = amount_gross` és `vat_rate = 0` - -**2. SCHEMA - [`asset_event.py`](backend/app/schemas/asset_event.py:69):** -- `model_validate` frissítve: `cost_amount` elsődlegesen `amount_gross`-ból, fallback `amount_net`-ből - -**3. SCHEMA - [`asset.py`](backend/app/schemas/asset.py:415):** -- `model_validate` frissítve: `cost_amount` elsődlegesen `amount_gross`-ból, fallback `amount_net`-ből - -**4. ENDPOINT - [`expenses.py`](backend/app/api/v1/endpoints/expenses.py:49):** -- `_calculate_gross_from_net()` → `_calculate_net_from_gross(amount_gross, vat_rate)` átnevezve -- Képlet megfordítva: `net = gross / (1 + vat_rate/100)` -- VAT handling logika átírva: ha `vat_rate` adott és `amount_net` nincs, net kiszámítása gross-ból -- Ha `amount_net` és `vat_rate` is adott, `vat_rate` felülírva a back-számított értékkel -- Ha egyik sincs, `amount_net = amount_gross`, `vat_rate = 0` - -**5. ENDPOINT - [`assets.py`](backend/app/api/v1/endpoints/assets.py):** -- `create_maintenance_record`: `cost` mező gross-ként kezelve, `amount_gross` és `amount_net` is a cost értékkel töltve -- `create_asset_event`: `cost_amount` gross-ként kezelve -- `list_asset_costs`: response-ban `amount_gross` az `amount_net` előtt - -### ✅ Verifikáció -- Python szintaxis ellenőrzés: ✅ 5/5 fájl OK -- Sync engine: ✅ 1087/1087 OK, 0 fix, 0 extra -- Unit math tesztek: ✅ 5/5 passed (Gross=12700→Net=10000 27% VAT, Gross=10500→Net=10000 5% VAT, stb.) -- E2E API tesztek: ✅ 3/3 passed (Gross only, Gross+27%, Gross+5%) -- A teljes Gross-first matematika helyesen működik: a bruttó összeg a Source of Truth, a nettó és ÁFA visszaszámolva - -## 2026-06-18 - P0 Frontend Cost Payload Audit & Fix (Gross-First Compliance) - -### 🎯 Cél -A backend már szigorúan Gross-first rendszer a pénzügyi adatokra, de a frontend űrlapok még `amount_net`-et küldtek a payloadban, ami `amount_gross: null`-t eredményezett az adatbázisban. Teljes audit és javítás. - -### 🔧 Változtatások - -**1. FRONTEND - [`cost.ts`](frontend/src/stores/cost.ts:8):** -- `AddExpensePayload` interface: `amount_net` → `amount_gross` (elsődleges mező) -- `ExpenseResponse` interface: `amount_net` → `amount_gross` -- `addExpense()` body builder: `amount_net: payload.amount_net` → `amount_gross: payload.amount_gross` - -**2. FRONTEND - [`ComplexExpenseModal.vue`](frontend/src/components/dashboard/ComplexExpenseModal.vue:268):** -- Payload `amount_net: form.amount` → `amount_gross: form.amount` - -**3. FRONTEND - [`SimpleFuelModal.vue`](frontend/src/components/dashboard/SimpleFuelModal.vue:193):** -- Payload `amount_net: form.amount` → `amount_gross: form.amount` - -**4. BACKEND - [`asset_cost.py`](backend/app/schemas/asset_cost.py:27):** -- `@model_validator(mode='before')` safety net hozzáadva: ha a bejövő JSON `amount_net`-et tartalmaz, de `amount_gross` hiányzik, automatikusan átmásolja az `amount_net` értékét az `amount_gross`-ba. Ez véd a régebbi cache-elt böngészők ellen. - -### ✅ Verifikáció -- Vite build: ✅ Sikeres (216 module transformed, 0 error) -- Backend schema import: ✅ Sikeres -- Safety net tesztek: ✅ 3/3 passed - - `amount_net` only → `amount_gross` auto-copy: ✅ - - Both fields present → no interference: ✅ - - `amount_gross` only → no change: ✅ - -### 🔍 Utólagos Adatjavítás (Data Fix) -A felhasználó jelentése alapján az UOK795 (Honda CB1000R) járműnél az utolsó 2 rögzített költség 0 Ft-ot mutatott. Vizsgálat után kiderült: - -**Gyökér ok:** A P0 audit előtt rögzített rekordoknál a frontend `amount_net`-et küldött, de a backend `amount_gross`-t 0-ra állította (a `validate_gross_first` `mode='after'` validátor `None` → `Decimal("0")`-ra állította a hiányzó gross mezőt). - -**Érintett rekordok (országosan 2 db):** -1. `3468e81a` - UOK795 (Honda CB1000R) - 12 000 Ft tankolás → `amount_gross` javítva 0→12000 -2. `ea0395ea` - QWE123 (APRILIA af1) - 4 000 Ft → `amount_gross` javítva 0→4000 - -**Javítás:** `UPDATE vehicle.asset_costs SET amount_gross = amount_net, vat_rate = 0.00 WHERE amount_gross=0 AND amount_net>0` - -## 2026-06-18 - Jogosultságkezelés Audit (Permission System Audit) - -### 🎯 Cél -Teljes körű audit a jogosultsági szintek kezeléséről, kiterjedve az adatbázis táblákra, a backend kódra és a frontend rétegre. - -### 🔍 Vizsgált Területek -1. **Adatbázis (6 tábla):** identity.users, fleet.org_roles, fleet.organization_members, system.system_parameters, system.pending_actions, system.subscription_tiers -2. **Backend:** UserRole enum, DEFAULT_RANK_MAP, RBAC osztály, get_current_admin, scoped RBAC, Dual Control (SecurityService), JWT token -3. **Frontend:** authStore.ts (UserRole típus), Router Guard - -### 🔴 Feltárt Kritikus Problémák -- **P1:** fleet.org_roles tábla ÜRES - dinamikus szerepkör rendszer nem seedelt -- **P2:** DEFAULT_RANK_MAP inkonzisztens a UserRole enum-mal (6 rang soha nem kerül kiosztásra) -- **P3:** Enum duplikáció az adatbázisban (kis/nagybetű variánsok) -- **P4:** Frontend/backend UserRole eltérés (5 vs 10 role) -- **P5:** parameter_scope enum 3 különböző sémában definiálva - -### 📄 Dokumentáció -- [`docs/permission_system_audit_2026-06-18.md`](docs/permission_system_audit_2026-06-18.md) - Teljes audit jelentés Mermaid diagrammal - ---- - -## 2026-06-18 - RBAC Phase 1: Foundation & Seeding (P0 Critical) - -### 🎯 Cél -RBAC Phase 1 migráció Fine-Grained, Capability-Driven architektúrára. System-level (UserRole) és Organization-level (OrgUserRole) szerepkörök szétválasztása, SYSTEM_CAPABILITIES_MATRIX létrehozása, fleet.org_roles tábla seedelése. - -### 🔧 Változtatások - -**1. [`UserRole`](backend/app/models/identity/identity.py:24) enum tisztítás:** -- 10 régi érték helyett 6 system-level role: `SUPERADMIN, ADMIN, MODERATOR, SALES_REP, SERVICE_MGR, USER` -- Eltávolítva: `region_admin, country_admin, sales_agent, service_owner, fleet_manager, driver` -- Hozzáadva: `SALES_REP, SERVICE_MGR` -- Értékek UPPERCASE-re változtatva - -**2. [`SYSTEM_CAPABILITIES_MATRIX`](backend/app/core/capabilities.py:1) létrehozása:** -- Új fájl: `backend/app/core/capabilities.py` -- `Capability` osztály konstansokkal (~26 capability key) -- `SYSTEM_CAPABILITIES_MATRIX` dict minden role-hoz boolean capability mátrix -- Segédfüggvények: `get_capabilities_for_role()`, `role_has_capability()` - -**3. [`OrgUserRole`](backend/app/models/marketplace/organization.py:27) enum frissítés:** -- Új értékek: `OWNER, ADMIN, ACCOUNTANT, DRIVER, VIEWER` -- Eltávolítva: `MANAGER, MEMBER, AGENT` - -**4. [`DEFAULT_RANK_MAP`](backend/app/core/security.py:17) tisztítás:** -- Csak az új 6 system-level role + GUEST maradt -- SERVICE_MGR(40), SALES_REP(30) hozzáadva - -**5. Függő kódok frissítése:** -- [`deps.py`](backend/app/api/deps.py:106) - `check_resource_access()` és `get_current_admin()` frissítve -- [`rbac.py`](backend/app/core/rbac.py:7) - `UserRole.SUPERADMIN` referencia javítva -- [`billing_engine.py`](backend/app/services/billing_engine.py:37) - `RBAC_DISCOUNTS` tisztítva - -**6. PostgreSQL enum migráció:** -- [`migrate_rbac_phase1_enums.py`](backend/app/scripts/migrate_rbac_phase1_enums.py) - CREATE TYPE → ALTER COLUMN → DROP TYPE minta -- Régi értékek mapelése: `superadmin→SUPERADMIN, admin→ADMIN, moderator→MODERATOR, sales_agent→SALES_REP, user/service_owner/fleet_manager/driver→USER` -- ✅ Sikeres: `identity.userrole` enum 6 értékkel, meglévő user role-ok helyesen átkonvertálva - -**7. [`seed_org_roles.py`](backend/app/scripts/seed_org_roles.py) - fleet.org_roles seedelés:** -- 5 org role: OWNER(100), ADMIN(80), ACCOUNTANT(60), DRIVER(40), VIEWER(20) -- Minden role JSON permissions objektummal -- ✅ Sikeres: 5 rekord beszúrva - -### ✅ Verifikáció -- Schema sync: **1087 items OK, 0 fixed, 0 shadow data** -- `identity.userrole` enum: `SUPERADMIN, ADMIN, MODERATOR, SALES_REP, SERVICE_MGR, USER` -- `fleet.org_roles`: 5 rekord (OWNER, ADMIN, ACCOUNTANT, DRIVER, VIEWER) -- Meglévő user role-ok: `USER, SUPERADMIN, ADMIN` - helyesen konvertálva - -## 2026-06-18 - P0 EMERGENCY: Superadmin User ID 1 Recreated from Scratch - -### 🎯 Cél -A hard-deletelt User ID 1 rekord fizikai újraélesztése: SUPERADMIN joggal, a meglévő Person ID 1-hez kapcsolva, és a Profibot Test Fleet (Organization ID 15) garázshoz OWNER szerepkörrel hozzárendelve. - -### 🔧 Változtatások - -**1. Jelszó hash generálás:** -- [`security.py`](backend/app/core/security.py:14): `get_password_hash('Superadmin123!')` használatával bcrypt hash generálva -- Hash: `$2b$12$ozXD07/qiFl4eKg.RbadDeGcnMukY3tOpBPEgOza7VsqcbuUW7iq2` -- Verifikáció: `verify_password('Superadmin123!', hash)` → `True` - -**2. Email konfliktus feloldása:** -- User ID 29 (korábbi `superadmin@profibot.hu`) átnevezve → `superadmin_old@profibot.hu` - -**3. User ID 1 létrehozása (`identity.users`):** -- `INSERT INTO identity.users (id=1, email='superadmin@profibot.hu', role='SUPERADMIN', person_id=1, ...)` -- Kapcsolat a Person ID 1-hez (Super Admin személy) - -**4. Garázs hozzárendelés (`fleet.organization_members`):** -- `INSERT INTO fleet.organization_members (organization_id=15, user_id=1, person_id=1, role='OWNER', ...)` -- Organization: "Profibot Test Fleet" (ID 15) - -### ✅ Verifikáció -- `identity.users WHERE id=1`: `email=superadmin@profibot.hu, role=SUPERADMIN, person_id=1, is_active=true` -- `fleet.organization_members WHERE user_id=1`: `org_id=15 (Profibot Test Fleet), role=OWNER, status=active` -- `identity.persons WHERE id=1`: `last_name=Admin, first_name=Super` (meglévő személy) -- Jelszó hash verifikáció: `True` -- Belépési adatok: `superadmin@profibot.hu` / `Superadmin123!` - -## 2026-06-18: P0 CRITICAL DEBUG - 500 Internal Server Error on POST /api/v1/auth/login (UserRole UPPERCASE migration fix) - -### 🎯 Cél -A Phase 1 RBAC Enum UPPERCASE migráció után a `/api/v1/auth/login` endpoint 500-as hibát dobott `LookupError: 'ADMIN' is not among the defined enum values` üzenettel. A régi konténer cache-elt SQLAlchemy metaadatokat használt, és a `billing_engine.py`-ben lévő `UserRole.user` (kisbetűs) hivatkozás miatt az új konténer sem indult el. - -### 🔧 Változtatások (25 javítás 14 fájlban) - -**Kritikus javítások (startup-blokkoló):** -- [`backend/app/services/billing_engine.py:70`](backend/app/services/billing_engine.py:70) — `UserRole.user` → `UserRole.USER` (2 helyen) -- [`backend/app/services/social_auth_service.py:33`](backend/app/services/social_auth_service.py:33) — `UserRole.user` → `UserRole.USER` -- [`backend/app/services/search_service.py:41`](backend/app/services/search_service.py:41) — `UserRole.superadmin, UserRole.admin` → `UserRole.SUPERADMIN, UserRole.ADMIN` -- [`backend/app/services/auth_service.py:110`](backend/app/services/auth_service.py:110) — `UserRole.user` → `UserRole.USER` -- [`backend/app/api/v1/endpoints/system_parameters.py:125`](backend/app/api/v1/endpoints/system_parameters.py:125) — `UserRole.superadmin, UserRole.admin` → `UserRole.SUPERADMIN, UserRole.ADMIN` -- [`backend/app/schemas/admin.py:23`](backend/app/schemas/admin.py:23) — `UserRole.admin, UserRole.superadmin` → `UserRole.ADMIN, UserRole.SUPERADMIN` -- [`backend/app/api/v1/endpoints/finance_admin.py:27`](backend/app/api/v1/endpoints/finance_admin.py:27) — `UserRole.superadmin, UserRole.admin` → `UserRole.SUPERADMIN, UserRole.ADMIN` -- [`backend/app/api/v1/endpoints/admin.py:195`](backend/app/api/v1/endpoints/admin.py:195) — `UserRole.superadmin` → `UserRole.SUPERADMIN` (2 helyen) -- [`backend/app/api/v1/endpoints/security.py:39`](backend/app/api/v1/endpoints/security.py:39) — `UserRole.admin, UserRole.superadmin` → `UserRole.ADMIN, UserRole.SUPERADMIN` (5 helyen) -- [`backend/app/core/security.py:56`](backend/app/core/security.py:56) — `DEFAULT_RANK_MAP` már helyes (UPPERCASE kulcsok) -- [`backend/app/tests/e2e/test_admin_security.py:21`](backend/app/tests/e2e/test_admin_security.py:21) — `UserRole.user` → `UserRole.USER`, `UserRole.admin` → `UserRole.ADMIN` -- [`backend/app/scripts/seed_integration_data.py:158`](backend/app/scripts/seed_integration_data.py:158) — `UserRole.admin` → `UserRole.ADMIN`, `UserRole.superadmin` → `UserRole.SUPERADMIN` - -### ✅ Verifikáció -- `POST /api/v1/auth/login` with `admin@profibot.hu` / `Admin123!` → **200 OK** -- JWT token payload: `{"sub": "2", "role": "ADMIN", "rank": 90, "scope_level": "system", "scope_id": "2"}` -- Konténer indítás: sikeres (nem omlik össze `AttributeError`-rel) -- 3 db seed script nem javítható (EACCES jogosultsági hiba) — nem kritikus - ---- - -## 2026-06-18 - RBAC Phase 2: Capability Dependencies (Kapuőrök) - -### 🎯 Cél -P0 kritikus feladat: JSONB-alapú capability dependency-k bevezetése a hardcoded szerepkör-ellenőrzések helyett. Két új FastAPI dependency: `RequireSystemCapability` (system-level) és `RequireOrgCapability` (org-level). - -### 🔧 Változtatások - -**1. MODELL - [`organization.py`](backend/app/models/marketplace/organization.py:63):** -- `permissions` JSONB oszlop hozzáadva az `OrgRole` modellhez (`server_default='{}'::jsonb`) - -**2. DEPENDENCY - [`deps.py`](backend/app/api/deps.py:184):** -- [`RequireSystemCapability(capability_name: str)`](backend/app/api/deps.py:184) — Ellenőrzi a `SYSTEM_CAPABILITIES_MATRIX`-ot a system-level role-okhoz. SUPERADMIN automatikusan átmegy. -- [`RequireOrgCapability(capability_name: str)`](backend/app/api/deps.py:221) — Lekérdezi a `fleet.org_roles.permissions` JSONB oszlopot. Fallback: `OrganizationMember.permissions`. SUPERADMIN bypass. - -**3. EXPENSES - [`expenses.py`](backend/app/api/v1/endpoints/expenses.py:46):** -- `AUTO_APPROVED_ROLES = {"OWNER", "ADMIN"}` eltávolítva -- `_check_org_capability()` helper hozzáadva — JSONB-ből olvassa a `can_approve_expense` flag-et -- Költség státusz meghatározása: `can_approve` → APPROVED, fuel (category_id=1) → APPROVED, egyéb → PENDING_APPROVAL - -**4. ASSETS - [`assets.py`](backend/app/api/v1/endpoints/assets.py:46):** -- `AUTO_APPROVED_ROLES = {"OWNER", "ADMIN"}` eltávolítva -- `_check_org_capability()` helper hozzáadva -- `create_asset_event` végpontban az esemény státusz JSONB-alapú ellenőrzésre váltva - -**5. SEED - [`seed_org_roles.py`](backend/app/scripts/seed_org_roles.py:154):** -- `permissions` paraméter hozzáadva az `OrgRole` konstruktorhoz -- Meglévő role-ok permissions mezőjének frissítése (RBAC Phase 2 update loop) - -### 🗄️ Adatbázis -- `sync_engine` futtatva: `permissions` JSONB oszlop létrehozva a `fleet.org_roles` táblában -- 5 org role permissions adata feltöltve: OWNER, ADMIN, ACCOUNTANT, DRIVER, VIEWER - -### ✅ Verifikáció -- Backend indítás: sikeres (nincs import error) -- `POST /api/v1/expenses/` ADMIN userrel → **201 CREATED**, `expense_status: "APPROVED"` (mert ADMIN rendelkezik `can_approve_expense: True` képességgel) -- `fleet.org_roles` JSONB permissions: mind az 5 role helyesen tárolva - -## 2026-06-18: RBAC Phase 3 - Frontend Reactivity & UI Capabilities Integration - -### 🎯 Cél -P0 kritikus feladat: Frontend reaktivitás javítása (F5 Bug) és UI képesség-alapú megjelenítés bevezetése. - -### 🔧 Változtatások - -**Task 1 - F5 Bug javítás (Direct Mutation / Explicit Fetch pattern):** -- [`CostsActionsCard.vue`](frontend/src/components/dashboard/CostsActionsCard.vue:110): `cost-saved` event emit hozzáadva, ami a költség mentése után értesíti a szülőt -- [`DashboardView.vue`](frontend/src/views/DashboardView.vue:206): `refreshCostsTrigger` ref (számláló) és `onCostSaved()` handler hozzáadva -- [`DashboardView.vue`](frontend/src/views/DashboardView.vue:130): `:refresh-trigger="refreshCostsTrigger"` prop átadva a VehicleDetailModal-nak -- [`VehicleDetailModal.vue`](frontend/src/components/vehicle/VehicleDetailModal.vue:778): `refreshTrigger` prop és `watch` hozzáadva a költségek újratöltéséhez - -**Task 2 - Backend /users/me exposure:** -- [`schemas/user.py`](backend/app/schemas/user.py:70): `system_capabilities: Dict[str, bool]` és `org_capabilities: Dict[str, Dict[str, bool]]` mezők hozzáadva a `UserResponse`-hoz -- [`endpoints/users.py`](backend/app/api/v1/endpoints/users.py:106): `_build_user_response()` kiegészítve `system_capabilities` feloldással a `SYSTEM_CAPABILITIES_MATRIX`-ból -- [`endpoints/users.py`](backend/app/api/v1/endpoints/users.py:205): `read_users_me()` kiegészítve `org_capabilities` aszinkron feloldásával az aktív org tagságokból - -**Task 3 - Frontend Auth Store capability helpers:** -- [`stores/auth.ts`](frontend/src/stores/auth.ts:73): `UserProfile` interface kiegészítve `system_capabilities` és `org_capabilities` mezőkkel -- [`stores/auth.ts`](frontend/src/stores/auth.ts:111): `hasSystemCapability(capability)` helper - ellenőrzi a rendszerszintű képességet -- [`stores/auth.ts`](frontend/src/stores/auth.ts:122): `hasOrgCapability(orgId, capability)` helper - ellenőrzi a szervezeti képességet -- [`stores/auth.ts`](frontend/src/stores/auth.ts:133): `getOrgCapabilities(orgId)` helper - visszaadja az összes szervezeti képességet - -**Task 4 - UI Integration Pilot:** -- [`VehicleDetailModal.vue`](frontend/src/components/vehicle/VehicleDetailModal.vue:741): "Szerkesztés" gomb `v-if`-je kiegészítve `authStore.hasOrgCapability(orgId, 'can_approve_expense')` ellenőrzéssel - -### ✅ Verifikáció -- Backend Python szintaxis: `user.py` és `users.py` hibátlanul lefordul -- Frontend TypeScript: `vue-tsc --noEmit` sikeres (csak pre-existing `.vue.js` file hibák) -- Event propagation chain teljes: Cost modal → CostsActionsCard → DashboardView → VehicleDetailModal - -## 2026-06-18 - P0 Legacy Garage Migration (RBAC Phase 3) - -### 🎯 Cél -Adatbázis backfill szkript elkészítése és futtatása, amely a legacy `person_id`-alapú ownership struktúrát áttelepíti az új `organization_id`/`organization_members` RBAC struktúrába. Három feladat: (A) hiányzó személyes garázsok létrehozása, (B) userek bindolása a garázsukhoz OWNER role-lal, (C) gazdátlan járművek megmentése. - -### 🔧 Változtatások - -**1. BACKEND - [`migrate_legacy_garages.py`](backend/app/scripts/migrate_legacy_garages.py:1):** -- **Task A:** Lekérdezi a `identity.users` táblában lévő usereket, akiknek nincs `INDIVIDUAL` típusú személyes garázsuk (`fleet.organizations`). Létrehozza a garázst minden szükséges oszloppal. -- **Task B:** Minden userhez hozzárendeli a személyes garázsát `organization_members` táblában `OWNER` szerepkörrel. Frissíti a `scope_id` mezőt, ha az NULL. -- **Task C:** Megkeresi a gazdátlan járműveket (`vehicle.assets` táblában ahol `owner_person_id` kitöltött, de `current_organization_id` vagy `owner_org_id` NULL), és átvezeti őket a tulajdonos személyes garázsába. -- **Technikai részletek:** `asyncpg`-t használ közvetlen adatbázis kapcsolattal. Dinamikus schema discovery (`information_schema.columns`) a NOT NULL constraint-ek kezelésére. - -**2. BACKEND - [`verify_migration.py`](backend/app/scripts/verify_migration.py:1):** -- Verifikációs szkript, amely a migráció után lekérdezi a kulcs metrikákat. - -### ✅ Eredmény -- **6 új személyes garázs** létrehozva (Task A) -- **16 member** hozzáadva `organization_members`-hez (Task B) -- **10 scope_id** frissítve (Task B) -- **1 gazdátlan jármű** megmentve (Task C) -- **0 user** maradt személyes garázs nélkül -- **0 user** NULL scope_id-vel -- **0 orphaned jármű** maradt -- **17** total INDIVIDUAL garázs -- **22** organization member OWNER role-lal -- **38/41** jármű organization-höz rendelve - -## 2026-06-18 - P0 Feature: Subscription & Package Assignment Bridge - -### 🎯 Cél -Subscription Tier hozzárendelés szervezetekhez (fleet.organizations) a `subscription_tier_id` FK oszlopon keresztül. Teljes admin API + frontend integráció a csomagok kezeléséhez. - -### 🔧 Változtatások - -**1. ADATBÁZIS - [`Organization`](backend/app/models/marketplace/organization.py:146):** -- `subscription_tier_id` FK oszlop hozzáadva (nullable, FK → `system.subscription_tiers.id`) -- Sync engine sikeresen lefuttatva → oszlop létrejött - -**2. CAPABILITIES - [`capabilities.py`](backend/app/core/capabilities.py:61):** -- `CAN_MANAGE_SUBSCRIPTIONS = "can_manage_subscriptions"` új capability -- SUPERADMIN és ADMIN mátrixba bevéve - -**3. API ENDPOINT - [`organizations.py`](backend/app/api/v1/endpoints/organizations.py:730):** -- `PUT /{org_id}/subscription` végpont `RequireSystemCapability(Capability.CAN_MANAGE_SUBSCRIPTIONS)` RBAC-cal -- `SubscriptionAssignIn` Pydantic schema a body-hoz - -**4. BILLING ENGINE - [`billing_engine.py`](backend/app/services/billing_engine.py:805):** -- `upgrade_org_subscription()` függvény: beállítja a `subscription_tier_id`-t, frissíti a `subscription_plan` és `base_asset_limit` mezőket, létrehozza az `OrganizationSubscription` audit rekordot - -**5. QUOTA ENGINE - [`evidence.py`](backend/app/api/v1/endpoints/evidence.py:17):** -- scan-registration végpont most az org `subscription_tier_id` → `SubscriptionTier.rules.allowances.max_vehicles` értéket olvassa -- Fallback: `org.base_asset_limit` ha nincs tier hozzárendelve - -**6. SCHEMA - [`organization.py`](backend/app/schemas/organization.py:66):** -- `subscription_tier_id: Optional[int]` hozzáadva `OrganizationUpdate` és `OrganizationResponse` modellekhez - -**7. FRONTEND TYPES - [`organization.ts`](frontend/src/types/organization.ts:5):** -- `subscription_tier_id?: number | null` hozzáadva `OrganizationItem` interfészhez - -**8. FRONTEND UI - [`OrganizationSettingsModal.vue`](frontend/src/components/organization/OrganizationSettingsModal.vue:1):** -- Subscription tier dropdown (select + assign gomb) -- `availableTiers` betöltése `GET /admin/packages/`-ből -- `assignSubscription()` hívás `PUT /organizations/{id}/subscription`-ra - -### ✅ Verifikáció -- `subscription_tier_id` oszlop létezik (`information_schema` ellenőrizve) -- Org 45: `corp_premium_plus_v1` (tier_id=17, max_vehicles=50) ✅ -- Org 49: `corp_premium_v1` (tier_id=16, max_vehicles=20) ✅ -- 8 subscription tier elérhető a `system.subscription_tiers` táblában - -## 2026-06-18 - P0 Critical: Admin Button Fix & Orphaned Vehicle Rescue - -### 🎯 Cél -5-lépéses P0 kritikus feladat: (1) Törött Admin/Vezérlőpult gomb javítása a frontenden, (2) Adatbázis felderítés: gazdátlan járművek keresése, (3) Admin felhasználó garázsainak ellenőrzése, (4) Gazdátlan járművek megmentése (UPDATE), (5) Verifikáció és jelentés. - -### 🔧 Változtatások - -**1. FRONTEND - [`auth.ts`](frontend/src/stores/auth.ts:97):** -- **Root Cause:** A `ModeSwitcher.vue` a `stores/auth`-ból importálja az `authStore.isAdmin`-et, de a store-ból hiányzott az `isAdmin` computed getter. Mindig `undefined`-et adott vissza, így a gomb sosem jelent meg. -- **Fix:** `isAdmin` computed property hozzáadva, amely ellenőrzi a `superadmin`, `admin`, `region_admin`, `country_admin`, `moderator` szerepköröket (kisbetűs összehasonlítással). - -**2. ADATBÁZIS - Gazdátlan járművek felderítése:** -- **3 db orphaned vehicle** találva: TEST-API-01, ABB112, ABC-123 (mind `archived` státuszú, `owner_person_id = NULL`, `current_organization_id = NULL`) -- **Admin user** (admin@profibot.hu, user_id=2, person_id=2, role=ADMIN) nem rendelkezik Corporate (fleet_owner/business) garázzsal OWNER joggal - csak "Admin Garázsa" (Org 67, individual típus) -- Admin 17 járműve már helyesen a Test Company (Org 1, fleet_owner) alá van rendelve - -**3. ADATBÁZIS - Rescue UPDATE:** -- 3 gazdátlan jármű átvezetve az Admin Garázsába (Org 67): `owner_person_id=2, owner_org_id=67, current_organization_id=67` -- Parancs: `docker compose exec -T shared-postgres psql ... UPDATE vehicle.assets SET ... WHERE current_organization_id IS NULL AND owner_person_id IS NULL` -- Eredmény: `UPDATE 3` - -### ✅ Verifikáció -- `SELECT ... WHERE current_organization_id IS NULL` → **üres eredmény** (0 gazdátlan jármű) ✅ -- 3 rescued vehicle: TEST-API-01, ABB112, ABC-123 → Admin Garázsa (Org 67) ✅ -- Admin 17 egyéb járműve: Test Company (Org 1) alatt maradt ✅ - -## 2026-06-18 - P0 CRITICAL: Fix Admin Panel Case Sensitivity (SUPERADMIN/ADMIN) - -### 🎯 Cél -A backend UPPERCASE role értékei (SUPERADMIN, ADMIN) és a frontend lowercase összehasonlításai közötti mismatch javítása. - -### 🔧 Változtatások -- [`frontend/src/stores/auth.ts`](frontend/src/stores/auth.ts:99) — `isAdmin` getter: lowercase `[superadmin,admin,...]` → UPPERCASE `[SUPERADMIN,ADMIN,MODERATOR]` + `role.toUpperCase()` -- [`frontend/src/router/index.ts`](frontend/src/router/index.ts:131) — Router guard: ugyanez a javítás a `requiresAdmin` meta ellenőrzésben -- [`frontend/src/stores/authStore.ts`](frontend/src/stores/authStore.ts:27) — Secondary auth store `isAdmin`/`isSuperAdmin` getterek: `role.toUpperCase()` -- [`frontend/src/components/header/HeaderProfile.vue`](frontend/src/components/header/HeaderProfile.vue:145) — Inline `isAdmin` computed: lowercase → UPPERCASE + `toUpperCase()` -- [`frontend/src/views/admin/AdminUsersView.vue`](frontend/src/views/admin/AdminUsersView.vue:130,299) — Két inline `=== superadmin` check → `=== SUPERADMIN` + `toUpperCase()` - -### ✅ Verifikáció -- Backend admin ping endpoint: `role: "ADMIN"` (UPPERCASE) ✅ -- Frontend Vite HMR: minden módosított fájl újratöltve ✅ -- Admin felület minden ponton (store getter, router guard, header button, users view) case-insensitive módon ellenőrzi a SUPERADMIN/ADMIN szerepköröket ✅ - -## 2026-06-18 - P0 Critical: Precise Subscription Backfill & Legacy Cleanup - -### 🎯 Cél -Admin garázsok VIP csomag hozzárendelése, tömeges free tier fallback, legacy string oszlopok kivezetése a kvóta logikából. - -### 🔧 Változtatások -1. **Adatbázis backfill** ([`backend/app/scripts/p0_subscription_backfill.py`](backend/app/scripts/p0_subscription_backfill.py)): - - VIP: Admin Garázsa (org_id=67) → `private_test_v01` (tier_id=19, max_vehicles=100) ✅ - - VIP: Test Company (org_id=1) → `org_test_v01` (tier_id=20, max_vehicles=100) ✅ - - Free Tier Fallback: 14 individual org → `private_free_v1`, 13 other org → `corp_free_v1` ✅ - - Eredmény: 31/31 org assigned, 0 NULL ✅ - -2. **Kvóta Motor refaktor** ([`backend/app/services/asset_service.py`](backend/app/services/asset_service.py:491)): - - `get_user_vehicle_limit()`: Eltávolítva a legacy `subscription_plan` string lookup - - Single Source of Truth: `SubscriptionTier.rules['allowances']['max_vehicles']` JSONB - - Fallback lánc: user subscription → org subscription tier → org.base_asset_limit → config - -3. **Dokumentum helper cleanup** ([`backend/app/api/v1/endpoints/documents.py`](backend/app/api/v1/endpoints/documents.py:90)): - - `_check_premium_or_admin()`: Eltávolítva a `subscription_plan` string alapú premium check - -### ✅ Verifikáció -- Schema szinkron: sync_engine → 1089/1089 OK ✅ -- 0 org with NULL subscription_tier_id ✅ -- Distribution: private_free_v1(14), corp_free_v1(13), corp_premium_v1(1), private_test_v01(1), corp_premium_plus_v1(1), org_test_v01(1) ✅ - -## 2026-06-18 - P0 CRITICAL: PATCH Organization 500 Error Fix - -### 🎯 Cél -A `PATCH /api/v1/organizations/{id}` végpont 500 Internal Server Error hibájának javítása, amely a subscription backfill után jelentkezett. - -### 🔍 Root Cause Analysis - -**Kiváltó ok #1:** PostgreSQL ENUM típus mismatch. - -A `fleet.organization_members.role` oszlop az adatbázisban `fleet.orguserrole` ENUM típusként volt definiálva, de a SQLAlchemy modellben [`OrganizationMember.role`](backend/app/models/marketplace/organization.py:246) `String(50)`-ként. Amikor a `PATCH /organizations/{id}` végpont [`update_organization`](backend/app/api/v1/endpoints/organizations.py:672) függvénye lefutott, az SQLAlchemy a következő SQL-t generálta: - -```sql -WHERE fleet.organization_members.role IN ('OWNER', 'ADMIN') -``` - -PostgreSQL nem tud implicit módon összehasonlítani egy ENUM típust (`fleet.orguserrole`) egy string literállal (`character varying`), ezért dobta: - -``` -operator does not exist: fleet.orguserrole = character varying -HINT: No operator matches the given name and argument types. You might need to add explicit type casts. -``` - -**Kiváltó ok #2:** Hiányzó `OrganizationMember` rekord az `owner_id` alapú tulajdonosoknak. - -A subscription backfill során számos szervezetnél az `owner_id` mező be lett állítva, de a `fleet.organization_members` táblába nem került be a megfelelő OWNER szerepkörű rekord. A PATCH végpont csak a `OrganizationMember` táblában keresett, így a jogos tulajdonos is 403-as hibát kapott. - -**Érintett fájlok:** -- [`backend/app/models/marketplace/organization.py`](backend/app/models/marketplace/organization.py:246) - `OrganizationMember.role` oszlop típusa -- [`backend/app/api/v1/endpoints/organizations.py`](backend/app/api/v1/endpoints/organizations.py:668) - A PATCH végpont RBAC ellenőrzése - -### 🔧 Javítások - -**1. MODELL JAVÍTÁS** ([`organization.py`](backend/app/models/marketplace/organization.py:246)): -- `OrganizationMember.role` oszlop típusa `String(50)` → `PG_ENUM(OrgUserRole, name="orguserrole", schema="fleet", create_type=False)`-re változtatva -- `create_type=False` paraméter biztosítja, hogy nem próbáljuk újra létrehozni a már meglévő ENUM típust - -**2. VÉGPONT JAVÍTÁS** ([`organizations.py`](backend/app/api/v1/endpoints/organizations.py:668)): -- Az RBAC ellenőrzés kiegészítve: ha a user nem található `OrganizationMember`-ként, de `Organization.owner_id`-ként szerepel, akkor is engedélyezve van a módosítás -- A szervezet lekérése előrébb került (az RBAC ellenőrzés előtt), hogy elérhető legyen az `owner_id` mező -- Ezzel elkerültük a duplikált SQL lekérdezést is - -### ✅ Verifikáció -- `PATCH /api/v1/organizations/44` → **200 OK** ✅ (korábban 500, majd 403) -- Nincs több `UndefinedFunctionError` a logokban ✅ -- Az SQLAlchemy által generált SQL helyesen kezeli az ENUM összehasonlítást ✅ -- A tulajdonos (owner_id) sikeresen tudja módosítani a szervezet adatait ✅ - -## 2026-06-18: Address fields missing from OrganizationResponse and GET /my (#3-as probléma) - -### 🔍 Root Cause Analysis - -A PATCH `/api/v1/organizations/{id}` végpont 200 OK-val tért vissza, és az adatok az adatbázisba is bekerültek, de a frontend UI-ban nem jelentek meg a cím adatok. A probléma **három rétegben** jelentkezett: - -**1. Backend `OrganizationResponse` séma hiányossága** ([`organization.py`](backend/app/schemas/organization.py:70)): -- A `OrganizationResponse` Pydantic osztály nem tartalmazta a cím mezőket (`address_zip`, `address_city`, `address_street_name`, `address_street_type`, `address_house_number`, `address_hrsz`) -- A PATCH végpont `response_model=OrganizationResponse`-et használ, így a válaszból kimaradtak a cím adatok - -**2. Backend `GET /my` végpont hiányossága** ([`organizations.py`](backend/app/api/v1/endpoints/organizations.py:210)): -- A `get_my_organizations` függvény által visszaadott dictionary-ből hiányoztak a cím mezők -- A frontend a PATCH után `authStore.fetchMyOrganizations()`-t hív, ami ezt a végpontot használja - -**3. Frontend `OrganizationItem` típus és `onMounted` hiányossága** ([`OrganizationSettingsModal.vue`](frontend/src/components/organization/OrganizationSettingsModal.vue:222)): -- A TypeScript `OrganizationItem` interfészből hiányoztak a cím mezők -- Az `onMounted` hook nem töltötte be a cím mezőket a `form` objektumba - -**További probléma:** A `OrganizationUpdate` séma tartalmazott olyan mezőket (`address_stairwell`, `address_floor`, `address_door`), amelyek nem léteznek az `Organization` SQLAlchemy modellben. Ezeket a PATCH végpont csendben figyelmen kívül hagyta, de a `GET /my` végpont AttributeError-t dobott volna rájuk. - -### 🔧 Javítások - -**1. `OrganizationResponse` séma bővítése** ([`organization.py`](backend/app/schemas/organization.py:88)): -- Hozzáadva: `address_zip`, `address_city`, `address_street_name`, `address_street_type`, `address_house_number`, `address_hrsz` -- Eltávolítva a nem létező mezők: `address_stairwell`, `address_floor`, `address_door` - -**2. `OrganizationUpdate` séma tisztítása** ([`organization.py`](backend/app/schemas/organization.py:54)): -- Eltávolítva a nem létező mezők: `address_stairwell`, `address_floor`, `address_door` - -**3. `GET /my` végpont bővítése** ([`organizations.py`](backend/app/api/v1/endpoints/organizations.py:227)): -- Hozzáadva a cím mezők a visszaadott dictionary-hez - -**4. Frontend `OrganizationItem` típus bővítése** ([`organization.ts`](frontend/src/types/organization.ts:33)): -- Hozzáadva a cím mezők az interfészhez - -**5. Frontend `OrganizationSettingsModal.vue` javítása** ([`OrganizationSettingsModal.vue`](frontend/src/components/organization/OrganizationSettingsModal.vue:222)): -- `form` objektum bővítve a cím mezőkkel -- `onMounted` hook kiegészítve a cím mezők betöltésével (mindkét ágban) - -### ✅ Verifikáció -- `GET /api/v1/organizations/my` → **200 OK**, tartalmazza a cím mezőket ✅ -- `PATCH /api/v1/organizations/{id}` → **200 OK**, a válasz tartalmazza a cím mezőket ✅ -- A cím adatok elérhetők a frontend `OrganizationSettingsModal` form-jában ✅ - -## 2026-06-18 - P0: Subscription Stacking & Ad Engine Implementation - -### 🎯 Cél -Subscription időhalmozás (stacking) implementálása `duration_days` segítségével a JSONB `rules`-ban, valamint a Native Ad Engine adatbázis séma (`marketing` schema) létrehozása. - -### 🔧 Változtatások - -**1. Pydantic Schema Bővítés - [`subscription.py`](backend/app/schemas/subscription.py:43):** -- `DurationModel`: `days` (default=30) és `allow_stacking` (default=True) mezők -- `AdPolicyModel`: `show_ads`, `ad_free_grace_days`, `max_daily_impressions` mezők -- `SubscriptionRulesModel`: `duration: Optional[DurationModel]` és `ad_policy: Optional[AdPolicyModel]` opcionális mezők - -**2. Stacking Logika - [`billing_engine.py`](backend/app/services/billing_engine.py:722):** -- `upgrade_org_subscription()`: `duration_days` kiolvasása `tier.rules`-ból, `valid_until` számítás stackinggel -- `upgrade_subscription()`: `UserSubscription` kezelés stacking logikával -- Képlet: `IF existing.valid_until > NOW → stacked = existing + duration_days ELSE fresh = NOW + duration_days` - -**3. Ad Engine Modellek - [`marketing.py`](backend/app/models/marketing.py:1):** -- 7 tábla a `marketing` sémában: `campaigns`, `creatives`, `placements`, `campaign_creatives`, `campaign_placements`, `ad_impressions`, `ad_clicks` -- String-based enumok (`CampaignStatus`, `CreativeType`, `PlacementType`) `CheckConstraint`-tel a `sync_engine` kompatibilitásért -- Priority + weighted random selection logika a hirdetés kiválasztáshoz - -**4. Seed Adatok - [`seed_packages.py`](backend/app/scripts/seed_packages.py:266):** -- Mind a 6 csomag frissítve `duration` (30 nap, `allow_stacking=true`) és `ad_policy` mezőkkel -- Free: `show_ads=true`, `max_daily_impressions=50` -- Pro: `show_ads=true`, `grace_days=3`, `max_impressions=100` -- VIP/Corporate: `show_ads=false` -- UPSERT (`INSERT ... ON CONFLICT DO UPDATE`) használata DELETE helyett FK referenciák megőrzéséért - -**5. Modell Regisztráció - [`__init__.py`](backend/app/models/__init__.py:54):** -- Marketing modellek importálva és `__all__`-hoz adva - -### ✅ Verifikáció -- `sync_engine`: 1152 elem szinkronban, 7 marketing tábla létrehozva ✅ -- `seed_packages`: 6 csomag sikeresen frissítve duration/ad_policy mezőkkel ✅ -- Pydantic validáció: `DurationModel`, `AdPolicyModel`, `SubscriptionRulesModel` hibátlan ✅ -- Stacking logika szimuláció: Fresh (30 nap), Stacked (40 nap = 10 + 30), Expired reset (30 nap) ✅ -- Gitea kártya #271 létrehozva és elindítva ✅ - -## 2026-06-18 - P0 Debug: GET /api/v1/subscriptions/public 404 Fix - -### 🎯 Cél -A frontend SubscriptionPlansView által hívott `GET /api/v1/subscriptions/public` végpont 404-es hibájának kivizsgálása és javítása. - -### 🔧 Változtatások - -**1. ROOT CAUSE - Stale container cache:** -- A [`subscriptions.py`](backend/app/api/v1/endpoints/subscriptions.py) fájl új volt (untracked `??` git statusban) -- A futó uvicorn konténer a régi modulgráfot cache-elte, nem tudott az új router-ről -- **Fix:** `docker compose restart sf_api` — a konténer újratöltése után a végpont 200 OK-val válaszolt ✅ - -**2. i18n - Missing `common.retry` key:** -- A [`hu.ts`](frontend/src/i18n/hu.ts:9) `common` szekciójába hozzáadva: `retry: 'Újrapróbálkozás'` -- A [`en.ts`](frontend/src/i18n/en.ts:9) `common` szekciójába hozzáadva: `retry: 'Retry'` - -**3. `is_public` filtering:** -- A [`get_public_subscriptions`](backend/app/api/v1/endpoints/subscriptions.py:47) végpont most már szűri a csomagokat a `rules.lifecycle.is_public` JSONB mező alapján -- Csak azok a tier-ek kerülnek visszaadásra, ahol `is_public=True` (alapértelmezett: True) - -### ✅ Verifikáció -- `GET /api/v1/subscriptions/public` → **200 OK**, 10 tier visszaadva -- Minden tier `is_public=True` értékkel rendelkezik -- A frontend i18n kulcsok (`common.retry`) mindkét nyelven elérhetők - -## 2026-06-18 - P0 Booster Architecture & Minimalist Subscription UI - -### 🎯 Cél -P0 architektúra frissítés: Booster kvóta rendszer, marketing adatok a subscription tier-ekhez, minimalista előfizetési kártyák és részletes modal. - -### 🔧 Változtatások - -**1. `extra_allowances` JSONB oszlop** -- [`OrganizationSubscription`](backend/app/models/core_logic.py:42) modellhez hozzáadva: `extra_allowances: Mapped[dict] = mapped_column(JSONB, server_default=text("'{}'::jsonb"), default=dict)` -- A `sync_engine` sikeresen létrehozta a `finance.org_subscriptions.extra_allowances` oszlopot - -**2. Kvóta motor frissítés** -- [`get_user_vehicle_limit()`](backend/app/services/asset_service.py:610) most beolvassa az `extra_allowances.extra_vehicles` értéket és hozzáadja a végső limithez -- [`scan_registration_document`](backend/app/api/v1/endpoints/evidence.py:43) evidence végpont is figyelembe veszi az `extra_allowances`-t a kvóta ellenőrzésnél - -**3. Marketing adatok** -- [`MarketingModel`](backend/app/schemas/subscription.py:68) Pydantic séma létrehozva `subtitle`, `badge`, `highlight_color` mezőkkel -- [`SubscriptionRulesModel`](backend/app/schemas/subscription.py:121) `marketing` mezővel bővítve -- Mind a 6 fő csomag [`seed_packages.py`](backend/app/scripts/seed_packages.py) marketing adatokkal frissítve (magyar subtitle, badge, highlight_color) - -**4. Minimalista előfizetési kártyák** -- [`SubscriptionPlansView.vue`](frontend/src/views/SubscriptionPlansView.vue) teljes átírása: csak név, badge, subtitle, ár, 3 kulcs statisztika (max_vehicles, max_garages, monthly_free_credits) megjelenítése -- Gomb szöveg: "Részletek & Vásárlás" -- `h-full` + `flex flex-col` az egyenlő kártyamagasságokért - -**5. PlanDetailsModal komponens** -- [`PlanDetailsModal.vue`](frontend/src/components/subscription/PlanDetailsModal.vue) létrehozva: Teleport modal, fejléc highlight színnel, havi/éves váltó, megtakarítás % kijelzés, 3 kulcs statisztika, szolgáltatás lista, rendelés szimuláció gomb -- i18n kulcsok hozzáadva (`hu.ts`, `en.ts`) - -### ✅ Verifikáció -- `extra_allowances` oszlop létezik: `finance.org_subscriptions.extra_allowances` JSONB, default `'{}'::jsonb` -- 6 subscription tier rendelkezik marketing adatokkal (private_free, private_pro, private_vip, corp_premium, corp_premium_plus, corp_vip) -- Minden Python fájl szintaktikailag helyes -- `seed_packages.py` sikeresen lefutott, az adatok perzisztálva - -## 2026-06-18 - P0: Multi-Region Pricing & Admin Marketing UI - -### 🎯 Cél -Multi-region pricing (pricing_zones) bevezetése a subscription rendszerbe: a backend dinamikusan feloldja a megfelelő árat a felhasználó országkódja alapján, a frontend pedig megjeleníti a feloldott árat. Admin UI-ban marketing mezők és pricing zones szerkesztő hozzáadása. - -### 🔧 Változtatások - -**1. BACKEND - Schema [`subscription.py`](backend/app/schemas/subscription.py):** -- `PricingZoneModel` osztály hozzáadva: `monthly_price`, `yearly_price`, `currency`, `credit_price` -- `pricing_zones: Optional[Dict[str, PricingZoneModel]]` mező a `SubscriptionRulesModel`-ben -- `MarketingModel` osztály hozzáadva: `subtitle`, `badge`, `highlight_color` -- `marketing: Optional[MarketingModel]` mező a `SubscriptionRulesModel`-ben -- `PublicSubscriptionTierResponse` séma: `resolved_pricing: Optional[PricingZoneModel]` mezővel - -**2. BACKEND - Seed [`seed_packages.py`](backend/app/scripts/seed_packages.py):** -- `pricing_zones()` helper függvény: DEFAULT (EUR), HU (HUF), US (USD) zónákkal -- Mind a 6 fő csomag (`private_free`, `private_pro`, `private_vip`, `corp_premium`, `corp_premium_plus`, `corp_vip`) `pricing_zones` adatokat kapott - -**3. BACKEND - Endpoint [`subscriptions.py`](backend/app/api/v1/endpoints/subscriptions.py):** -- `resolve_pricing(rules, country_code)` függvény: országkód → pricing_zones → DEFAULT → legacy pricing → None -- `get_user_country_code(current_user, db)` aszinkron függvény: aktív org country_code → person address country → "DEFAULT" -- `GET /public` végpont frissítve: `List[PublicSubscriptionTierResponse]` visszatérés, `resolved_pricing` kitöltve - -**4. FRONTEND - [`SubscriptionPlansView.vue`](frontend/src/views/SubscriptionPlansView.vue):** -- `resolved_pricing` mező a `SubscriptionPlan` interfészben -- `resolvedMonthlyPrice()`, `resolvedYearlyPrice()`, `resolvedCurrency()`, `yearlySavingsPercent()` helperek -- Dinamikus `Intl.NumberFormat` pénznem formázás (HUF=0 tizedes, más=2 tizedes) -- Havi + éves ár kijelzés megtakarítás %-kal - -**5. FRONTEND - [`PlanDetailsModal.vue`](frontend/src/components/subscription/PlanDetailsModal.vue):** -- `resolved_pricing` alapján számított `resolvedMonthlyPrice`, `resolvedYearlyPrice`, `resolvedCurrency` computed property-k -- Dinamikus pénznem formázás - -**6. FRONTEND - Admin Store [`adminPackages.ts`](frontend/src/stores/adminPackages.ts):** -- `PricingZoneModel` interfész hozzáadva -- `MarketingModel` interfész hozzáadva -- `pricing_zones` és `marketing` mezők a `SubscriptionRulesModel`-ben - -**7. FRONTEND - Admin UI [`PackageEditModal.vue`](frontend/src/components/admin/PackageEditModal.vue):** -- Marketing szekció: Alcím (Subtitle), Jelvény (Badge), Kiemelő szín (Hex color picker) -- Pricing Zones szekció: dinamikus lista zónák hozzáadásával/törlésével/szerkesztésével (country code, havi/éves ár, pénznem, kredit ár) -- `buildRules()` frissítve: marketing és pricing_zones beszúrása a rules objektumba -- `watch` handler frissítve: meglévő marketing és pricing_zones adatok betöltése szerkesztéskor - -### ✅ Verifikáció -- `sync_engine` sikeresen lefutott, a séma konzisztens -- `seed_packages.py` sikeresen lefutott, mind a 6 fő csomag rendelkezik `pricing_zones` adatokkal (HU, US, DEFAULT) -- API végpont (`GET /api/v1/subscriptions/public`) helyesen adja vissza a `resolved_pricing` mezőt -- Frontend TypeScript ellenőrzés: nincs új hiba a módosított fájlokban -- Admin UI: marketing mezők és pricing zones szerkesztő működik +- A kártyára kattintás után a böngésző a `/dashboard/vehicles/:id` (privát) vagy `/organization/:id/vehicles/:id` (céges) útvonalra navigál +- A `VehicleDetailsView.vue` betöltődik és megjeleníti az 5 tabot +- A TypeScript fordítás hibátlan (csak pre-existing `.js` fájl warningok) diff --git a/docs/i18n_duplicate_key_analysis.md b/docs/i18n_duplicate_key_analysis.md new file mode 100644 index 0000000..0f7e39f --- /dev/null +++ b/docs/i18n_duplicate_key_analysis.md @@ -0,0 +1,242 @@ +# 🌐 i18n Duplikált Kulcsok Összehasonlító Elemzése + +**Dátum:** 2026-06-19 +**Készítette:** Rendszer-Architect +**Cél:** Nyelvi modulok (i18n) kulcsainak felmérése, duplikációk azonosítása, optimalizálási javaslat + +--- + +## 1. Feltárt Fájlok Áttekintése + +Összesen **8 i18n fájl** található a projektben, két elkülönült rendszerben: + +### 1.1 Frontend (vue-i18n, TypeScript) — `frontend/src/i18n/` + +| Fájl | Sorok | Hatókör | Státusz | +|------|-------|---------|---------| +| en.ts | ~1603 | Teljes (25 szekció) | ✅ Teljes | +| hu.ts | ~1600 | Teljes (25 szekció) | ✅ Teljes | +| de.ts | ~83 | Részleges: menu, header, dashboard, zen, auth | ⚠️ Hiányos | +| ro.ts | ~83 | Részleges: menu, header, dashboard, zen, auth | ⚠️ Hiányos | +| cz.ts | ~83 | Részleges: menu, header, dashboard, zen, auth | ⚠️ Hiányos | +| sk.ts | ~83 | Részleges: menu, header, dashboard, zen, auth | ⚠️ Hiányos | + +### 1.2 Backend (statikus JSON) — `backend/static/locales/` + +| Fájl | Sorok | Hatókör | Státusz | +|------|-------|---------|---------| +| en.json | ~76 | AUTH, SENTINEL, EMAIL, ORGANIZATION | ✅ Teljes | +| hu.json | ~83 | AUTH, SENTINEL, EMAIL, ORGANIZATION | ⚠️ Aszimmetrikus | + +> **⚠️ Fontos:** A frontend és backend rendszerek **teljesen elkülönülnek**. Más kulcs-sémát használnak (dot-notation vs UPPER_SNAKE_CASE), így közöttük NINCS duplikáció. + +--- + +## 2. Frontend Kulcsok Hierarchikus Szerkezete + +A frontend i18n rendszer 25 gyökér-szekcióra tagolódik: + +``` +common (9 kulcs) — Általános (cancel, save, select, year, mileage, stb.) +subscription (11) — Előfizetések +landing (20+) — Nyitóoldal +menu (8) — Navigációs menü +garage (18) — Garázs felület +vehicleDetail (20+) — Jármű részletek +header (35+) — Fejléc +dashboard (40+) — Irányítópult +finance (25+) — Pénzügyi motor +zen (2) — Zen mód +auth (50+) — Bejelentkezés, regisztráció +profile (60+) — Felhasználói profil +kyc (20+) — KYC +verifyEmail (15+) — E-mail megerősítés +company (70+) — Cég műveletek +organization (2) — Szervezeti név +address (7) — Cím komponensek +servicebook (15+) — Szervizkönyv +vehicle (200+) — Jármű adatok +serviceFinder (30+) — Szervizkereső +provider (60+) — Szolgáltató adatok +categories (4) — Kategóriák +gamification (2) — Gamifikáció +admin.services (20+) — Admin: szolgáltatások +admin.packages (30+) — Admin: csomagok +admin.users (30+) — Admin: felhasználók +``` + +--- + +## 3. Duplikált Kulcsok Kategorizált Listája + +### 3.1 ✅ Pontos Egyezések (Biztonságosan Összevonható) + +Ezek a kulcsok **angolban és magyarban is azonos értéket** adnak vissza. Egyetlen közös kulccsá vonhatók össze. + +| # | Érintett Kulcsok | EN érték | HU érték | Előfordulás | +|---|-----------------|----------|----------|-------------| +| 1 | common.cancel / profile.cancel / servicebook.cancel | "Cancel" | "Mégse" | **3 helyen** | +| 2 | common.save / profile.save | "Save" | "Mentés" | **2 helyen** | +| 3 | common.saving / servicebook.saving / vehicle.saving / admin.services.saving / admin.packages.saving | "Saving..." | "Mentés..." | **5 helyen** | +| 4 | common.back / kyc.back / company.back | "Back" | "Vissza" | **3 helyen** | +| 5 | common.mileage / garage.mileage / vehicleDetail.mileage | "Mileage" | "Futásteljesítmény" | **3 helyen** | +| 6 | common.year / garage.year | "Year" | "Évjárat" | **2 helyen** | +| 7 | common.retry / dashboard.errorRetry | "Retry" | "Újrapróbálkozás" | **2 helyen** | +| 8 | garage.noPlate / vehicleDetail.noPlate | "No Plate" | "Nincs rendszám" | **2 helyen** | +| 9 | garage.nextService / vehicleDetail.nextService / vehicle.nextService | "Next Service" | "Következő Szerviz" | **3 helyen** | +| 10 | vehicleDetail.noData / vehicle.noData | "No data" | "Nincs adat" | **2 helyen** | +| 11 | header.subtitle / dashboard.subtitle | "Your dashboard and vehicle overview" | "Irányítópultod és járműveid áttekintése" | **2 helyen ⭐** | +| 12 | garage.backToGarage / profile.backToGarage | "Back to Garage" | "Vissza a Garázsba" | **2 helyen** | +| 13 | header.profile / profile.title | "Profile Settings" | "Profil beállítások" | **2 helyen** | +| 14 | dashboard.statusActive / company.fleetActive / admin.users.status_active | "Active" | "Aktív" | **3 helyen** | +| 15 | vehicleDetail.vin / vehicle.label_vin | "VIN (Chassis Number)" | "Alvázszám (VIN)" | **2 helyen** | +| 16 | kyc.country / company.country / profile.country | "Country" | "Ország" | **3 helyen** | +| 17 | kyc.zipCode / company.zipCode / profile.zipCode | "ZIP Code" | "Irányítószám" | **3 helyen** | +| 18 | kyc.city / company.city / profile.city | "City" | "Város" | **3 helyen** | +| 19 | kyc.houseNumber / company.houseNumber / profile.houseNumber | "House Number" | "Házszám" | **3 helyen** | +| 20 | profile.stairwell / company.stairwell | "Stairwell" | "Lépcsőház" | **2 helyen** | +| 21 | profile.floor / company.floor | "Floor" | "Emelet" | **2 helyen** | +| 22 | profile.door / company.door | "Door" | "Ajtó" | **2 helyen** | +| 23 | profile.parcelId / company.parcelId | "Parcel ID" | "Helyrajzi szám" | **2 helyen** | +| 24 | kyc.streetName / company.streetName / provider.streetNameLabel | "Street Name" | "Utca neve" | **3 helyen** | +| 25 | kyc.streetType / company.streetType | "Type" | "Típus" | **2 helyen** | + +> ⭐ A **#11** (header.subtitle = dashboard.subtitle) a legszembetűnőbb: **teljesen azonos mondat**, mindkét nyelven. Egyértelműen összevonandó. + +### 3.2 ⚠️ Kis Eltéréssel (Egyedi Kontextus, Megfontolandó) + +| # | Érintett Kulcsok | EN érték | HU érték(ek) | Megjegyzés | +|---|-----------------|----------|-------------|------------| +| 26 | common.cancel vs company.cancel vs admin.services.cancel | "Cancel" | "Mégse" / "Mégsem" | "Mégsem" elütés? | +| 27 | finance.retry vs common.retry vs dashboard.errorRetry | "Retry" | "Újra" / "Újrapróbálkozás" | finance.retry rövidebb | +| 28 | vehicleDetail.vin vs vehicle.label_vin | "VIN" / "VIN (Chassis Number)" | "Alvázszám (VIN)" | EN nem azonos | +| 29 | company.streetType vs profile.streetType | "Type" / "Street Type" | "Típus" / "Közterület jellege" | Profile formalisabb | +| 30 | company.email vs provider.emailLabel | "Email" / "Email" | "E-mail" / "E-mail cím" | Provider explicitabb | +| 31 | company.phone vs provider.phoneLabel | "Phone" / "Phone" | "Telefonszám" / "Telefon" | Különböző hossz | +| 32 | dashboard.addVehicle vs menu.addVehicle | "+ Add Vehicle" / "+ Add New Vehicle" | "+ Jármű Hozzáadása" / "+ Új Jármű Hozzáadása" | Menü-ben "New" | + +### 3.3 🔄 Jármű Funkciók Átfedései + +A vehicle.features (~130), vehicle.motorcycleFeatures (~70), vehicle.lcvFeatures (~40) és vehicle.hgvFeatures (~90) között jelentős átfedés. + +#### Pontos egyezések (összevonható): + +| Kulcs | EN | HU | Érintett szekciók | +|-------|----|----|------------------| +| cruise_control | "Cruise Control" | "Tempomat" | features, motorcycleFeatures, lcvFeatures | +| alloy_wheels | "Alloy Wheels" | "Könnyűfém felni" | features, lcvFeatures | +| garage_kept | "Garage Kept" | "Garázsban tartott" | features, motorcycleFeatures | +| xenon_headlights | "Xenon Headlights" | "Xenon fényszóró" | features, lcvFeatures | +| fog_lights | "Fog Lights" | "Ködlámpa" | features, lcvFeatures | +| sunroof | "Sunroof" | "Napfénytető" | features, lcvFeatures | +| heated_seat | "Heated Seat" | "Fűthető ülés" | motorcycleFeatures, lcvFeatures | +| navigation | "Navigation (GPS)" | "Navigáció (GPS)" | features, hgvFeatures | +| apple_carplay | "Apple CarPlay" | "Apple CarPlay" | features, hgvFeatures | +| android_auto | "Android Auto" | "Android Auto" | features, hgvFeatures | +| bluetooth | "Bluetooth" | "Bluetooth" | features, hgvFeatures | +| wireless_charging | "Wireless Charging" | "Vezeték nélküli töltő" | features, hgvFeatures | +| dab_radio | "DAB+ Digital Radio" | "DAB+ digitális rádió" | features, hgvFeatures | +| premium_sound | "Premium Sound System" | "Prémium hangrendszer" | features, hgvFeatures | +| wifi_hotspot | "WiFi Hotspot" | "WiFi Hotspot" | features, hgvFeatures | +| adaptive_cruise | "Adaptive Cruise Control (ACC)" | "Adaptív tempomat (ACC)" | features, hgvFeatures | +| 360_camera | "360° Camera" | "360°-os kamera" | features, hgvFeatures | +| parking_sensors_rear | "Rear Parking Sensors" | "Hátsó parkolóérzékelők" | features, lcvFeatures | + +#### Eltérő magyar fordítás (manuális átgondolást igényel): + +| Kulcs | EN (features) | EN (másik) | HU (features) | HU (másik) | Forrás | +|-------|--------------|------------|---------------|------------|--------| +| abs | "ABS" | "ABS" | "ABS" | "ABS (blokkolásgátló)" | motorcycleFeatures | +| gps_tracker | "GPS Tracker" | "GPS Tracker" | "GPS követő" | "GPS nyomkövető" | lcvFeatures, hgvFeatures | +| alarm | "Alarm System" | "Alarm" | "Riasztórendszer" | "Riasztó" | motorcycleFeatures, lcvFeatures | +| first_owner | "First Owner" | "First Owner" | "Első tulajdonos" | "Első tulajdonostól" | motorcycleFeatures | +| service_book | "Service Book" | "Service Book" | "Szervizkönyvvel" | "Szervizkönyv" | motorcycleFeatures | +| isofix | "ISOFIX" | "ISOFIX Child Seat Anchors" | "Isofix" | "Isofix gyerekülés rögzítés" | lcvFeatures | +| tow_bar | "Tow Bar" | "Tow Bar" | "Vontatóhorog (vonóhorog)" | "Vontatóhorog" | lcvFeatures | +| usb_charging | "USB Charging" | "USB Charging" | "USB töltőcsatlakozó" | "USB töltő" | hgvFeatures | +| subwoofer | "Subwoofer" | "Subwoofer" | "Mélynyomó (Subwoofer)" | "Mélynyomó" | hgvFeatures | +| rear_entertainment | "Rear Entertainment" | "Rear Entertainment System" | "Hátsó szórakoztató rendszer" | "Hátsó szórakoztató rendszer" | hgvFeatures | +| traction_control | "Traction Control (TCS)" | "Traction Control" | "Kipörgésgátló (TCS)" | "Kipörgésgátló" | hgvFeatures | + +--- + +## 4. Backend JSON Aszimmetria + +### en.json vs hu.json + +| Kulcs | en.json | hu.json | Státusz | +|-------|---------|---------|---------| +| AUTH.ERROR.UNAUTHORIZED | ✅ "Unauthorized access" | ❌ **HIÁNYZIK** | Pótlandó hu.json-ban | +| COMMON.SAVE_SUCCESS | ❌ **HIÁNYZIK** | ✅ "Sikeres mentés!" | Pótlandó en.json-ban | +| SENTINEL.APPROVAL.REQUIRED | ❌ **HIÁNYZIK** | ✅ "A kérelem jóváhagyása szükséges" | Pótlandó en.json-ban | + +A fennmaradó ~40 kulcs mindkét fájlban azonos szerkezetű. + +--- + +## 5. Hiányos Fordítások (de, ro, cz, sk) + +A 4 részleges fájl mindegyike **csak 5 szekciót** fed le a 25-ből: + +| Szekció | de.ts | ro.ts | cz.ts | sk.ts | +|---------|-------|-------|-------|-------| +| menu | ✅ | ✅ | ✅ | ✅ | +| header | ✅ | ✅ | ✅ | ✅ | +| dashboard | ✅ | ✅ | ✅ | ✅ | +| zen | ✅ | ✅ | ✅ | ✅ | +| auth | ✅ | ✅ | ✅ | ✅ | +| Többi 20 szekció | ❌ | ❌ | ❌ | ❌ | + +**Hiányzó:** common, subscription, landing, garage, vehicleDetail, finance, profile, kyc, verifyEmail, company, organization, address, servicebook, vehicle, serviceFinder, provider, categories, gamification, admin + +--- + +## 6. Statisztikai Összesítés + +| Mérőszám | Érték | +|----------|-------| +| Összes i18n fájl | **8 db** (6 frontend + 2 backend) | +| Frontend teljes kulcsok (en.ts) | **~900+** egyedi kulcs | +| Frontend hiányos fájlok | **4 db** (de, ro, cz, sk) — csak 20% lefedettség | +| Backend teljes kulcsok | **~55** db | +| **Pontos duplikációk (összevonható)** | **25 csoport**, ~60 kulcs érintett | +| **Közel-duplikációk (felülvizsgálandó)** | **7 csoport**, ~15 kulcs érintett | +| **Jármű feature átfedések (pontos)** | **18 csoport**, ~50 kulcs érintett | +| **Jármű feature átfedések (eltérő)** | **11 csoport**, ~25 kulcs érintett | +| **Backend hiányzó kulcsok** | **3 db** | +| **Összes optimalizálható kulcs** | **~150+** (a ~900-ból) ≈ **16-17%** | + +--- + +## 7. Optimalizálási Javaslat + +### 7.1 Azonnal összevonható (25 csoport) + +Javasolt elnevezési konvenció: A leggyakrabban használt kulcsot tartsuk meg (pl. common), a többit alias-ként hivatkozzuk rá. + +### 7.2 Jármű feature-ök konszolidációja + +Javasolt struktúra: egy központi `vehicle.features` szekció, a motorcycleFeatures/lcvFeatures/hgvFeatures pedig hivatkozzon rá. Az eltérő magyar fordításokat egységesíteni kell. + +### 7.3 Backend hiányzó kulcsok pótlása + +- AUTH.ERROR.UNAUTHORIZED → hozzáadandó a hu.json-hoz +- COMMON.SAVE_SUCCESS → hozzáadandó az en.json-hoz +- SENTINEL.APPROVAL.REQUIRED → hozzáadandó az en.json-hoz + +### 7.4 Várakozó lista (nem sürgős) + +A 4 hiányos fordítást (de, ro, cz, sk) csak az optimalizálás után érdemes bővíteni. + +--- + +## 8. Összefoglaló + +A frontend i18n rendszerben **~150 kulcs (16-17%)** duplikált vagy átfedő. Összevonásukkal: + +- **Karbantarthatóság javul:** egy helyen módosítandó fordítás +- **Konzisztencia nő:** nem jelenhet meg kétféleképp ugyanaz +- **Fájlméret csökken:** ~5-10% méretcsökkenés +- **Backend hiba javul:** 3 hiányzó kulcs pótlása + +**A véglegesítéshez kérjük a felhasználó jóváhagyását.** diff --git a/frontend/src/components/dashboard/AdPlacementWidget.vue b/frontend/src/components/dashboard/AdPlacementWidget.vue index fba9b7c..d8d05e7 100644 --- a/frontend/src/components/dashboard/AdPlacementWidget.vue +++ b/frontend/src/components/dashboard/AdPlacementWidget.vue @@ -109,9 +109,10 @@ async function fetchAds() { const res = await api.get(`/marketing/placements/${props.zone}`) ads.value = res.data.ads || [] } catch (err: any) { - // Silently handle 404 (placement not configured yet) + // Silently handle 404 (placement not configured yet) — no console noise if (err.response?.status === 404) { ads.value = [] + error.value = null } else { error.value = 'Failed to load ads' console.error('[AdPlacementWidget] Error:', err) diff --git a/frontend/src/components/dashboard/MyVehiclesCard.vue b/frontend/src/components/dashboard/MyVehiclesCard.vue index 21bc905..17d8559 100644 --- a/frontend/src/components/dashboard/MyVehiclesCard.vue +++ b/frontend/src/components/dashboard/MyVehiclesCard.vue @@ -1,8 +1,13 @@ + + diff --git a/frontend/src/components/vehicles/tabs/FinancialsTab.vue b/frontend/src/components/vehicles/tabs/FinancialsTab.vue new file mode 100644 index 0000000..055b67c --- /dev/null +++ b/frontend/src/components/vehicles/tabs/FinancialsTab.vue @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/components/vehicles/tabs/OverviewTab.vue b/frontend/src/components/vehicles/tabs/OverviewTab.vue new file mode 100644 index 0000000..10c5e27 --- /dev/null +++ b/frontend/src/components/vehicles/tabs/OverviewTab.vue @@ -0,0 +1,165 @@ + + + diff --git a/frontend/src/components/vehicles/tabs/ServiceBookTab.vue b/frontend/src/components/vehicles/tabs/ServiceBookTab.vue new file mode 100644 index 0000000..cc0c990 --- /dev/null +++ b/frontend/src/components/vehicles/tabs/ServiceBookTab.vue @@ -0,0 +1,16 @@ + + + diff --git a/frontend/src/components/vehicles/tabs/TechDataTab.vue b/frontend/src/components/vehicles/tabs/TechDataTab.vue new file mode 100644 index 0000000..cefc6df --- /dev/null +++ b/frontend/src/components/vehicles/tabs/TechDataTab.vue @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/i18n/en.ts b/frontend/src/i18n/en.ts index 1899672..59937f9 100644 --- a/frontend/src/i18n/en.ts +++ b/frontend/src/i18n/en.ts @@ -7,6 +7,12 @@ export default { optional: 'optional', select: 'Select', retry: 'Retry', + back: 'Back', + licensePlate: 'License Plate', + vin: 'VIN', + fuel: 'Fuel', + mileage: 'Mileage', + year: 'Year', }, subscription: { title: 'Subscription Plans', @@ -65,6 +71,48 @@ export default { addVehicle: '+ Add New Vehicle', service: 'Service', }, + garage: { + title: 'My Garage', + subtitle: 'Managing {count} vehicles', + backToDashboard: 'Back to Dashboard', + backToGarage: 'Back to Garage', + noPlate: 'No Plate', + primary: 'Primary', + brandModel: 'Brand / Model', + year: 'Year', + nextService: 'Next Service', + mileage: 'Mileage', + emptyTitle: 'Your garage is empty', + emptySubtitle: 'Add your first vehicle to get started!', + }, + vehicleDetail: { + title: 'Vehicle Details', + placeholderTitle: 'Deep Dive Coming Soon', + placeholderSubtitle: 'This page will soon feature a full tabbed layout with detailed vehicle information, service history, costs, and more.', + notFound: 'Vehicle not found.', + noPlate: 'No Plate', + // Tab labels + tabOverview: 'Overview', + tabTechData: 'Technical Data', + tabServiceBook: 'Service Book', + tabFinancials: 'Financials', + tabDocuments: 'Documents', + // Tab placeholders + placeholderOverview: 'Vehicle overview details will appear here.', + placeholderTechData: 'Technical specifications will appear here.', + placeholderServiceBook: 'Service history will appear here.', + placeholderFinancials: 'Financial records will appear here.', + placeholderDocuments: 'Documents will appear here.', + // OverviewTab keys + photoPlaceholder: 'Photo upload coming soon', + vin: 'VIN', + fuelType: 'Fuel', + mileage: 'Mileage', + motExpiry: 'MOT Expiry', + nextService: 'Next Service', + noData: 'No data', + comingSoon: 'Coming soon', + }, header: { welcome: 'Welcome', userMenu: 'User menu', diff --git a/frontend/src/i18n/hu.ts b/frontend/src/i18n/hu.ts index d852dfc..7fc1bd8 100644 --- a/frontend/src/i18n/hu.ts +++ b/frontend/src/i18n/hu.ts @@ -7,6 +7,12 @@ export default { optional: 'opcionális', select: 'Válassz', retry: 'Újrapróbálkozás', + back: 'Vissza', + licensePlate: 'Rendszám', + vin: 'Alvázszám (VIN)', + fuel: 'Üzemanyag', + mileage: 'Futásteljesítmény', + year: 'Évjárat', }, subscription: { title: 'Előfizetési Csomagok', @@ -65,6 +71,45 @@ export default { addVehicle: '+ Új Jármű Hozzáadása', service: 'Szervíz', }, + garage: { + title: 'Garázsom', + subtitle: '{count} jármű kezelése', + backToDashboard: 'Vissza az irányítópultra', + backToGarage: 'Vissza a Garázsba', + noPlate: 'Nincs rendszám', + primary: 'Elsődleges', + brandModel: 'Márka / Modell', + year: 'Évjárat', + nextService: 'Következő Szerviz', + mileage: 'Futásteljesítmény', + emptyTitle: 'A garázsod üres', + emptySubtitle: 'Add hozzá az első járművedet!', + }, + vehicleDetail: { + title: 'Jármű Részletek', + placeholderTitle: 'Részletes nézet hamarosan', + placeholderSubtitle: 'Ez az oldal hamarosan teljes tabokkal ellátott részletes járműinformációkat, szerviztörténetet, költségeket és egyebeket tartalmaz.', + notFound: 'A jármű nem található.', + noPlate: 'Nincs rendszám', + tabOverview: 'Áttekintés', + tabTechData: 'Műszaki adatok', + tabServiceBook: 'Szervizkönyv', + tabFinancials: 'Pénzügyek', + tabDocuments: 'Dokumentumok', + placeholderOverview: 'A jármű áttekintő adatai itt jelennek meg.', + placeholderTechData: 'A műszaki adatok itt jelennek meg.', + placeholderServiceBook: 'A szerviztörténet itt jelenik meg.', + placeholderFinancials: 'A pénzügyi adatok itt jelennek meg.', + placeholderDocuments: 'A dokumentumok itt jelennek meg.', + photoPlaceholder: 'Fotó feltöltés hamarosan', + vin: 'Alvázszám (VIN)', + fuelType: 'Üzemanyag', + mileage: 'Futásteljesítmény', + motExpiry: 'Műszaki érvényesség', + nextService: 'Következő Szerviz', + noData: 'Nincs adat', + comingSoon: 'Hamarosan', + }, header: { welcome: 'Üdvözlünk', userMenu: 'Felhasználói menü', diff --git a/frontend/src/layouts/OrganizationLayout.vue b/frontend/src/layouts/OrganizationLayout.vue index 814fa1e..ca5988a 100644 --- a/frontend/src/layouts/OrganizationLayout.vue +++ b/frontend/src/layouts/OrganizationLayout.vue @@ -38,6 +38,17 @@ class="absolute right-0 top-full mt-2 w-56 z-50 rounded-xl border border-white/10 bg-[#04151F]/95 backdrop-blur-xl shadow-2xl shadow-black/40 overflow-hidden" >
+ + +