24 KiB
Service Finder Fejlesztési Történet
2026-06-14 - Admin Search Bugs Fix & Sticky UX
🎯 Cél
Backend SQL hibák javítása a GET /admin/users végponton (Address outerjoin, phone search), valamint frontend UX fejlesztések (sticky bulk action bar, clear/X gomb, üres állapot üzenet, HeaderProfile az AdminLayout-ban).
🔧 Változtatások
1. BACKEND - admin.py:
GET /admin/usersSQL javítás:Addressouterjoin-ra változtatva (isouter=True), hogy a cím nélküli userek is visszajöjjenek- Telefonszám keresés:
phonemező hozzáadva aor_()feltételhez
2. FRONTEND - AdminLayout.vue:
HeaderProfilekomponens beillesztve a jobb felső sarokba (belépett user avatar + név)- A
SidebarTogglegomb fixálva a sidebar szélén
3. FRONTEND - AdminUsersView.vue:
- Sticky Bulk Action Bar: A tömeges műveletek sáv (
BulkActionBar)stickypozícionálást kapott, hogy görgetéskor is látszódjon - Clear/X gomb: A keresőmezőbe egy
Xgomb 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:
- "Nincs kategória" javítás: A
v-if="provider.category"feltétel helyettv-if="provider.categories && provider.categories.length > 0"lett beállítva. Ha acategoriesarray üres, de a régicategorystring 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_phonemezőhöz telefon ikont + linket (tel:), awebsitemezőhöz weboldal ikont + linket (target="_blank") jelenít meg. - TypeScript interface bővítés: A
ProviderSearchResultinterfé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:
_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-exceptblokkba csomagolva a teljes beszúrási logikalogger.error(..., exc_info=True)hibanaplózás hozzáadvaparent_id=Noneéspath=Noneexplicit 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
categoriestö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ő:
Organizationmodel:plus_code: Mapped[Optional[str]] = mapped_column(String(20))ProviderSearchResult:plus_code: Optional[str] = NoneProviderQuickAddIn:plus_code: Optional[str] = Field(None, max_length=20)ProviderUpdateIn:plus_code: Optional[str] = Field(None, max_length=20)provider_service.py: plus_code hozzáadva a search SELECT-hez, quick_add Organization creation-höz, és update null-safety checkhezsync_enginesikeresen lefuttatva, aplus_codeoszlop létrejött
2. FRONTEND - Univerzális kategóriák:
ProviderEditModal.vue:allLevel1Categoriescomputed property, amely a checked Level 0 szülők Level 1 gyermekeit gyűjti össze deduplikálvaProviderQuickAddModal.vue: Ugyanaz aallLevel1Categorieslogika- Plus Code input mező hozzáadva mindkét modal "Alapadatok" tabjához
3. FRONTEND - Detail Modal Rework:
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:
✅ 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:
- Data Persistence Black Hole: Organization ID 4859 (Shell Dunakeszi)
plus_codeéscontact_phonemezői nem perzisztáltak mentés után - UNION ALL 500 error:
CompoundSelect must have identical numbers of columnshiba a search végponton
🔧 Változtatások
1. BACKEND - provider_service.py:
plus_codehozzáadva aProviderSearchResultkonstruktorhoz (getattr(row, "plus_code", None)) - a mező SELECT-ben volt, de a Python objektumba nem került átplus_codeoszlop (literal(None).label("plus_code")) hozzáadva aUNION ALLmásodik (staging_stmt) és harmadik (crowd_stmt) ágához, hogy a 15 oszlopszám konzisztens legyen- P0 CRITICAL BUGFIX: Ha nincs
ServiceProfilerekord az Organization-höz, aupdate_providermost létrehozza azt (fingerprint + location + contact adatokkal), ahelyett hogy csak logolná a hiányt és eldobná a contact/category adatokat ServiceStatusimport hozzáadva a provider_service.py-hoz
2. FRONTEND - ProviderEditModal.vue:
allLevel1CategoriesszétbontvavehicleLevel1Categories-re (Level 0 checkboxoktól függő) ésuniversalLevel1Categories-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
✅ Verifikáció
plus_code=8FVX+3W,contact_phone=+36 20 123 4567,contact_email=shell.dunakeszi@example.comsikeresen mentve és visszaolvasva ID 4859-hez- API
/api/v1/providers/searchhibátlanul fut (nincs 500-as UNION ALL hiba) - Adatbázis szinten ellenőrizve:
fleet.organizations.plus_codeésmarketplace.service_profiles.contact_phone/contact_emailhelyesen perzisztálva
2026-06-17 - P0 Ownership & Access Control Fix: OrganizationMember + owner_id
🎯 Cél
Két P0 kritikus hiba javítása a Provider Quick-Add és Update folyamatban:
- Ownerless Providers: A
quick_add_providerowner_id=None-t használt, így az újonnan létrehozott szolgáltatóknak nem volt tulajdonosa - Missing OrganizationMember: A 6-lépéses folyamat 5. lépésnél megállt - nem jött létre
OrganizationMemberrekord, így senki sem volt kapcsolatban a céggel - No Access Control: A
update_providervégpont bárki által hívható volt - nem ellenőrizte, hogy a hívó user jogosult-e szerkeszteni az adott szervezetet
🔧 Változtatások
1. BACKEND - provider_service.py:
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:
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_providerfü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.
2. BACKEND - providers.py:
PermissionError→HTTPException(403)kezelés hozzáadva aupdate_service_providervégponthoz
✅ Verifikáció
owner_id=2(admin user) sikeresen beállítva a létrehozott Organization-benOrganizationMember(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
2026-06-17 - P0 Critical Bugfix: Quick-Add Provider 422 category_id null
🎯 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.
🔧 Változtatások
1. BACKEND - provider.py:
ProviderQuickAddIn.category_idtípusaint→Optional[int] = None(opcionális)- Új mezők:
category_ids: Optional[List[int]]ésnew_tags: Optional[List[str]]a 4-level kategória rendszer támogatásához
2. BACKEND - provider_service.py:
quick_add_provider()most már kezeli acategory_id=Noneesetet (kihagyja az elsődleges kategória lekérdezést)- A
category_idsésnew_tagsmező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:
phone→contact_phoneésemail→contact_emailmező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
-
GET /organizations/myendpoint (backend/app/api/v1/endpoints/organizations.py:183-186): Nincsorg_typeszűrés, minden szervezetet visszaad, ahol a user tag azOrganizationMembertáblában. -
HeaderCompanySwitcher.vue:142-146: AcompanyOrganizationscomputed property csak azindividualtípust szűri ki, aservice_providertípusú szervezeteket nem. -
quick_add_provider()(backend/app/services/provider_service.py:638-646): LétrehozOrganizationMember-etrole=OWNER-rel, ami miatt aGET /myvisszaadja a szolgáltatót.
Javítási terv
organizations.py:183-186:org_typeszűrés hozzáadása (csakbusiness,fleet_owner,individual)organizations.py:193-206:owner_idmező hozzáadása a response-hozHeaderCompanySwitcher.vue:142-146:service_providerésservicetípusok kiszűrése- 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
-
backend/app/api/v1/endpoints/organizations.py:183-186—GET /myendpoint:- Hozzáadva
org_typeszűrés: csakbusiness,fleet_owner,individualtípusú szervezetek visszaadása owner_idmező hozzáadva a response dict-hez
- Hozzáadva
-
frontend/src/components/header/HeaderCompanySwitcher.vue:142-146—companyOrganizationscomputed:service_providerésservicetí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:
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:
- A
GET /myvégpont most márselect(Organization, OrganizationMember.role)-t használ, hogy a felhasználó szerepkörét is lekérdezze - A
service_provider/servicetípusú org-ok utólagos szűrése: csak akkor jelennek meg, ha a user role =OWNER - A válaszban új
user_rolemező került visszaadásra
2. FRONTEND - HeaderCompanySwitcher.vue:
- A
companyOrganizationscomputed property most már auser_rolemezőt is figyelembe veszi service_provider/servicetípus csakuser_role === 'OWNER'esetén jelenik meg
3. FRONTEND - organization.ts:
- Új
user_role?: stringmező azOrganizationIteminterfészben
Verifikáció
- ✅ Backend syntax check — OK
- ✅ API teszt: 4 org visszatér (1 individual + 3 service_provider OWNER)
- ✅
user_rolemező 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
OWNERszerepkö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:
owner_id=user_id→owner_id=None— crowdsourcingból felvett szolgáltatónak nincs tulajdonosa
2. BACKEND - provider_service.py:
role=OrgUserRole.OWNER→role=OrgUserRole.ADMIN— a felhasználó ADMIN jogot kap (szerkesztheti), de nem tulajdonos
✅ 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:
get_my_organizationsquery javítva:INNER JOIN→LEFT JOIN+ORfeltétel (owner_idVAGYOrganizationMember.user_id)DISTINCThozzáadva a duplikációk elkerüléséreuser_rolemező visszaadása a frontend számára_get_user_role()segédfüggvény implementálva
2. BACKEND - organization.py:
OrganizationMembermodellhez hozzáadva:status(String(20)),created_at,updated_atoszlopok- 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_memberstáblához
4. DOKUMENTÁCIÓ - 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/myhelyesen adja vissza a tulajdonosi és tagsági viszonyokatuser_rolemező 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:
OrganizationMembermodellhez hozzáadva:invited_email(String(255)) ésexpires_at(DateTime(timezone=True)) oszlopokOrgUserRoleenum szinkronizálva az adatbázisban lévő értékekkel:OWNER, ADMIN, MANAGER, MEMBER, AGENTdefault=OrgUserRole.DRIVER→default=OrgUserRole.MEMBER
2. BACKEND - organizations.py:
GET /organizations/mySQL javítás: ADISTINCTa 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 selectinloadimport hozzáadva asqlalchemy.orm-ból
3. BACKEND - organizations.py:
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ésPOST /invitations/{token}/acceptvégpontok eltávolítva
4. ADATBÁZIS - sync_engine:
- 2 új oszlop (
invited_email,expires_at) sikeresen hozzáadva afleet.organization_memberstá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:
OrgRoleúj modell létrehozva afleet.org_rolestáblában (id, name_key, display_name, description, is_system, priority, is_active, created_at)Organization.settingsJSONB oszlop hozzáadva alapértelmezett értékekkel:{"invite_expiry_days": 7, "max_members": 50, "allow_public_join": false}OrganizationMember.roletípusa megváltoztatvaPG_ENUM(OrgUserRole)-rólString(50)-re, alapértelmezett"MEMBER"
2. MODELL EXPORT - __init__.py:
OrgRolehozzáadva az importokhoz és az__all__listához
3. API VÉGPONTOK - organizations.py:
_()helper függvény hozzáadva aTranslationService.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.ADMINEnum 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, en.json:
ORGANIZATIONné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.pysikeresen lefuttatva:fleet.org_rolestábla létrehozva,fleet.organizations.settingsoszlop 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
OrgUserRoleEnum 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