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:
- The
Usermodel hassubscription_expires_at - The
Organizationmodel hassubscription_plan,base_asset_limit,purchased_extra_slots,subscription_tier_id - The
OrganizationSubscriptionmodel hasvalid_until
Current frontend approach (from SubscriptionStatusWidget.vue):
- For corporate mode:
activeOrg?.subscription_valid_until(but this field is NOT in the backend/organizations/myresponse) - For individual mode:
authStore.user?.subscription_valid_until(but this field is NOT in theUserResponseschema)
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:
UserResponseschema- The
/organizations/myendpoint 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(fromsubscription_expires_at)current_vehiclescountcurrent_garagescount
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
4.1 Backend Changes (Optional but Recommended)
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
- User clicks "Előfizetés" in dropdown
- Modal opens, reads data from:
authStore.user.subscription_plan/authStore.user.subscription_expires_atadminPackagesStore.tiers(already fetched) to find matching tier by namevehicleStore.vehicles.lengthfor current vehicle countauthStore.myOrganizations.lengthfor current garage count
- "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_atis 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/myendpoint - (C) Use only the plan name and show "N/A" for expiry (fallback)
- Tier rules resolution: The frontend
adminPackagesStorealready fetches all tiers. We can match byplan.nameto get the rules. - Vehicle/garage counts: Already available in existing stores.
7. Implementation Order
- Add
subscription_expires_atto backendUserResponseschema - Add
subscription_expires_atto/organizations/myresponse - Create
SubscriptionInfoModal.vue - Add i18n keys
- Modify
HeaderProfile.vueto add menu item and wire up modal - Run sync_engine and verify