Files
service-finder/plans/p0_subscription_profile_menu_modal_plan.md
2026-06-23 21:11:21 +00:00

6.5 KiB

P0 Plan: Subscription Profile Menu & Modal

1. Overview

Add an "Előfizetés" (Subscription) menu item to the avatar dropdown in HeaderProfile.vue, which opens a modal showing real subscription data (package name, expiry, vehicle limits/usage, garage limits/usage) plus a "Renew" (Meghosszabbítás) button.

2. Files to Modify

# File Action Reason
1 frontend/src/components/header/HeaderProfile.vue Modify Add "Előfizetés" menu item between Profile and Admin Center
2 frontend/src/components/subscription/SubscriptionInfoModal.vue Create New modal component showing subscription details
3 frontend/src/i18n/en.ts Modify Add i18n keys for the new modal
4 frontend/src/i18n/hu.ts Modify Add Hungarian translations

3. Data Binding Paths

3.1 Current Plan Name

authStore.user?.subscription_plan          (individual mode)
activeOrg?.subscription_plan               (corporate mode)

3.2 Expiry Date

The backend UserResponse schema does NOT currently expose subscription_expires_at. However:

Current frontend approach (from SubscriptionStatusWidget.vue):

  • For corporate mode: activeOrg?.subscription_valid_until (but this field is NOT in the backend /organizations/my response)
  • For individual mode: authStore.user?.subscription_valid_until (but this field is NOT in the UserResponse schema)

Gap: The subscription_expires_at field exists on the backend User model but is not exposed in the UserResponse Pydantic schema. Similarly, subscription_expires_at exists on the Organization model but is not returned by the /organizations/my endpoint.

Plan: We will add subscription_expires_at to:

  1. UserResponse schema
  2. The /organizations/my endpoint response dict (line 222 area)

3.3 Vehicle Limits & Usage

From SubscriptionTier.rules.allowances:
  plan.rules.allowances.max_vehicles       (max limit)
  plan.rules.allowances.max_garages        (max limit)

Current usage from:
  vehicleStore.vehicles.length             (current vehicles count)
  authStore.myOrganizations.length         (current garages count)

3.4 Resolving the Tier

The tier is resolved via subscription_tier_id on the org/user. We need a new API endpoint or we can use the existing /subscriptions/public endpoint to fetch all tiers and match by name.

Better approach: Add a new lightweight endpoint GET /subscriptions/my that returns the current user's/org's subscription details including:

  • tier_name (the plan name)
  • tier_rules (the full rules JSONB)
  • expires_at (from subscription_expires_at)
  • current_vehicles count
  • current_garages count

This avoids multiple round-trips and complex client-side stitching.

3.5 Renewal Action

The "Meghosszabbítás" button should navigate to the existing SubscriptionPlansView which already shows plan cards with a "Details & Purchase" flow. The existing PlanDetailsModal handles the purchase simulation.

4. Proposed Architecture

Add subscription_expires_at to UserResponse schema and to the /organizations/my response.

4.2 New Component: SubscriptionInfoModal.vue

  • Glassmorphism style matching the dropdown
  • Shows:
    • Package name (display_name from tier rules, or plan name)
    • Expiry date with days-remaining countdown
    • Progress bar (same as SubscriptionStatusWidget)
    • Vehicle usage: "3 / 5 vehicles" with progress bar
    • Garage usage: "1 / 2 garages" with progress bar
    • "Meghosszabbítás" button → navigates to /subscription-plans

4.3 HeaderProfile.vue Changes

Add a new menu item between Profile and Admin Center:

<button @click="openSubscriptionModal">
  <svg>...</svg>
  {{ t('header.subscription') }}
</button>

4.4 Data Flow

  1. User clicks "Előfizetés" in dropdown
  2. Modal opens, reads data from:
    • authStore.user.subscription_plan / authStore.user.subscription_expires_at
    • adminPackagesStore.tiers (already fetched) to find matching tier by name
    • vehicleStore.vehicles.length for current vehicle count
    • authStore.myOrganizations.length for current garage count
  3. "Meghosszabbítás" button → router.push('/subscription-plans')

5. i18n Keys to Add

English (en.ts)

subscription: {
  // ... existing keys ...
  mySubscription: "My Subscription",
  renew: "Renew",
  expiresAt: "Expires at",
  vehicleUsage: "Vehicle Usage",
  garageUsage: "Garage Usage",
  of: "of",
}
header: {
  // ... existing keys ...
  subscription: "Subscription",
}

Hungarian (hu.ts)

subscription: {
  // ... existing keys ...
  mySubscription: "Előfizetésem",
  renew: "Meghosszabbítás",
  expiresAt: "Lejárat",
  vehicleUsage: "Járműhasználat",
  garageUsage: "Garázshasználat",
  of: "/",
}
header: {
  // ... existing keys ...
  subscription: "Előfizetés",
}

6. Risk Assessment

  • Data availability: subscription_expires_at is NOT currently exposed in the frontend-accessible schemas. We need to either:
    • (A) Add it to the backend schemas (recommended, clean)
    • (B) Create a new /subscriptions/my endpoint
    • (C) Use only the plan name and show "N/A" for expiry (fallback)
  • Tier rules resolution: The frontend adminPackagesStore already fetches all tiers. We can match by plan.name to get the rules.
  • Vehicle/garage counts: Already available in existing stores.

7. Implementation Order

  1. Add subscription_expires_at to backend UserResponse schema
  2. Add subscription_expires_at to /organizations/my response
  3. Create SubscriptionInfoModal.vue
  4. Add i18n keys
  5. Modify HeaderProfile.vue to add menu item and wire up modal
  6. Run sync_engine and verify