web/src/stores/subscription.js
Rama main · réplica de lectura del repositorio original.
import { defineStore } from 'pinia'
import axios from '@/utils/axios'
export const useSubscriptionStore = defineStore('subscription', {
state: () => ({
subscriptionStatus: null,
loading: false,
error: null,
lastFetched: null
}),
getters: {
/**
* Check if user has write access (active subscription)
*/
hasAccess: (state) => {
return state.subscriptionStatus?.has_access || false
},
/**
* Check if user's subscription is expired
*/
isExpired: (state) => {
return state.subscriptionStatus?.has_access === false
},
/**
* Get expiration date if available
*/
expiresAt: (state) => {
return state.subscriptionStatus?.expires_at || null
},
/**
* Get source of access (user, organization, both, none)
*/
source: (state) => {
return state.subscriptionStatus?.source || 'none'
},
/**
* Get user's personal subscription
*/
userSubscription: (state) => {
return state.subscriptionStatus?.user_subscription || null
},
/**
* Get organization subscriptions user benefits from
*/
orgSubscriptions: (state) => {
return state.subscriptionStatus?.org_subscriptions || []
},
/**
* Check if user has permanent free subscription
*/
isPermanentFree: (state) => {
const userSub = state.subscriptionStatus?.user_subscription
return userSub?.is_permanent_free || false
},
/**
* Get subscription type (free, monthly, annual)
*/
subscriptionType: (state) => {
const userSub = state.subscriptionStatus?.user_subscription
return userSub?.subscription_type || 'free'
},
/**
* Get formatted subscription type for display
*/
subscriptionTypeLabel: (state) => {
const userSub = state.subscriptionStatus?.user_subscription
if (!userSub) return 'No Subscription'
if (userSub.is_permanent_free) {
return 'Permanent Free'
}
const typeMap = {
free: 'Free',
monthly: 'Monthly',
annual: 'Annual'
}
return typeMap[userSub.subscription_type] || 'Unknown'
},
/**
* Get subscription status (active, expired, cancelled)
*/
status: (state) => {
const userSub = state.subscriptionStatus?.user_subscription
return userSub?.status || 'unknown'
},
/**
* Check if subscription data is stale (> 5 minutes old)
*/
isStale: (state) => {
if (!state.lastFetched) return true
const fiveMinutes = 5 * 60 * 1000
return Date.now() - state.lastFetched > fiveMinutes
}
},
actions: {
/**
* Fetch subscription status from API
*/
async fetchStatus() {
// Skip if already loading
if (this.loading) return
// Use cached data if fresh
if (!this.isStale && this.subscriptionStatus) {
return this.subscriptionStatus
}
this.loading = true
this.error = null
try {
const response = await axios.get('/api/subscriptions/status')
this.subscriptionStatus = response.data
this.lastFetched = Date.now()
return response.data
} catch (err) {
console.error('Failed to fetch subscription status:', err)
this.error = err.response?.data?.error || 'Failed to load subscription status'
// On 401, user is not authenticated - set default state
if (err.response?.status === 401) {
this.subscriptionStatus = null
}
throw err
} finally {
this.loading = false
}
},
/**
* Refresh subscription status (force fetch)
*/
async refresh() {
this.lastFetched = null
return this.fetchStatus()
},
/**
* Clear subscription status (on logout)
*/
clear() {
this.subscriptionStatus = null
this.loading = false
this.error = null
this.lastFetched = null
},
/**
* Set expired state (called when receiving 402 response)
*/
setExpired() {
if (this.subscriptionStatus) {
this.subscriptionStatus.has_access = false
this.subscriptionStatus.source = 'none'
}
}
}
})