Files
service-finder/.roo/history.md
2026-07-08 08:03:57 +00:00

282 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Service Finder Fejlesztési Történet
## 2026-06-21 - P0 Deep Audit: Database Consistency & Zombie API Hunt
### 🎯 Cél
...
## 2026-07-07 - P0 ServiceProvider AddressManager Integration
### 🎯 Cél
...
#### 2. Seed script létrehozása és futtatása
A `seed_dismantler_category.py` szkript létrehozva (`backend/app/scripts/`), amely idempotens módon beszúrja az "Autóbontó / Használt alkatrész" kategóriát.
### ✅ Verifikáció
1. **Seed script futtatva** → ✅ Sikeresen beszúrva az 'Autóbontó / Használt alkatrész' kategória (ID: 767). Összes rekord: 135.
2. **admin_providers.py szintaxis ellenőrzés**`import app.api.v1.endpoints.admin_providers` sikeres (✅ module imports OK)
3. **sync_engine futtatva** → 1282 OK, 0 issue, schema fully in sync
## 2026-07-07 - P0 BUGFIX: Live Provider Advanced Fields & Gas Station Categories
### 🎯 Cél
P0 kritikus hibajavítás: Live Provider (ID 6, MOL Fót) `opening_hours` és `category_ids` mezői elvesztek a backend `update_provider()` végpontban, mert a ServiceProvider-hez nem tartozott ServiceProfile rekord. Emellett a "Shop / Kisbolt" kategória hiányzott a rendszerből.
### 🔧 Változtatások
#### 1. Backend fix: [`admin_providers.py`](backend/app/api/v1/endpoints/admin_providers.py:1571)
- **P0 BUGFIX (2026-07-07):** Ha egy Live Provider-hez nem létezik ServiceProfile, de a PATCH kérésben advanced mezők (`opening_hours`, `specializations`, `supported_vehicle_classes`, `category_ids`) érkeznek, a backend MOST automatikusan létrehoz egy ServiceProfile rekordot.
- **Response serialization fix:** A `category_ids` visszaadása előtt a rendszer lekérdezi a tényleges DB állapotot a `ServiceExpertise` táblából (`resolved_category_ids`), így a frontend mindig a valóban elmentett adatokat kapja vissza.
#### 2. Seed script: [`seed_gas_station.py`](backend/app/scripts/seed_gas_station.py)
- Létrehozva a hiányzó "Shop / Kisbolt" (Convenience Store) kategória (ID: 768) az "Üzemanyag és Töltőállomás" (ID=755) szülő alá.
- Meglévő kategóriák (ID=755, 756, 757) érintetlenek.
### ✅ Verifikáció
1. **Seed script futtatva** → ✅ 'Shop / Kisbolt' (ID: 768) már létezik. Összes rekord: 136.
2. **sync_engine futtatva** → 1282 OK, 0 issue, schema fully in sync
## 2026-07-07 - P0 CRITICAL HOTFIX: Promotion Engine v2
### 🎯 Cél
Javítani a Promotion Engine-t a `PATCH /admin/providers/{id}` végponton, amely ServiceStaging rekordok jóváhagyásakor nem futott le.
### 🔍 Root Cause Analysis
1. **Trigger condition too fragile**: Az `if is_staging and new_status == "approved":` feltétel a `new_status` változóra támaszkodott, amely a `update_fields.get("status")`-ból származott. Ha a státusz nem volt explicit átadva a PATCH payload-ban (pl. csak `name` mezőt módosítottak), `new_status` `None` volt, és a promotion blokk teljesen kimaradt.
2. **ID handling bug**: Promotion után a `provider_obj` átállításra került az új ServiceProvider-re, de az AuditLog (`target_id=str(provider_id)`) és a ServiceProfile lekérdezés (`ServiceProfile.service_provider_id == provider_id`) továbbra is a régi staging ID-t (pl. 1754) használta, nem az új provider ID-t.
### 🔧 Javítások
1. **Trigger condition → `effective_status`**: A feltétel most a `provider_obj.status`-t (tényleges DB állapot) ellenőrzi közvetlenül, nem a `new_status` payload változót. Ez biztosítja, hogy a promotion akkor is lefusson, ha a státusz már "approved" volt a DB-ben, vagy ha nem volt explicit átadva.
2. **ID fix**: Minden `provider_id` hivatkozás (AuditLog, ServiceProfile lookup) `provider_obj.id`-ra lett cserélve, ami promotion után az új ServiceProvider ID-ját tartalmazza.
3. **Logolás**: `logger.error("P0 PROMOTION TRIGGERED")` hozzáadva a promotion blokk elejére a Docker logokban való trace-elhetőségért.
### ✅ Verifikáció
1. **Szintaxis ellenőrzés** → ✅ `py_compile` sikeres
2. **Konténer újraindítás** → ✅ `docker compose restart sf_api` sikeres
3. **API elérhető** → ✅ Uvicorn fut a 8000-es porton
## 2026-07-07 - DDD Database Refactoring 1.0 (P0 - 3 Phase)
### 🎯 Cél
DDD Database Refactoring 1.0 három fázisban: address_id FK bevezetése, admin_providers.py egyszerűsítése, frontend cleanup.
### 🔧 Változtatások
#### #396 - Phase 1: address_id FK (Already Complete)
- `ServiceProvider.address_id` és `address_rel` már létezett a modellben
- Mind a 7 provider rekord rendelkezik `address_id`-vel
- `sync_engine` 1282 OK, 0 drift - nincs teendő
#### #397 - Phase 2: admin_providers.py Simplify
- `_build_unified_providers_query()`: `ServiceProvider.address_id` hozzáadva a SELECT-hez (12. oszlop)
- `ServiceStaging` és `Organization` UNION ágak: `literal(None).label("address_id")` placeholder a column parityért
- `list_providers()`: `address_detail` feloldása `AddressManager.get_normalized()` segítségével minden sorban
- Ellenőrizve: `list_providers` endpoint minden provider esetén visszaadja az `address_detail`-t
#### #398 - Phase 3: Frontend Cleanup
- `garages/index.vue` line 157: `garage.city` fallback eltávolítva → csak `garage.address_detail?.city`
- `providers/index.vue` line 137: már `address_detail`-t használ (nem kellett módosítani)
- `providers/[id]/index.vue` lines 144-166: már `address_detail`-t használ (nem kellett módosítani)
- `providers/[id]/edit.vue` lines 1133-1148: már `data.address_detail`-t használ (nem kellett módosítani)
### ✅ Verifikáció
1. **#397 API teszt** → ✅ `list_providers` endpoint 5 provider-t ad vissza `address_detail` mezővel
2. **#398 Frontend regex audit** → ✅ Nincs több `provider.address` vagy `provider.city` flat field referencia
3. **Konténer újraindítás** → ✅ `docker compose restart sf_api` sikeres
## 2026-07-07 - P0 BUGFIX: Category Sync & Parent-Child UX Automation
### 🎯 Cél
A provider admin felület kategória szinkronizációjának javítása legacy provider-eknél (ahol a ServiceProfile `organization_id`-n keresztül kapcsolódik), valamint a frontend kategória-választó UX automatizálása.
### 🔧 Változtatások
#### Backend: `backend/app/api/v1/endpoints/admin_providers.py`
- **`get_provider_detail`** (line 615-618): ServiceProfile lekérdezés kiterjesztve OR-ra: `(ServiceProfile.service_provider_id == provider_id) | (ServiceProfile.organization_id == provider_id)`
- **`update_provider`** (line 1575-1578): Ugyanez a javítás a PATCH végpontban, `provider_obj.id`-t használva
- **Root Cause**: Legacy provider-eknél (pl. ID 6) a ServiceProfile `organization_id` mezőn keresztül kapcsolódik, nem `service_provider_id`-n. Az egyszerű FK keresés miatt a profil nem található -> `category_ids` elveszett a válaszból és a mentésből.
#### Frontend: `frontend_admin/pages/providers/[id]/edit.vue`
- **Import** (line 626): `watch` hozzáadva a Vue import-hoz
- **`parentLookupMap`** (line 942): Flat Map<number, number> a kategória fa rekurzív bejárásához
- **`buildParentLookupMap()`** (line 944): Rekurzív fa bejáró, ami minden gyermek node-hoz hozzárendeli a szülő ID-t
- **`watch(categoryTree, ...)`** (line 956): A map újraépítése minden fa betöltéskor
- **`toggleCategory()`** (line 961): Gyermek kategória kiválasztásakor automatikusan hozzáadja a szülő kategória ID-t is a `form.category_ids` tömbhöz
### ✅ Verifikáció
1. **Backend szintaxis** -> OK: `admin_providers` modul sikeresen betöltve a konténerben
2. **Sync Engine** -> OK: 1282 OK, 0 hiba, adatbázis szinkronban
3. **Frontend** -> OK: Import és `toggleCategory` módosítva, `watch` + `parentLookupMap` beépítve
## 2026-07-07 - P0 BUGFIX: ServiceProfile Auto-Creation on PATCH for New Providers
### 🎯 Cél
P0 kritikus hibajavítás: `PATCH /admin/providers/{id}` 500-as hibát dobott, ha a provider-hez nem tartozott ServiceProfile rekord. A `_sync_service_expertises()` hívás `profile.id`-t használt, de `profile` `None` maradt, mert a ServiceProfile létrehozás feltételes volt (`has_advanced_fields`).
### 🔧 Változtatások
#### 1. Import fix: [`admin_providers.py`](backend/app/api/v1/endpoints/admin_providers.py:48)
- `ServiceExpertise` hozzáadva az import-hoz (hiányzott, `NameError`-t okozott futásidőben)
#### 2. ServiceProfile unconditional creation: [`admin_providers.py`](backend/app/api/v1/endpoints/admin_providers.py:1588)
- **Előtte:** ServiceProfile csak akkor jött létre, ha `has_advanced_fields` True volt
- **Utána:** Ha nincs ServiceProfile, MINDIG létrejön egy alapértelmezett profil (fingerprint: `auto-created-{provider_id}`, status: `active`, trust_score: 30)
- Debug logolás hozzáadva (`print` + `logger.info`) a sync hívás előtt
### ✅ Verifikáció
1. **Sync Engine** -> OK: 1282 OK, 0 hiba, adatbázis szinkronban
2. **E2E teszt** -> ✅ PATCH 200 SUCCESS: provider #21 (Test Provider No Address - Fixed v4) sikeresen frissítve `category_ids=[12, 13, 14]`-gyel
3. **Docker log** -> ✅ `P0 BUGFIX: Auto-created ServiceProfile #24 for Live provider #21`
## 2026-07-07 - P0 HOTFIX: Edit Form Chips & Reactivity Deep Clone
### 🎯 Cél
P0 kritikus hibajavítás: A provider edit űrlapon a kiválasztott kategória chipek nyers ID-kat jelenítettek meg (#760) név helyett, és a "Mentés" gomb disabled maradt kategória toggle után.
### 🔍 Root Cause Analysis
1. **Chip Display**: A template már `{{ getCategoryName(id) }}`-t használt (line 243), és a `useCategories` composable `getCategoryName()` függvénye helyesen visszaadja a nevet, ha a `categoryMap` be van töltve. A `#760` fallback akkor jelenik meg, ha a kategóriák még nem töltődtek be — ez normális átmeneti állapot.
2. **Reactivity Bug (Fő probléma)**: A `mapProviderToForm()` függvényben a `form.category_ids = data.category_ids || []` direkt referenciát másolt, nem deep clone-t. Mivel a `form.category_ids` és a `provider.value.category_ids` ugyanarra a tömbre mutattak, a `hasChanges` computed property sosem érzékelte a változást — a kategória toggle mindkét referenciát egyszerre módosította.
### 🔧 Változtatások
#### [`frontend_admin/pages/providers/[id]/edit.vue`](frontend_admin/pages/providers/[id]/edit.vue:1137)
- **`mapProviderToForm()`** (lines 1137-1148): Minden tömb értékadás deep clone-ra javítva:
- `form.supported_vehicle_classes = [...(data.supported_vehicle_classes || [])]`
- `form.category_ids = [...(data.category_ids || [])]`
- `form.specializations.brands = Array.isArray(spec.brands) ? [...spec.brands] : []`
- `form.specializations.propulsion = Array.isArray(spec.propulsion) ? [...spec.propulsion] : []`
### ✅ Verifikáció
1. **Deep clone ellenőrzés**: `form.category_ids` és `provider.value.category_ids` most külön referenciák — a toggleCategory() hívás után a `hasChanges` helyesen `true`-t ad vissza.
2. **Chip display**: A template már `{{ getCategoryName(id) }}`-t használ (line 243), a `useCategories.getCategoryName()` pedig a `categoryMap`-ből adja vissza a nevet, vagy `#${id}` fallback-et, ha még nem töltődött be.
## 2026-07-07 - P0 EPIC: i18n JSONB Database Migration (Phase 1 & 2)
### 🎯 Cél
Architect által jóváhagyott i18n JSONB migrációs terv ([`plans/logic_spec_i18n_jsonb_migration_phase1.md`](plans/logic_spec_i18n_jsonb_migration_phase1.md)) első két fázisának implementálása: Séma bővítés (Phase 1) és Adatmigráció (Phase 2).
### 🔧 Változtatások
#### Phase 1: Schema Expansion (3 SQLAlchemy model)
1. **`backend/app/models/marketplace/service.py`** — `ExpertiseTag` osztály:
- `name_i18n: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default=text("'{\"hu\": \"\"}'::jsonb"))`
- `description_i18n: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)`
- GIN index: `Index('idx_expertise_tags_name_i18n', 'name_i18n', postgresql_using='gin')`
2. **`backend/app/models/vehicle/vehicle_definitions.py`** — `BodyTypeDictionary` osztály:
- `name_i18n: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default=text("'{\"hu\": \"\"}'::jsonb"))`
- GIN index: `Index('idx_dict_body_types_name_i18n', 'name_i18n', postgresql_using='gin')`
3. **`backend/app/models/core_logic.py`** — `ServiceCatalog` osztály:
- `name_i18n: Mapped[dict] = mapped_column(JSONB, nullable=False, server_default=text("'{\"hu\": \"\"}'::jsonb"))`
- `description_i18n: Mapped[Optional[dict]] = mapped_column(JSONB, nullable=True)`
- GIN index: `Index('idx_service_catalog_name_i18n', 'name_i18n', postgresql_using='gin')`
#### Phase 2: Data Migration Script
4. **`backend/app/scripts/migrate_i18n_jsonb.py`** — Létrehozva:
- Aszinkron migrációs script, amely a régi `name_hu`/`name_en` oszlopokat JSONB struktúrába csomagolja
- Kezeli a `name_translations` meglévő JSONB mező merge-elését is
- `json.dumps()` használata az asyncpg JSONB kompatibilitás miatt
- Idempotens: `ON CONFLICT DO NOTHING` + meglévő adatok felülírása
### 📊 Migrációs Eredmények
| Tábla | Sorok | name_i18n | description_i18n |
|-------|-------|-----------|------------------|
| `marketplace.expertise_tags` | 136 | ✅ `{"hu": "...", "en": "..."}` | ✅ `{"hu": "..."}` |
| `vehicle.dict_body_types` | 16 | ✅ `{"hu": "Szedán", ...}` | ❌ N/A |
| `system.service_catalog` | 3 | ✅ `{"hu": "Adat Export"}` | ✅ `{"hu": "PDF és Excel..."}` |
### 🗄️ GIN Indexek
| Index | Tábla | Típus |
|-------|-------|-------|
| `idx_expertise_tags_name_i18n` | `marketplace.expertise_tags` | GIN on `name_i18n` |
| `idx_dict_body_types_name_i18n` | `vehicle.dict_body_types` | GIN on `name_i18n` |
| `idx_service_catalog_name_i18n` | `system.service_catalog` | GIN on `name_i18n` |
### ✅ Verifikáció
1. **sync_engine futtatva** → 1287 OK, 0 Fixed, 0 Shadow — schema fully in sync
2. **5 új JSONB oszlop** létrehozva a DB-ben (3× `name_i18n`, 2× `description_i18n`)
3. **3 GIN index** manuálisan létrehozva (sync_engine nem kezeli az indexeket)
4. **155 sor** sikeresen migrálva (136 expertise_tags + 16 dict_body_types + 3 service_catalog)
5. **Régi oszlopok** (`name_hu`, `name_en`, `name`, `description`) érintetlenek — backward compatibility biztosítva
## 2026-07-08 - P0 i18n Static Dictionary Cleanup (Admin Frontend)
### 🎯 Cél
Az admin frontend összes providers oldalának és a layout sidebar menüjének teljes i18n konverziója. Minden hardcoded magyar szöveg `$t()` / `t()` hívásokra cserélése, és a hiányzó fordítási kulcsok felvétele a `hu.json` és `en.json` fájlokba.
### 🔧 Változtatások
#### 1. Layout (`default.vue`)
- `menuGroups` array összes label-je i18n kulcsokra cserélve (`menu.center`, `menu.dashboard`, `menu.providers`, stb.)
- `pageTitle` computed property összes hardcoded string i18n kulcsokra cserélve
- Key fix: `menu.ledger``menu.points_ledger`, `gamification.users.detail``gamification.users.user_detail_title`
#### 2. Providers oldalak
- **index.vue** - Teljes konverzió: template + script (`statusLabel()`)
- **pending.vue** - Teljes konverzió: template + script (toast üzenetek)
- **rejected.vue** - Teljes konverzió: template + script (toast üzenetek)
- **[id]/index.vue** - Teljes konverzió: template (minden szekció, modal, drawer) + script (tabs, DAYS, statusLabel, statusDescription, vehicleClassLabel, validationTypeLabel, historyActionLabel, toast üzenetek)
- **[id]/edit.vue** - Teljes konverzió: template (minden tab, szekció, drawer) + script (tabs, VEHICLE_CLASSES, DAYS, copyWeekdays, saveForm toasts)
#### 3. Lokalizációs fájlok
- **hu.json** - ~200+ új providers kulcs (index, pending, rejected, detail, edit)
- **en.json** - ~200+ új providers kulcs angol fordítással
### ✅ Verifikáció
1. **Build**`npm run build` sikeres (sf_admin_frontend konténerben) ✅
2. **Nincs fordítási hiba** - minden használt i18n kulcs létezik a locale fájlokban ✅
## 2026-07-08 - P0 Architectural Upgrade: Modular i18n Locales Migration
### 🎯 Cél
Migrate the Nuxt admin frontend from monolithic i18n locale files (single `hu.json`, `en.json`) to Nuxt i18n Modular Locales (multiple domain-specific JSON files per locale).
### 🔧 Változtatások
1. **`frontend_admin/nuxt.config.ts`** (31. sor): Changed `langDir` from `'i18n/locales/'` to `'locales/'` to avoid path doubling with Nuxt i18n module's automatic `i18nDir` detection. Updated hu and en locale configs to use `files` array pattern instead of single `file`.
2. **`frontend_admin/i18n/locales/hu/common.json`** (CREATED): Extracted all `common.*` keys from old monolithic `hu.json` (loading, saving, error, retry, save, cancel, delete, edit, search, filter, status, permissions, etc.)
3. **`frontend_admin/i18n/locales/en/common.json`** (CREATED): English equivalents of all common.* keys.
4. **`frontend_admin/i18n/locales/hu/menu.json`** (CREATED): Extracted all `menu.*` keys (center, dashboard, providers, garages, users, finance, packages, gamification, system, etc.)
5. **`frontend_admin/i18n/locales/en/menu.json`** (CREATED): English equivalents of all menu.* keys.
6. **`frontend_admin/i18n/locales/hu/providers.json`** (UPDATED): Expanded from ~84 lines to ~280 lines with all provider-related keys from old monolithic file.
7. **`frontend_admin/i18n/locales/en/providers.json`** (UPDATED): English equivalents, expanded similarly.
8. **`frontend_admin/locales/hu.json`** (DELETED): Old monolithic Hungarian locale file (1003 lines).
9. **`frontend_admin/locales/en.json`** (DELETED): Old monolithic English locale file (1003 lines).
### 🔍 Megjegyzések
- A Nuxt i18n modul automatikusan detektálja az `i18n/` könyvtárat a projekt gyökerében, és azt használja `i18nDir`-ként. Ezért a `langDir`-t relatívan kell megadni ehhez a könyvtárhoz (`'locales/'`), nem pedig abszolút módon (`'i18n/locales/'`).
- A többi nyelv (de, fr, ro, cz, sk) továbbra is a régi `frontend_admin/locales/` könyvtárban található monolit fájlokat használja.
- A konténer újraindítás után sikeresen elindult, a Nitro szerver és a Vite klienst is felépítette.
- A `@intlify/message-compiler` `Invalid linked format` hibát dobott az `info@example.com` email placeholder értékben lévő `@` karakter miatt. A javítás: `{'@'}` Intlify szintaxis használata a `hu/providers.json` és `en/providers.json` fájlokban.
## 2026-07-08 - P0 CRITICAL i18n Audit & Repair (HU/EN) - Fix #6: Missing persons.json
### 🎯 Cél
A Nuxt i18n modul `computeLocaleHashes` függvénye `ENOENT` hibát dobott, mert a `persons.json` fájl nem létezett a `frontend_admin/i18n/locales/en/` és `hu/` könyvtárakban, pedig a `nuxt.config.ts` `files` tömbje hivatkozott rá.
### 🔧 Végrehajtott Javítások
1. **`frontend_admin/i18n/locales/en/persons.json`** (LÉTREHOZVA): Angol nyelvű személykezelő fordítások (3456 byte), 80+ kulcs a `persons.*` és `persons.details.*` névtérben.
2. **`frontend_admin/i18n/locales/hu/persons.json`** (LÉTREHOZVA): Magyar nyelvű személykezelő fordítások (3898 byte), azonos struktúrával.
### 📝 Technikai Részletek
- A `persons.*` kulcsokat a `pages/persons/index.vue` (lista nézet) és `pages/persons/[id]/index.vue` (részletek nézet) template-ek használják.
- A hiányzó fájl a `nuxt.config.ts` 24 moduláris fájljának egyike volt, amelyet a Fix #3 során adtunk hozzá a `files` tömbhöz, de a fájl ténylegesen nem létezett a lemezen.
- A `.nuxt` cache törlése (`sudo rm -rf frontend_admin/.nuxt/dev`) és a konténer újraindítása után a build sikeresen lefutott i18n hibák nélkül.
### ✅ Verifikáció
1. **Konténer újraindítva**`docker compose restart sf_admin_frontend`
2. **Build logok**`✔ Vite client built in 40ms`, `✔ Vite server built in 445ms`, `[nitro] ✔ Nuxt Nitro server built in 1526ms`
3. **i18n hibák** → Nincs `ERROR`, nincs `ENOENT`, nincs i18n compilation error ✅