149 lines
6.5 KiB
Markdown
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
|