# P0 Plan: Subscription Profile Menu & Modal ## 1. Overview Add an "Előfizetés" (Subscription) menu item to the avatar dropdown in [`HeaderProfile.vue`](frontend/src/components/header/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`](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`](frontend/src/components/subscription/SubscriptionInfoModal.vue) | **Create** | New modal component showing subscription details | | 3 | [`frontend/src/i18n/en.ts`](frontend/src/i18n/en.ts) | **Modify** | Add i18n keys for the new modal | | 4 | [`frontend/src/i18n/hu.ts`](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 [`User` model](backend/app/models/identity/identity.py:142) has `subscription_expires_at` - The [`Organization` model](backend/app/models/marketplace/organization.py:141-143) has `subscription_plan`, `base_asset_limit`, `purchased_extra_slots`, `subscription_tier_id` - The [`OrganizationSubscription` model](backend/app/models/core_logic.py:25-47) has `valid_until` **Current frontend approach** (from [`SubscriptionStatusWidget.vue`](frontend/src/components/dashboard/SubscriptionStatusWidget.vue:87-95)): - 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`](backend/app/schemas/user.py:54-73) 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`](frontend/src/views/SubscriptionPlansView.vue:309) 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`](frontend/src/views/SubscriptionPlansView.vue) which already shows plan cards with a "Details & Purchase" flow. The existing [`PlanDetailsModal`](frontend/src/components/subscription/PlanDetailsModal.vue) 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: ```html ``` ### 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`) ```json 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`) ```json 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