10 KiB
🔴 Finance Module Recovery Blueprint — Root Cause Analysis & Recovery Plan
Date: 2026-07-26
Author: Architect Mode
Status: Pending Code Mode Execution
Priority: CRITICAL — Multiple systemic bugs across Frontend & Backend
STEP 1: ARCHITECTURAL COMPARISON — Dashboard vs. Finance
1.1 Layout Wrapper Comparison
| Aspect | DashboardView (Working) | FinanceMainView (Broken) |
|---|---|---|
| Parent Layout | PrivateLayout — provides BaseHeader with hamburger menu | FinanceLayout — provides same BaseHeader but with HeaderBackButton |
| Background | Self-contained: fixed inset-0 + bg-[url('@/assets/garage-bg.png')] in the component itself |
Duplicated in layout: same fixed inset-0 background in FinanceLayout |
| Header Teleport | NO Teleport usage — DashboardView owns its own header via the background div | USES Teleport: FinanceMainView teleports <span> into #header-teleport-target |
| Router Nesting | Dash is a child of PrivateLayout | Finance is a child of FinanceLayout |
1.2 Card Component Comparison
| Aspect | Working Dashboard Cards | Broken Finance Cards |
|---|---|---|
| Root Element | Single <div> with CSS classes directly on the card (e.g., MyVehiclesCard.vue:2-4) |
Same pattern: plain <div> with identical CSS ✅ |
| Click Handler | Invisible overlay <div> with @click (e.g., MyVehiclesCard.vue:6-9) |
Same pattern: invisible overlay with @click router push ✅ |
| Data Binding | Uses Pinia stores (financeStore, vehicleStore, authStore) | Same Pinia stores used ✅ |
| Component Import | Imports card components from dashboard/ | Standalone views — no card components imported, HTML inline |
1.3 Key Architectural Difference
The only structural difference is the Teleport usage. The working DashboardView uses <Teleport to="body"> ONLY for its Flip Modal overlay, which is always available in the DOM. The broken FinanceMainView uses <Teleport to="#header-teleport-target"> — a target that is conditionally rendered inside BaseHeader, which itself is nested inside FinanceLayout.
STEP 2: ROOT CAUSE ANALYSIS OF 4 KNOWN BUGS
🔴 BUG 1: Teleport Freeze — Failed to locate Teleport target with selector "#header-teleport-target"
Root Cause: Vue Router lifecycle timing mismatch between Teleport mount/unmount and DOM availability.
Evidence:
FinanceMainView.vue:38-40uses<Teleport to="#header-teleport-target">to inject a page title- The target
#header-teleport-targetis defined inBaseHeader.vue:15 BaseHeaderis rendered insideFinanceLayout.vue:35
Failure scenario:
- User navigates FROM
/financeTO/dashboard - Vue Router starts unmounting the
/financeroute tree: FinanceMainView → FinanceLayout → BaseHeader - FinanceMainView tries to unmount its Teleported
<span>from#header-teleport-target - BUT BaseHeader (which contains the target) has already been destroyed or is in the process of being destroyed
- Vue throws:
Failed to locate Teleport target with selector "#header-teleport-target" - The app freezes because the Teleport cleanup throws an unhandled error that halts the transition
The same issue can happen on MOUNT: if FinanceMainView renders before BaseHeader finishes mounting, the Teleport target doesn't exist yet.
Contrast with DashboardView: The working DashboardView:101 teleports to "body" — which always exists in the DOM, regardless of component lifecycle.
🔴 BUG 2: Component Resolution Error — Failed to resolve component: TechDataTab
Root Cause: Missing import statement in VehicleDetailModal.vue.
Evidence:
VehicleDetailModal.vue:284uses<TechDataTab />in its template- The
<script setup>section (lines 892-903) imports:VehiclePlateBadge,ProviderAutocomplete,ProviderQuickAddModal— but NOTTechDataTab VehicleDetailsView.vue:112correctly importsTechDataTab from '../../components/vehicles/tabs/TechDataTab.vue'- This is a copy-paste bug: the
<TechDataTab />usage was copied from VehicleDetailsView but the import was not
Failure scenario:
- User opens VehicleDetailModal and clicks the "Műszaki adatok" tab
- Vue tries to render
<TechDataTab />but cannot resolve the component - Vue emits warning:
[Vue warn]: Failed to resolve component: TechDataTab - The tab shows blank/empty content
Note: This bug is NOT caused by the finance module changes — it's a pre-existing bug in VehicleDetailModal.vue.
🟡 BUG 3: Fragment/Emit Warning — Extraneous non-emits event listeners (deleted)
Root Cause: DashboardView.vue:169 listens for @deleted but the current VehicleFormModal.vue does not declare or emit deleted.
Evidence:
DashboardView.vue:164-170:<VehicleFormModal @deleted="onVehicleDeleted" />VehicleFormModal.vue:1778-1782:const emit = defineEmits<{ (e: 'close'): void (e: 'saved'): void // NO "deleted" event declared! }>()
Failure scenario:
- Vue 3 sees
@deletedon the component but no matching emit declaration - Vue tries to fall through and treat it as a native DOM event on the root element
- Since VehicleFormModal's root is
<Teleport>(a fragment-like component), Vue cannot attach native DOM listeners - Vue warns:
Extraneous non-emits event listeners (deleted) were passed to component
🔴 BUG 4: Backend API Crash — HTTP 500 on GET /api/v1/providers/categories
Root Cause: The backend endpoint fails with an unhandled exception during ExpertiseTag query or ExpertiseCategoryOut serialization.
Evidence:
providers.py:149-183: The/categoriesendpoint queries ExpertiseTag and constructs ExpertiseCategoryOut objects- The endpoint wraps everything in try/except and returns HTTP 500 on any exception
- Git status shows modified schema files — possible schema mismatch
Required diagnostic: Run docker compose logs --tail 100 sf_api after reproducing the 500 error.
STEP 3: THE RECOVERY BLUEPRINT
Phase 1: Stabilize the Frontend
Fix 1.1: Teleport Target Lifecycle (BUG 1)
- File:
frontend_app/src/views/FinanceMainView.vue - Solution: Wrap Teleport in a v-if guard tied to onMounted/onBeforeUnmount lifecycle:
<Teleport v-if="teleportReady" to="#header-teleport-target"> - Also apply to: FinanceWalletsView, FinancePackagesView, FinanceTransactionsView
Fix 1.2: Missing TechDataTab Import (BUG 2)
- File:
frontend_app/src/components/vehicle/VehicleDetailModal.vue - Action: Add
import TechDataTab from '../vehicles/tabs/TechDataTab.vue'after line 903
Fix 1.3: Fragment Emit Warning (BUG 3)
- File:
frontend_app/src/components/dashboard/VehicleFormModal.vue - Action: Add
(e: 'deleted'): voidto defineEmits and emit on deletion
Phase 2: Diagnose & Fix the Backend
Fix 2.1: Backend 500 on providers/categories (BUG 4)
- Restart sf_api container and capture live logs
- Trigger the endpoint and analyze the Python traceback
- Fix based on actual error (model/schema/DB issue)
STEP 4: CODE MODE HAND-OFF INSTRUCTIONS
Task 1: Fix Teleport Lifecycle (BUG 1) — CRITICAL
- Open:
frontend_app/src/views/FinanceMainView.vue - Add
refandonBeforeUnmountto vue imports (line 237) - Add:
const teleportReady = ref(false) - In onMounted, add:
teleportReady.value = true - Add:
onBeforeUnmount(() => { teleportReady.value = false }) - Change line 38:
<Teleport v-if="teleportReady" to="#header-teleport-target"> - Apply same pattern to:
FinanceWalletsView.vue,FinancePackagesView.vue,FinanceTransactionsView.vue
Task 2: Fix Missing TechDataTab Import (BUG 2) — HIGH
- Open:
frontend_app/src/components/vehicle/VehicleDetailModal.vue - After line 903, add:
import TechDataTab from '../vehicles/tabs/TechDataTab.vue'
Task 3: Fix Fragment Emit Warning (BUG 3) — MEDIUM
- Open:
frontend_app/src/components/dashboard/VehicleFormModal.vue - At line 1778-1782, add
(e: 'deleted'): voidto defineEmits - Find delete/archive handler, add
emit('deleted')after successful operation
Task 4: Diagnose Backend 500 (BUG 4) — HIGH
- Run:
docker compose restart sf_api - Run diagnostic query:
docker compose exec sf_api python3 -c " import asyncio from app.database import async_session from sqlalchemy import select, text from app.models.data.expertise_tag import ExpertiseTag async def main(): async with async_session() as db: stmt = select(ExpertiseTag).order_by(ExpertiseTag.level, ExpertiseTag.id).limit(10) result = await db.execute(stmt) tags = result.scalars().all() print(f'Query OK: {len(tags)} tags') asyncio.run(main()) " - Based on output, fix providers.py or the ExpertiseTag model
Task 5: Verify All Fixes — MANDATORY
- Frontend build:
cd frontend_app && npm run build 2>&1 | tail -20 - Backend imports:
docker compose exec sf_api python3 -c "from app.main import app; print('OK')" - Navigate
/finance→ all 4 cards →/dashboard— verify no freeze - VehicleDetailModal → "Műszaki adatok" tab — verify renders
📋 Summary Table
| # | Bug | Severity | Root Cause | File(s) to Fix |
|---|---|---|---|---|
| 1 | Teleport Freeze | CRITICAL | Teleport target lifecycle mismatch | FinanceMainView.vue:38 + 3 sub-views |
| 2 | TechDataTab not found | HIGH | Missing import | VehicleDetailModal.vue:284 |
| 3 | Fragment emit warning | MEDIUM | Missing emit declaration | VehicleFormModal.vue:1778 |
| 4 | Backend 500 error | HIGH | Unhandled exception in /providers/categories | providers.py:149-183 (needs live diagnosis) |
⚠️ DO NOT MODIFY
The following files are working correctly and MUST NOT be changed:
DashboardView.vue— working dashboardPrivateLayout.vue— working layoutBaseHeader.vue— working headerFinancialCard.vue— working cardMyVehiclesCard.vue— working cardrouter/index.ts— correct route configuration