12 KiB
🔵 Finance Module — FleetView Layout Clone + Enum Fix Blueprint
Date: 2026-07-26
Author: Architect Mode
Status: Pending Code Mode Execution
Priority: MEDIUM — Layout fix and enum data repair
STEP 1: THE WORKING LAYOUT — FleetView Analysis
Route: /organization/:org_id/vehicles
Component: FleetView.vue wrapped in OrganizationLayout.vue
FleetView.vue DOM Hierarchy (Key Elements)
<div class="relative min-h-screen">
<!-- Fixed background -->
<div class="fixed inset-0 z-0">
<div class="absolute inset-0 bg-[url('@/assets/garage-bg.png')] bg-cover bg-center bg-fixed"></div>
</div>
<!-- Dark overlay -->
<div class="fixed inset-0 z-0 bg-slate-900/65 pointer-events-none"></div>
<!-- ═══ MAIN CONTENT CONTAINER ═══ -->
<div class="relative z-10 mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
<!-- Page header: flex justify-between -->
<div class="mb-8 flex items-center justify-between">
<div>
<h1 class="text-3xl font-bold text-white">{{ title }}</h1>
<p class="mt-1 text-sm text-white/60">{{ subtitle }}</p>
</div>
<div class="flex items-center gap-3">
<!-- Action buttons + Back button -->
</div>
</div>
<!-- Content Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Cards -->
</div>
</div>
</div>
OrganizationLayout.vue Role
OrganizationLayout.vue provides:
BaseHeaderwith company name + hamburger menumain class="relative z-10"→<router-view />- NO background — the child view (
FleetView) provides its own
FinanceLayout.vue Current State
FinanceLayout.vue provides:
BaseHeaderwithHeaderLogo+LanguageSwitcher+HeaderProfile- ALREADY HAS background —
fixed inset-0 z-0+bg-[url('@/assets/garage-bg.png')]+ dark overlaybg-black/30 main class="relative z-10"→<router-view />
Critical Difference: FinanceLayout vs OrganizationLayout
| Property | OrganizationLayout | FinanceLayout |
|---|---|---|
| Background | ❌ None (child provides) | ✅ Already provides garage-bg.png |
| Dark overlay | ❌ None | ✅ Already provides bg-black/30 |
| Header | Company name + hamburger | Logo + lang switcher + profile |
main class |
relative z-10 |
relative z-10 |
Conclusion: Since FinanceLayout already provides the background + overlay, the child views should NOT duplicate them. The fix is to make FinanceMainView.vue use the same inner container pattern as FleetView (line 12: relative z-10 mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8) but SKIP the background/overlay divs (which are in FinanceLayout).
What's Currently Wrong with FinanceMainView
Current structure (after previous fixes):
<!-- FinanceMainView.vue template: -->
<div class="flex flex-col justify-end min-h-[85vh] pb-8">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full">
<div class="mb-8 flex items-center justify-between"> ...header... </div>
<div class="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4"> ...cards... </div>
</div>
</div>
Problems:
justify-end min-h-[85vh]— This is the DashboardView pattern (bottom-aligned). FleetView usespy-8(top-aligned). The finance cards should be top-aligned with proper padding.lg:grid-cols-5— Dashboard uses 5-column for its 5 cards. Finance has 4 cards. Should use whatever fits well.- No
relativeon outer wrapper — needed for z-index context.
STEP 2: THE FIX — Clone FleetView Inner Structure
For FinanceMainView.vue:
<template>
<div class="relative min-h-screen">
<!-- ═══ MAIN CONTENT CONTAINER (FleetView.vue line 12 clone) ═══ -->
<div class="relative z-10 mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
<!-- ── Page header ── -->
<div class="mb-8 flex items-center justify-between">
<div>
<h1 class="text-3xl font-bold text-white">💰 {{ t('menu.finance') }}</h1>
</div>
<div class="flex items-center gap-3">
<button
@click="router.push('/dashboard')"
class="flex items-center gap-2 rounded-xl bg-white/10 px-4 py-2 text-sm text-white/80 transition-all duration-200 hover:bg-white/20 hover:text-white"
>
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
{{ t('garage.backToDashboard') }}
</button>
</div>
</div>
<!-- ── 4-Card Grid ── -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
...cards...
</div>
</div>
</div>
</template>
Key changes:
- Outer wrapper:
<div class="relative min-h-screen">(replacesflex flex-col justify-end min-h-[85vh] pb-8) - Content container:
<div class="relative z-10 mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">— exact clone of FleetView:12 - Header stays nested inside the
mx-autocontainer - Grid:
lg:grid-cols-4instead oflg:grid-cols-5(4 cards instead of 5) - NO background divs (FinanceLayout provides them)
Same fix for other 3 finance views
FinanceWalletsView.vue, FinancePackagesView.vue, FinanceTransactionsView.vue — each needs the same structural fix: remove flex flex-col justify-start min-h-[85vh] py-8 wrapper and replace with relative min-h-screen + relative z-10 mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8.
STEP 3: LEDGER_STATUS ENUM FIX
3A: The Enum Definition
class LedgerStatus(str, enum.Enum):
PENDING = "PENDING"
SUCCESS = "SUCCESS"
FAILED = "FAILED"
REFUNDED = "REFUNDED"
REFUND = "REFUND"
3B: The Seed Script (BROKEN)
DB_STATUSES = ["pending", "completed", "failed", "cancelled"]
Mapping to valid enum values:
| Seed value | Valid Enum | Reason |
|---|---|---|
pending → |
PENDING |
Case fix only |
completed → |
SUCCESS |
Closest semantic match (completed payment = success) |
failed → |
FAILED |
Case fix only |
cancelled → |
REFUNDED |
Cancelled payments are typically refunded |
3C: Fix Plan
Fix A — Seed script:
DB_STATUSES = ["PENDING", "SUCCESS", "FAILED", "REFUNDED"]
Fix B — Database cleanup SQL:
UPDATE audit.financial_ledger SET status = 'PENDING' WHERE status::text = 'pending';
UPDATE audit.financial_ledger SET status = 'SUCCESS' WHERE status::text = 'completed';
UPDATE audit.financial_ledger SET status = 'FAILED' WHERE status::text = 'failed';
UPDATE audit.financial_ledger SET status = 'REFUNDED' WHERE status::text = 'cancelled';
STEP 4: CODE MODE HAND-OFF — Task List
Task 1: Restructure FinanceMainView layout (FleetView clone)
File: frontend_app/src/views/FinanceMainView.vue
Replace the entire <template> section. The current outer structure:
<div class="flex flex-col justify-end min-h-[85vh] pb-8">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full">
<!-- header inside container (already correct from previous fix) -->
<!-- grid: lg:grid-cols-5 -->
</div>
</div>
Replace with:
<div class="relative min-h-screen">
<div class="relative z-10 mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8">
<!-- header (same as current, already correct) -->
<!-- grid: change lg:grid-cols-5 → lg:grid-cols-4 -->
</div>
</div>
- Change the outermost
<div>: removeflex flex-col justify-end min-h-[85vh] pb-8→ addrelative min-h-screen - Change the content container:
mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full→relative z-10 mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8(addrelative z-10andpy-8, removew-full) - Change all card grid:
lg:grid-cols-5→lg:grid-cols-4(4 cards, not 5) - Also change Card 1: remove
lg:col-span-2since on a 4-col grid each card gets 1 column
Task 2: Restructure FinanceWalletsView layout
File: frontend_app/src/views/FinanceWalletsView.vue
Same structural change:
- Outer:
flex flex-col justify-start min-h-[85vh] py-8→relative min-h-screen - Inner content:
mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 w-full→relative z-10 mx-auto max-w-7xl px-4 py-8 sm:px-6 lg:px-8
Task 3: Restructure FinancePackagesView layout
File: frontend_app/src/views/FinancePackagesView.vue
Same structural change as Task 2.
Task 4: Restructure FinanceTransactionsView layout
File: frontend_app/src/views/FinanceTransactionsView.vue
Same structural change as Task 2.
Task 5: Fix seed_financial_ledger.py ledger_status enum (3A)
File: backend/scripts/seed_financial_ledger.py:20
Change:
DB_STATUSES = ["pending", "completed", "failed", "cancelled"]
To:
DB_STATUSES = ["PENDING", "SUCCESS", "FAILED", "REFUNDED"]
Task 6: Fix existing DB ledger_status bad data (3B)
Run SQL:
docker compose exec shared-postgres psql -U service_finder_app -d service_finder -c "
UPDATE audit.financial_ledger SET status = 'PENDING' WHERE status::text = 'pending';
UPDATE audit.financial_ledger SET status = 'SUCCESS' WHERE status::text = 'completed';
UPDATE audit.financial_ledger SET status = 'FAILED' WHERE status::text = 'failed';
UPDATE audit.financial_ledger SET status = 'REFUNDED' WHERE status::text = 'cancelled';
SELECT status::text, COUNT(*) as cnt FROM audit.financial_ledger GROUP BY status ORDER BY status;
"
Task 7: Full verification
-
Frontend build:
docker compose exec sf_public_frontend npm run build 2>&1 | tail -5 -
Backend wallet balance API (no enum crash):
docker compose exec sf_api python3 -c " import requests, json r = requests.post('http://localhost:8000/api/v1/auth/login', data={'username': 'admin@profibot.hu', 'password': 'Admin123!'}) token = r.json()['access_token'] resp = requests.get('http://localhost:8000/api/v1/billing/wallet/balance', headers={'Authorization': f'Bearer {token}'}) print(f'Status: {resp.status_code}') if resp.status_code == 200: d = resp.json() print(f'total={d.get(\"total\")} earned={d.get(\"earned\")}') else: print(f'ERROR: {resp.text}') "
📋 Summary
| # | Task | File(s) |
|---|---|---|
| 1 | Clone FleetView layout to FinanceMainView | FinanceMainView.vue |
| 2 | Clone FleetView layout to FinanceWalletsView | FinanceWalletsView.vue |
| 3 | Clone FleetView layout to FinancePackagesView | FinancePackagesView.vue |
| 4 | Clone FleetView layout to FinanceTransactionsView | FinanceTransactionsView.vue |
| 5 | Fix seed enum: ledger_status |
seed_financial_ledger.py:20 |
| 6 | Fix DB: normalize ledger_status values |
PostgreSQL audit.financial_ledger |
| 7 | Verify: build + API | Build + /billing/wallet/balance |
⚠️ DO NOT MODIFY
FleetView.vue— reference patternOrganizationLayout.vue— reference patternFinanceLayout.vue— already correct (provides background)BaseHeader.vue— workingrouter/index.ts— correct