szervíz rögzítés külső szemmel

This commit is contained in:
Roo
2026-06-18 00:04:42 +00:00
parent 127b130401
commit 404d04c56b
10 changed files with 524 additions and 14 deletions

View File

@@ -378,3 +378,71 @@ A Team Management modul teljes refaktorálása: hardcoded `OrgUserRole` Enum kiv
- 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é)

View File

@@ -100,6 +100,7 @@ class ExpertiseTag(Base):
key: Mapped[str] = mapped_column(String(50), unique=True, index=True)
name_hu: Mapped[Optional[str]] = mapped_column(String(100))
name_en: Mapped[Optional[str]] = mapped_column(String(100))
name_translations: Mapped[Any] = mapped_column(JSONB, server_default=text("'{}'::jsonb"))
category: Mapped[Optional[str]] = mapped_column(String(30), index=True)
# --- 🏗️ 4-LEVEL HIERARCHY (2026-06-17) ---

View File

@@ -31,6 +31,112 @@ logger = logging.getLogger(__name__)
# "children": { ... } }
# A szintek: 0=Járműtípus, 1=Iparág, 2=Szakma, 3=Specifikus feladat
# ──────────────────────────────────────────────
# UNIVERZÁLIS (JÁRMŰFÜGGETLEN) LEVEL 1 KATEGÓRIÁK
# ──────────────────────────────────────────────
# Ezek a kategóriák nem tartoznak egyetlen járműtípushoz sem (parent_id=None).
# A frontend "Univerzális Szolgáltatások" blokkban jelennek meg.
# Példák: Benzinkút, Étterem, Szállás
UNIVERSAL_CATEGORIES = {
"uzemanyag_toltoallomas": {
"name_hu": "Üzemanyag és Töltőállomás",
"name_en": "Fuel and Charging Station",
"category": "fuel_station",
"search_keywords": ["benzin", "dízel", "üzemanyag", "töltőállomás", "elektromos töltés", "fuel", "charging", "gas station"],
"description": "Üzemanyagtöltő állomások és elektromos jármű töltőállomások",
"children": {
"benzinkut": {
"name_hu": "Benzinkút",
"name_en": "Gas Station",
"category": "fuel_station",
"search_keywords": ["benzin", "dízel", "üzemanyag", "benzinkút", "töltőállomás", "gas station", "fuel", "petrol"],
"description": "Hagyományos üzemanyagtöltő állomások benzinnel, dízel üzemanyaggal",
},
"elektromos_toltoallomas": {
"name_hu": "Elektromos Töltőállomás",
"name_en": "EV Charging Station",
"category": "ev_charging",
"search_keywords": ["elektromos töltés", "töltőállomás", "EV charging", "electric car charging", "elektromos autó"],
"description": "Elektromos járművek töltőállomásai",
},
},
},
"etel_ital": {
"name_hu": "Étel és Ital",
"name_en": "Food and Drink",
"category": "food_drink",
"search_keywords": ["étterem", "büfé", "kávézó", "gyorsétterem", "vendéglő", "restaurant", "cafe", "food"],
"description": "Étterem, büfé, kávézó és egyéb vendéglátó egységek",
"children": {
"etterem": {
"name_hu": "Étterem",
"name_en": "Restaurant",
"category": "restaurant",
"search_keywords": ["étterem", "vendéglő", "restaurant", "fine dining", "étkezés"],
"description": "Étterem és vendéglátó egységek",
},
"gyorsetterem_bufe": {
"name_hu": "Gyorsétterem / Büfé",
"name_en": "Fast Food / Cafeteria",
"category": "fast_food",
"search_keywords": ["gyorsétterem", "büfé", "kávézó", "fast food", "cafe", "snack"],
"description": "Gyorséttermek, büfék és kávézók",
},
},
},
"szallas": {
"name_hu": "Szállás",
"name_en": "Accommodation",
"category": "accommodation",
"search_keywords": ["szállás", "motel", "hotel", "panzió", "vendégház", "accommodation", "hotel", "motel"],
"description": "Szálláshelyek, motelek, hotelek és panziók",
"children": {
"hotel": {
"name_hu": "Hotel",
"name_en": "Hotel",
"category": "hotel",
"search_keywords": ["hotel", "szálloda", "accommodation", "hotel"],
"description": "Hotelek és szállodák",
},
"motel_panzio": {
"name_hu": "Motel / Panzió",
"name_en": "Motel / Guesthouse",
"category": "motel",
"search_keywords": ["motel", "panzió", "vendégház", "guesthouse", "motel"],
"description": "Motelek, panziók és vendégházak",
},
},
},
"orzes_parkolas": {
"name_hu": "Őrzés és Parkolás",
"name_en": "Security and Parking",
"category": "parking",
"search_keywords": ["parkoló", "őrzött parkoló", "garázs", "parking", "garage"],
"description": "Őrzött parkolók, garázsok és parkolási szolgáltatások",
"children": {
"uni_orzott_parkolo": {
"name_hu": "Őrzött Parkoló",
"name_en": "Secured Parking",
"category": "secured_parking",
"search_keywords": ["őrzött parkoló", "parkoló", "secured parking", "parking lot"],
"description": "Őrzött parkolók és parkolóházak",
},
"uni_garazs": {
"name_hu": "Garázs",
"name_en": "Garage",
"category": "garage",
"search_keywords": ["garázs", "garage", "tároló", "storage"],
"description": "Garázsok és járműtárolók",
},
},
},
}
# Formátum:
# "key": { "name_hu", "name_en", "category", "search_keywords", "description",
# "children": { ... } }
# A szintek: 0=Járműtípus, 1=Iparág, 2=Szakma, 3=Specifikus feladat
TAXONOMY = {
# ════════════════════════════════════════════
# SZEMÉLYGÉPJÁRMŰ (Level 0)
@@ -983,12 +1089,50 @@ async def seed_expertise_taxonomy():
await db.commit()
print(f"✅ Sikeresen beszúrva {total_inserted} kategória.")
# 3. Ellenőrzés
# 3. Univerzális (járműfüggetlen) Level 1 kategóriák beszúrása
print("🌱 Univerzális (járműfüggetlen) Level 1 kategóriák beszúrása...")
universal_inserted = 0
for key, node in UNIVERSAL_CATEGORIES.items():
tag = ExpertiseTag(
key=key,
name_hu=node["name_hu"],
name_en=node["name_en"],
category=node.get("category", ""),
search_keywords=node.get("search_keywords", []),
description=node.get("description", ""),
is_official=True,
level=1,
parent_id=None,
path=key,
discovery_points=10,
usage_count=0,
)
db.add(tag)
universal_inserted += 1
# Gyermekek (Level 2) rekurzív feldolgozása
children = node.get("children", {})
if children:
await db.flush()
child_inserted = await _flatten_and_insert(
db=db,
parent_id=tag.id,
level=2,
path_prefix=key,
data=children,
parent_key=key,
)
universal_inserted += child_inserted
await db.commit()
print(f"✅ Sikeresen beszúrva {universal_inserted} univerzális kategória (Level 1 + Level 2 gyermekek).")
# 4. Ellenőrzés
result = await db.execute(select(func.count(ExpertiseTag.id)))
final_count = result.scalar()
print(f"📊 Összes rekord az expertise_tags táblában: {final_count}")
# 4. Statisztika szintenként
# 5. Statisztika szintenként
for level in range(4):
result = await db.execute(
select(func.count(ExpertiseTag.id)).where(

View File

@@ -0,0 +1,230 @@
"""
Seed script: Master Taxonomy & i18n Production Seed
Feltölti a marketplace.expertise_tags táblát a teljes 4-szintű,
kétnyelvű (HU/EN) autóipari taxonómiával.
Architektúra:
Level 0: Járműtípus (Vehicle Type)
Level 1: Iparág/Főcsoport (Industry)
Level 2: Szakma (Profession)
Level 3: Specifikus feladat/címke (Specific Tag)
Adatmodell:
- name_translations (JSONB): {"hu": "Klímatöltés", "en": "AC Recharge"}
- name_hu, name_en: Továbbra is kitöltésre kerülnek a JSONB-ből kinyert értékekkel
- Adjacency List (parent_id) + Materialized Path (path)
Idempotencia:
- Törli a meglévő is_official=True rekordokat
- MEGHAGYJA a felhasználók által létrehozott (is_official=False) címkéket
Futtatás:
docker exec sf_api python3 /app/backend/scripts/seed_master_taxonomy_i18n.py
"""
import asyncio
import logging
from sqlalchemy import select, func, delete
from app.db.session import AsyncSessionLocal
from app.models.marketplace.service import ExpertiseTag
logger = logging.getLogger(__name__)
TAXONOMY = {
"szemelygepjarmu": {
"name_translations": {"hu": "Személygépjármű", "en": "Passenger Car"},
"category": "vehicle_type",
"search_keywords": ["autó", "car", "személyautó", "passenger car", "személygépjármű"],
"description": "Személygépjárművekhez kapcsolódó szolgáltatások",
"children": {
"karbantartas_es_javitas": {
"name_translations": {"hu": "Karbantartás, Szerviz és Javítás", "en": "Maintenance, Service and Repair"},
"category": "vehicle_service",
"search_keywords": ["karbantartás", "szerviz", "javítás", "maintenance", "repair"],
"description": "Gépjárművek karbantartása, szervizelése és javítása",
"children": {
"autoszerelo": {
"name_translations": {"hu": "Autószerelő", "en": "Car Mechanic"},
"category": "mechanic",
"search_keywords": ["szerelő", "autószerelő", "mechanic", "garage"],
"description": "Személygépjárművek mechanikai javítása, karbantartása",
"children": {
"motorolaj_csere": {"name_translations": {"hu": "Motorolaj csere", "en": "Oil Change"}, "category": "mechanic", "search_keywords": ["olajcsere", "motorolaj", "olaj", "oil change"], "description": "Motorolaj és olajszűrő cseréje"},
"vezerles_csere": {"name_translations": {"hu": "Vezérlés csere", "en": "Timing Belt Replacement"}, "category": "mechanic", "search_keywords": ["vezérlés", "vezérműszíj", "timing belt", "lánc"], "description": "Vezérműszíj vagy vezérlőlánc cseréje"},
"fekbetet_csere": {"name_translations": {"hu": "Fékbetét csere", "en": "Brake Pad Replacement"}, "category": "mechanic", "search_keywords": ["fék", "fékbetét", "brake", "fékpofa"], "description": "Fékbetétek és fékpofák cseréje"},
"kuplung_csere": {"name_translations": {"hu": "Kuplung csere", "en": "Clutch Replacement"}, "category": "mechanic", "search_keywords": ["kuplung", "clutch", "kuplungcsere"], "description": "Kuplung szett cseréje"},
"turbo_felujitas": {"name_translations": {"hu": "Turbó felújítás", "en": "Turbo Repair"}, "category": "mechanic", "search_keywords": ["turbó", "turbo", "feltöltő", "turbo repair"], "description": "Turbófeltöltők javítása és felújítása"},
"dpf_tisztitas": {"name_translations": {"hu": "DPF tisztítás", "en": "DPF Cleaning"}, "category": "mechanic", "search_keywords": ["dpf", "részecskeszűrő", "diesel particulate filter"], "description": "Dízel részecskeszűrő tisztítása és regenerálása"},
"egr_tisztitas": {"name_translations": {"hu": "EGR szelep tisztítás", "en": "EGR Valve Cleaning"}, "category": "mechanic", "search_keywords": ["egr", "egr szelep", "kipufogógáz visszavezetés"], "description": "EGR szelep tisztítása és karbantartása"},
"motordiagnosztika": {"name_translations": {"hu": "Motordiagnosztika", "en": "Engine Diagnostics"}, "category": "mechanic", "search_keywords": ["diagnosztika", "motor", "hibakód", "diagnostics"], "description": "Motorhibák diagnosztizálása számítógépes eszközökkel"},
"klimatoltes": {"name_translations": {"hu": "Klímatöltés", "en": "AC Service"}, "category": "mechanic", "search_keywords": ["klíma", "klímatöltés", "ac", "air conditioning"], "description": "Légkondicionáló rendszer töltése és karbantartása"},
"ozonos_klimatisztitas": {"name_translations": {"hu": "Ózonos klímatisztítás", "en": "Ozone AC Cleaning"}, "category": "mechanic", "search_keywords": ["ózon", "klímatisztítás", "ozone", "ac cleaning"], "description": "Klímarendszer ózonos fertőtlenítése"},
"kipufogo_javitas": {"name_translations": {"hu": "Kipufogó javítás", "en": "Exhaust Repair"}, "category": "mechanic", "search_keywords": ["kipufogó", "exhaust", "dob", "katalizátor"], "description": "Kipufogórendszer javítása és hegesztése"},
"lengescillapito_csere": {"name_translations": {"hu": "Lengéscsillapító csere", "en": "Shock Absorber Replacement"}, "category": "mechanic", "search_keywords": ["lengéscsillapító", "shock", "felfüggesztés", "suspension"], "description": "Lengéscsillapítók és felfüggesztés elemek cseréje"},
"vizpumpa_csere": {"name_translations": {"hu": "Vízpumpa csere", "en": "Water Pump Replacement"}, "category": "mechanic", "search_keywords": ["vízpumpa", "water pump", "hűtővíz", "coolant"], "description": "Hűtővíz szivattyú cseréje"},
"termosztat_csere": {"name_translations": {"hu": "Termosztát csere", "en": "Thermostat Replacement"}, "category": "mechanic", "search_keywords": ["termosztát", "thermostat", "hűtés", "cooling"], "description": "Motor hűtőrendszer termosztátjának cseréje"},
"gyujtasgyertya_csere": {"name_translations": {"hu": "Gyújtógyertya csere", "en": "Spark Plug Replacement"}, "category": "mechanic", "search_keywords": ["gyújtógyertya", "spark plug", "gyertya", "gyújtás"], "description": "Gyújtógyertyák cseréje"},
"injektor_tisztitas": {"name_translations": {"hu": "Injektor tisztítás", "en": "Injector Cleaning"}, "category": "mechanic", "search_keywords": ["injektor", "injector", "befecskendező", "tisztítás"], "description": "Befecskendező rendszer tisztítása"},
"kompresszio_meres": {"name_translations": {"hu": "Kompresszió mérés", "en": "Compression Test"}, "category": "mechanic", "search_keywords": ["kompresszió", "compression", "nyomásmérés", "henger"], "description": "Motorhenger kompresszió mérése"}
}
},
"autovillamossag": {
"name_translations": {"hu": "Autóvillamosság", "en": "Auto Electrician"},
"category": "electrical",
"search_keywords": ["villamosság", "elektromos", "auto electrician", "elektronika"],
"description": "Gépjármű elektromos rendszereinek javítása",
"children": {
"akkumulator_csere": {"name_translations": {"hu": "Akkumulátor csere", "en": "Battery Replacement"}, "category": "electrical", "search_keywords": ["akkumulátor", "battery", "akku"], "description": "Indítóakkumulátor cseréje"},
"generator_felujitas": {"name_translations": {"hu": "Generátor felújítás", "en": "Alternator Repair"}, "category": "electrical", "search_keywords": ["generátor", "alternator", "váltóáramú generátor"], "description": "Generátor javítása és felújítása"},
"onindito_javitas": {"name_translations": {"hu": "Önindító javítás", "en": "Starter Motor Repair"}, "category": "electrical", "search_keywords": ["önindító", "starter", "indítómotor"], "description": "Önindító motor javítása"},
"hibakod_olvasas": {"name_translations": {"hu": "Hibakód olvasás", "en": "DTC Reading"}, "category": "electrical", "search_keywords": ["hibakód", "dtc", "diagnosztika", "error code"], "description": "Fedélzeti diagnosztikai hibakódok kiolvasása"},
"immobiliser_programozas": {"name_translations": {"hu": "Immobiliser programozás", "en": "Immobiliser Programming"}, "category": "electrical", "search_keywords": ["immobiliser", "immobilizer", "kulcs", "key programming"], "description": "Indításgátló rendszer programozása és kulcsmásolás"},
"tolatoradar_beszereles": {"name_translations": {"hu": "Tolatóradar beszerelés", "en": "Parking Sensor Installation"}, "category": "electrical", "search_keywords": ["tolatóradar", "parking sensor", "parktronic"], "description": "Tolatóradar rendszerek beszerelése"},
"fedelzeti_kamera_szereles": {"name_translations": {"hu": "Fedélzeti kamera szerelés", "en": "Dashcam Installation"}, "category": "electrical", "search_keywords": ["kamera", "dashcam", "fedélzeti kamera", "parkolókamera"], "description": "Fedélzeti kamerák és tolatókamerák beszerelése"},
"lampa_hid_kisero": {"name_translations": {"hu": "Lámpa / HID / Xenon javítás", "en": "Headlight / HID / Xenon Repair"}, "category": "electrical", "search_keywords": ["lámpa", "headlight", "xenon", "hid", "led", "fényszóró"], "description": "Gépjármű világítási rendszerek javítása"},
"kozponi_zaras_javitas": {"name_translations": {"hu": "Központi zár javítás", "en": "Central Locking Repair"}, "category": "electrical", "search_keywords": ["központi zár", "central locking", "ajtózár", "távirányító"], "description": "Központi zár rendszer javítása"}
}
},
"gumiszerviz": {
"name_translations": {"hu": "Gumiszerviz", "en": "Tire Service"},
"category": "tire",
"search_keywords": ["gumi", "gumis", "tire", "tyre", "abroncs", "kerék"],
"description": "Gumiabroncsok cseréje, javítása",
"children": {
"gumiabroncs_csere": {"name_translations": {"hu": "Gumiabroncs csere", "en": "Tire Change"}, "category": "tire", "search_keywords": ["gumicsere", "tire change", "abroncs csere"], "description": "Szezonális gumiabroncs csere"},
"centrirozas": {"name_translations": {"hu": "Centrírozás", "en": "Wheel Balancing"}, "category": "tire", "search_keywords": ["centrírozás", "balancing", "kerék kiegyensúlyozás"], "description": "Kerekek centrírozása és kiegyensúlyozása"},
"defektjavitas": {"name_translations": {"hu": "Defektjavítás", "en": "Puncture Repair"}, "category": "tire", "search_keywords": ["defekt", "puncture", "gumijavítás"], "description": "Gumiabroncs defekt javítása"},
"alufelni_gorgozes": {"name_translations": {"hu": "Alufelni görgőzés", "en": "Alloy Wheel Straightening"}, "category": "tire", "search_keywords": ["alufelni", "görgőzés", "alloy wheel", "felni javítás"], "description": "Alumínium felnik görgőzése és javítása"},
"tpms_szenzor_tanitas": {"name_translations": {"hu": "TPMS szenzor tanítás", "en": "TPMS Sensor Programming"}, "category": "tire", "search_keywords": ["tpms", "szenzor", "guminyomás", "tire pressure"], "description": "Guminyomás monitor rendszer szenzorainak tanítása"},
"gumi_teli_tarolas": {"name_translations": {"hu": "Gumi téli tárolás", "en": "Winter Tire Storage"}, "category": "tire", "search_keywords": ["tárolás", "gumi tárolás", "tire storage", "szezonális"], "description": "Szezonális gumiabroncs tárolás"}
}
},
"karosszeria_javitas": {
"name_translations": {"hu": "Karosszéria javítás", "en": "Body Repair"},
"category": "body",
"search_keywords": ["karosszéria", "lakatos", "bodyshop", "kasztni"],
"description": "Karosszéria javítás és lakatolás",
"children": {
"horpadasjavitas_pdr": {"name_translations": {"hu": "Horpadásjavítás (PDR)", "en": "Paintless Dent Repair (PDR)"}, "category": "body", "search_keywords": ["horpadás", "pdr", "dent repair", "horpadásjavítás"], "description": "Fényezés nélküli horpadásjavítás"},
"jegkar_javitas": {"name_translations": {"hu": "Jégkár javítás", "en": "Hail Damage Repair"}, "category": "body", "search_keywords": ["jégkár", "hail", "jégeső", "hail damage"], "description": "Jégeső okozta károk javítása"},
"alvazvedelem": {"name_translations": {"hu": "Alvázvédelem", "en": "Underbody Protection"}, "category": "body", "search_keywords": ["alváz", "alvázvédelem", "underbody", "alvázkezelés"], "description": "Alvázvédelem és alvázkezelés"},
"szelvedo_csere": {"name_translations": {"hu": "Szélvédő csere", "en": "Windshield Replacement"}, "category": "body", "search_keywords": ["szélvédő", "windshield", "ablak", "üveg"], "description": "Szélvédő és autóüveg csere"},
"muanyag_lokharito_hegesztes": {"name_translations": {"hu": "Műanyag lökhárító hegesztés", "en": "Plastic Bumper Welding"}, "category": "body", "search_keywords": ["lökhárító", "bumper", "műanyag hegesztés", "bumper repair"], "description": "Műanyag lökhárítók hegesztése és javítása"},
"kerekdob_szigeteles": {"name_translations": {"hu": "Kerékdob szigetelés", "en": "Wheel Arch Soundproofing"}, "category": "body", "search_keywords": ["kerékdob", "szigetelés", "soundproofing", "zajszigetelés"], "description": "Kerékdobok zajszigetelése"}
}
},
"fenyezes": {
"name_translations": {"hu": "Fényezés", "en": "Painting"},
"category": "paint",
"search_keywords": ["fényező", "fényezés", "painter", "lakkozás"],
"description": "Gépjármű fényezés és lakkozás",
"children": {
"elemfenyezes": {"name_translations": {"hu": "Elemfényezés", "en": "Panel Painting"}, "category": "paint", "search_keywords": ["elemfényezés", "panel painting", "részfényezés"], "description": "Egyedi karosszériaelemek fényezése"},
"teljes_polirozas": {"name_translations": {"hu": "Teljes polírozás", "en": "Full Polishing"}, "category": "paint", "search_keywords": ["polírozás", "polishing", "fényezés helyreállítás"], "description": "Teljes gépjármű polírozás és fényezés helyreállítás"},
"fenyszoro_polirozas": {"name_translations": {"hu": "Fényszóró polírozás", "en": "Headlight Polishing"}, "category": "paint", "search_keywords": ["fényszóró", "headlight", "polírozás", "homályosítás"], "description": "Műanyag fényszórók polírozása és átlátszóság helyreállítás"},
"teljes_fenyezes": {"name_translations": {"hu": "Teljes fényezés", "en": "Full Repaint"}, "category": "paint", "search_keywords": ["teljes fényezés", "full repaint", "átfényezés"], "description": "Teljes gépjármű átfényezése"},
"matricazas_foliak": {"name_translations": {"hu": "Matricázás / Fóliázás", "en": "Vinyl Wrap"}, "category": "paint", "search_keywords": ["matrica", "fólia", "vinyl wrap", "wrap", "színező fólia"], "description": "Gépjármű fóliázása és matricázása"}
}
},
"sebessegvalto_specialista": {
"name_translations": {"hu": "Sebességváltó specialista", "en": "Transmission Specialist"},
"category": "transmission",
"search_keywords": ["váltó", "sebességváltó", "transmission", "dsg"],
"description": "Sebességváltók javítása és karbantartása",
"children": {
"automata_valto_olajcsere": {"name_translations": {"hu": "Automata váltó olajcsere", "en": "Automatic Transmission Fluid Change"}, "category": "transmission", "search_keywords": ["automata váltó", "atf", "olajcsere", "transmission fluid"], "description": "Automata sebességváltó olajcseréje"},
"kettostomegu_lendkerek_csere": {"name_translations": {"hu": "Kettőstömegű lendkerék csere", "en": "Dual Mass Flywheel Replacement"}, "category": "transmission", "search_keywords": ["dmf", "lendkerék", "dual mass", "kettőstömegű"], "description": "Kettőstömegű lendkerék (DMF) cseréje"},
"dsg_javitas": {"name_translations": {"hu": "DSG javítás", "en": "DSG Repair"}, "category": "transmission", "search_keywords": ["dsg", "dsg javítás", "dsg váltó", "direct shift gearbox"], "description": "DSG sebességváltók javítása és szervize"},
"manualis_valto_felujitas": {"name_translations": {"hu": "Manuális váltó felújítás", "en": "Manual Gearbox Rebuild"}, "category": "transmission", "search_keywords": ["manuális váltó", "manual gearbox", "váltó felújítás"], "description": "Manuális sebességváltók felújítása"}
}
},
"felfuggesztes_specialista": {
"name_translations": {"hu": "Felfüggesztés specialista", "en": "Suspension Specialist"},
"category": "suspension",
"search_keywords": ["felfüggesztés", "suspension", "futómű", "állítás"],
"description": "Futómű és felfüggesztés javítása, beállítása",
"children": {
"futomu_allitas_geometria": {"name_translations": {"hu": "Futómű geometria állítás", "en": "Wheel Alignment"}, "category": "suspension", "search_keywords": ["geometria", "alignment", "futómű állítás", "kerékbeállítás"], "description": "Elsőkerék és hátsókerék geometria beállítása"},
"golyoscsuklo_csere": {"name_translations": {"hu": "Golyóscsukló csere", "en": "Ball Joint Replacement"}, "category": "suspension", "search_keywords": ["golyóscsukló", "ball joint", "felfüggesztés"], "description": "Felfüggesztés golyóscsuklók cseréje"},
"stabilizator_csere": {"name_translations": {"hu": "Stabilizátor csere", "en": "Stabilizer Bar Replacement"}, "category": "suspension", "search_keywords": ["stabilizátor", "stabilizer", "billegésgátló", "stabilizátor rúd"], "description": "Stabilizátor rudak és csatlakozók cseréje"},
"csendesito_gumi_csere": {"name_translations": {"hu": "Csendesítő gumi csere", "en": "Bushing Replacement"}, "category": "suspension", "search_keywords": ["csendesítő", "bushing", "szilent", "gumi persely"], "description": "Felfüggesztés csendesítő gumik cseréje"}
}
},
"fekrendszer_specialista": {
"name_translations": {"hu": "Fékrendszer specialista", "en": "Brake System Specialist"},
"category": "brake",
"search_keywords": ["fék", "brake", "fékrendszer", "féktárcsa"],
"description": "Fékrendszer javítása és karbantartása",
"children": {
"fektarcsa_csere": {"name_translations": {"hu": "Féktárcsa csere", "en": "Brake Disc Replacement"}, "category": "brake", "search_keywords": ["féktárcsa", "brake disc", "fék rotor"], "description": "Féktárcsák cseréje"},
"fekfolyadek_csere": {"name_translations": {"hu": "Fékfolyadék csere", "en": "Brake Fluid Change"}, "category": "brake", "search_keywords": ["fékfolyadék", "brake fluid", "fék olaj"], "description": "Fékfolyadék cseréje és légtelenítése"},
"kezifek_allitas": {"name_translations": {"hu": "Kézifék állítás", "en": "Handbrake Adjustment"}, "category": "brake", "search_keywords": ["kézifék", "handbrake", "rögzítőfék"], "description": "Kézifék beállítása"},
"abs_szenzor_csere": {"name_translations": {"hu": "ABS szenzor csere", "en": "ABS Sensor Replacement"}, "category": "brake", "search_keywords": ["abs", "abs szenzor", "blokkolásgátló"], "description": "ABS keréksebesség szenzorok cseréje"}
}
},
"hutorendszer_specialista": {
"name_translations": {"hu": "Hűtőrendszer specialista", "en": "Cooling System Specialist"},
"category": "cooling",
"search_keywords": ["hűtés", "cooling", "hűtő", "radiátor", "ventilátor"],
"description": "Motor hűtőrendszer javítása és karbantartása",
"children": {
"huto_csere": {"name_translations": {"hu": "Hűtő csere", "en": "Radiator Replacement"}, "category": "cooling", "search_keywords": ["hűtő", "radiátor", "radiator", "hűtővíz"], "description": "Motor hűtő radiátor cseréje"},
"hutoviz_csere": {"name_translations": {"hu": "Hűtővíz csere", "en": "Coolant Change"}, "category": "cooling", "search_keywords": ["hűtővíz", "coolant", "fagyálló", "antifreeze"], "description": "Hűtőfolyadék cseréje"},
"ventilator_javitas": {"name_translations": {"hu": "Ventilátor javítás", "en": "Cooling Fan Repair"}, "category": "cooling", "search_keywords": ["ventilátor", "cooling fan", "hűtőventilátor"], "description": "Hűtőventilátor motor és kapcsoló javítása"}
}
},
"uzemanyagrendszer_specialista": {
"name_translations": {"hu": "Üzemanyagrendszer specialista", "en": "Fuel System Specialist"},
"category": "fuel",
"search_keywords": ["üzemanyag", "fuel", "befecskendező", "üzemanyag szivattyú"],
"description": "Üzemanyag rendszer javítása és karbantartása",
"children": {
"uzemanyagszivattyu_csere": {"name_translations": {"hu": "Üzemanyag szivattyú csere", "en": "Fuel Pump Replacement"}, "category": "fuel", "search_keywords": ["üzemanyag szivattyú", "fuel pump", "benzinpumpa"], "description": "Üzemanyag szivattyú cseréje"},
"porlaszto_csere": {"name_translations": {"hu": "Porlasztó csere", "en": "Injector Replacement"}, "category": "fuel", "search_keywords": ["porlasztó", "injector", "befecskendező szelep"], "description": "Befecskendező szelepek cseréje"},
"gazolaj_szuro_csere": {"name_translations": {"hu": "Gázolaj szűrő csere", "en": "Diesel Fuel Filter Change"}, "category": "fuel", "search_keywords": ["gázolaj szűrő", "diesel filter", "üzemanyag szűrő"], "description": "Dízel üzemanyagszűrő cseréje"}
}
}
}
},
"jarmumoso_es_kozmetika": {
"name_translations": {"hu": "Járműmosó és Kozmetika", "en": "Car Wash and Detailing"},
"category": "detailing",
"search_keywords": ["mosó", "kozmetika", "detailing", "mosás", "takarítás"],
"description": "Gépjárműmosás, takarítás és kozmetikai szolgáltatások",
"children": {
"kezi_autamoso": {
"name_translations": {"hu": "Kézi Autómosó", "en": "Hand Car Wash"},
"category": "detailing",
"search_keywords": ["kézi mosás", "hand wash", "autómosó"],
"description": "Kézi erővel végzett autómosás és ápolás",
"children": {
"kulso_mosas": {"name_translations": {"hu": "Külső mosás", "en": "Exterior Wash"}, "category": "detailing", "search_keywords": ["külső mosás", "exterior wash", "autómosás"], "description": "Gépjármű külső mosása"},
"belso_takaritas": {"name_translations": {"hu": "Belső takarítás", "en": "Interior Cleaning"}, "category": "detailing", "search_keywords": ["belső", "interior", "takarítás", "porszívózás"], "description": "Gépjármű belső terének takarítása"},
"karpi_tisztitas": {"name_translations": {"hu": "Kárpittisztítás", "en": "Upholstery Cleaning"}, "category": "detailing", "search_keywords": ["kárpit", "upholstery", "ülés", "seat cleaning"], "description": "Ülések és kárpitok mélytisztítása"},
"borapolas": {"name_translations": {"hu": "Bőrápolás", "en": "Leather Care"}, "category": "detailing", "search_keywords": ["bőr", "leather", "bőrápolás", "bőrtisztítás"], "description": "Bőrülések ápolása és tisztítása"},
"keramia_bevonat": {"name_translations": {"hu": "Kerámia bevonat", "en": "Ceramic Coating"}, "category": "detailing", "search_keywords": ["kerámia", "ceramic coating", "bevonat", "nano"], "description": "Kerámia nano-bevonat felvitele a karosszériára"}
}
},
"onkiszolgalo_moso": {
"name_translations": {"hu": "Önkiszolgáló mosó", "en": "Self-Service Car Wash"},
"category": "detailing",
"search_keywords": ["önkiszolgáló", "self-service", "mosó", "automata mosó"],
"description": "Önkiszolgáló autómosó állomások"
},
"gepi_kefes_moso": {
"name_translations": {"hu": "Gépi / Kefés mosó", "en": "Automatic Car Wash"},
"category": "detailing",
"search_keywords": ["gépi mosó", "kefés mosó", "automata", "automatic wash"],
"description": "Automata kefés gépjárműmosók"
}
}
},
"mentes_szallitas_asszisztencia": {
"name_translations": {"hu": "Mentés, Szállítás és Asszisztencia", "en": "Towing, Transport and Assistance"},
"category": "roadside",
"search_keywords": ["mentés", "szállítás", "asszisztencia", "tow", "roadside"],
"description": "Autómentés, szállítás és úti segítségnyújtás",
"children": {
"automento": {
"name_translations": {"hu

View File

@@ -1846,7 +1846,7 @@ const brandHighlightIndex = ref(0)
const modelHighlightIndex = ref(0)
function onBrandInput() {
if (!form.brand) { showBrandSuggestions = false; return }
if (!form.brand) { showBrandSuggestions.value = false; return }
// Normalize search query: uppercase, remove spaces for matching
const q = form.brand.toUpperCase().replace(/\s+/g, '')
const all = catalogStore.brands || []

View File

@@ -278,12 +278,12 @@
</div>
</div>
<!-- Level 1: Iparág/Főcsoport (children of CHECKED Level 0 + universal Level 1) -->
<div v-if="allLevel1Categories.length > 0" class="mb-3 ml-4 pl-3 border-l-2 border-sf-accent/20">
<!-- Level 1: Iparág/Főcsoport (children of CHECKED Level 0) -->
<div v-if="vehicleLevel1Categories.length > 0" class="mb-3 ml-4 pl-3 border-l-2 border-sf-accent/20">
<p class="text-xs font-semibold text-slate-500 uppercase tracking-wider mb-2">{{ t('provider.level1Label') }}</p>
<div class="flex flex-wrap gap-2">
<label
v-for="l1 in allLevel1Categories"
v-for="l1 in vehicleLevel1Categories"
:key="l1.id"
class="inline-flex items-center gap-1.5 rounded-lg border border-slate-200 px-3 py-1.5 text-xs font-medium text-slate-600 transition-all hover:border-sf-accent/40 hover:bg-sf-accent/5 cursor-pointer"
:class="{ 'border-sf-accent bg-sf-accent/10 text-sf-accent': selectedCategoryIds.includes(l1.id) }"
@@ -298,6 +298,35 @@
</label>
</div>
</div>
<!-- Universal Level 1: Iparágak és Univerzális Szolgáltatások (independent of Level 0) -->
<div v-if="universalLevel1Categories.length > 0" class="mb-3 mt-4 pt-3 border-t-2 border-dashed border-amber-200">
<div class="flex items-center gap-2 mb-3">
<div class="flex h-6 w-6 items-center justify-center rounded-full bg-amber-50 text-amber-500">
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
</svg>
</div>
<span class="text-sm font-bold text-slate-700">{{ t('categories.universal_title') }}</span>
</div>
<p class="text-xs text-slate-400 mb-2">{{ t('categories.universal_hint') }}</p>
<div class="flex flex-wrap gap-2">
<label
v-for="l1 in universalLevel1Categories"
:key="l1.id"
class="inline-flex items-center gap-1.5 rounded-lg border border-amber-200 px-3 py-1.5 text-xs font-medium text-slate-600 transition-all hover:border-amber-400 hover:bg-amber-50 cursor-pointer"
:class="{ 'border-amber-500 bg-amber-50 text-amber-700 font-semibold': selectedCategoryIds.includes(l1.id) }"
>
<input
type="checkbox"
:checked="selectedCategoryIds.includes(l1.id)"
@change="toggleCategory(l1.id)"
class="h-3.5 w-3.5 rounded border-amber-300 text-amber-500 focus:ring-amber-300"
/>
{{ l1.name_hu || l1.key }}
</label>
</div>
</div>
</div>
<!-- -->
@@ -447,7 +476,7 @@
</template>
<script setup lang="ts">
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { ref, reactive, computed, onMounted, watch, watchEffect } from 'vue'
import { useI18n } from 'vue-i18n'
import api from '@/api/axios'
@@ -559,14 +588,15 @@ const level0Categories = computed(() => {
return categoryTree.value.filter(c => c.level === 0)
})
// ── Computed: All visible Level 1 categories ──
// Shows Level 1 children of checked Level 0 parents.
const allLevel1Categories = computed(() => {
// ── Computed: Vehicle-specific Level 1 categories ──
// Shows Level 1 children of CHECKED Level 0 parents only.
const vehicleLevel1Categories = computed(() => {
const checkedL0Ids = new Set(selectedCategoryIds.value)
const result: CategoryTreeNode[] = []
for (const l0 of level0Categories.value) {
if (l0.children.length > 0 && checkedL0Ids.has(l0.id)) {
for (const l1 of l0.children) {
// Avoid duplicates if same L1 appears under multiple L0
if (!result.some(r => r.id === l1.id)) {
result.push(l1)
}
@@ -576,6 +606,14 @@ const allLevel1Categories = computed(() => {
return result
})
// ── Computed: Universal Level 1 categories ──
// Level 1 categories that are root nodes (parent_id IS NULL) in the tree.
// These are independent of any vehicle type and always visible.
// Examples: "Üzemanyag és Töltőállomás", "Étel és Ital", "Szállás"
const universalLevel1Categories = computed(() => {
return categoryTree.value.filter(c => c.level === 1)
})
// ── Computed ──
const isFormValid = computed(() => {
return form.name.trim().length >= 2
@@ -909,6 +947,14 @@ onMounted(() => {
fetchCategories()
loadCategoryTree()
})
// 🔍 P0 Debug: Universal Level 1 categories count verification
watchEffect(() => {
console.log(`[ProviderQuickAddModal] universalLevel1Categories count: ${universalLevel1Categories.value.length}`)
if (universalLevel1Categories.value.length > 0) {
console.log(`[ProviderQuickAddModal] Universal categories:`, universalLevel1Categories.value.map(c => c.name_hu || c.name_en))
}
})
</script>
<style scoped>

View File

@@ -1349,6 +1349,8 @@ export default {
// ── Categories (Hybrid Category UI) ────────────────────────────────
categories: {
universal_title: 'Universal Services',
universal_hint: 'Vehicle-independent infrastructure',
block_a_title: 'Basic Categories (Vehicle & Industry)',
block_b_title: 'Specific Tasks & Tags',
search_placeholder: 'Search for a service, or type a new one (Enter)...',

View File

@@ -1349,6 +1349,8 @@ export default {
// ── Categories (Hybrid Category UI) ────────────────────────────────
categories: {
universal_title: 'Univerzális Szolgáltatások',
universal_hint: 'Járműtípustól független infrastruktúra',
block_a_title: 'Alap kategóriák (Jármű és Iparág)',
block_b_title: 'Specifikus feladatok és címkék',
search_placeholder: 'Keress szolgáltatást, vagy írj be újat (Enter)...',

View File

@@ -44,6 +44,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"
>
<div class="py-1">
<!-- Vezérlőpult (Dashboard) -->
<button
@click="navigateToDashboard"
class="flex w-full items-center gap-3 px-4 py-2.5 text-sm text-white/80 transition-all duration-150 hover:bg-white/5 hover:text-white"
>
<svg class="w-4 h-4 text-white/40" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
{{ t('header.dashboard') }}
</button>
<!-- Járművek -->
<button
@click="navigateToCard('vehicles')"
@@ -144,10 +155,16 @@ function navigateToCard(cardId: string) {
}
}
// ── Open external Service Finder page in new tab ───────────────────
// ── Navigate to Service Finder page (same tab) ─────────────────────
function openExternalServiceFinder() {
isHamburgerOpen.value = false
window.open('https://app.servicefinder.hu/dashboard/service-finder', '_blank', 'noopener,noreferrer')
router.push('/dashboard/service-finder')
}
// ── Navigate to Dashboard homepage ─────────────────────────────────
function navigateToDashboard() {
isHamburgerOpen.value = false
router.push('/dashboard')
}
// ── Close hamburger on outside click ──────────────────────────────

View File

@@ -442,11 +442,11 @@ const isSearchModalOpen = ref(false)
const isSfQuickAddOpen = ref(false)
/**
* Opens the external Service Finder page in a new tab.
* Navigates to the Service Finder page in the same tab.
* Used by the card click and the "Tervezett karbantartás" button.
*/
function openExternalServiceFinder() {
window.open('https://app.servicefinder.hu/dashboard/service-finder', '_blank', 'noopener,noreferrer')
router.push('/dashboard/service-finder')
}
/**