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

149 lines
6.5 KiB
Markdown

# 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
<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`)
```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