Add improved production UI 4
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// Export commonly used hooks
|
||||
export { default as useLocalStorage } from './useLocalStorage';
|
||||
export { default as useDebounce } from './useDebounce';
|
||||
export { default as useSubscription } from './useSubscription';
|
||||
export { default as useSubscription } from '../api/hooks/subscription';
|
||||
export { useTenantId, useTenantInfo, useRequiredTenant } from './useTenantId';
|
||||
@@ -1,179 +0,0 @@
|
||||
/**
|
||||
* Subscription hook for checking plan features and limits
|
||||
*/
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { subscriptionService } from '../api';
|
||||
import { useCurrentTenant } from '../stores';
|
||||
import { useAuthUser } from '../stores/auth.store';
|
||||
|
||||
export interface SubscriptionFeature {
|
||||
hasFeature: boolean;
|
||||
featureLevel?: string;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface SubscriptionLimits {
|
||||
canAddUser: boolean;
|
||||
canAddLocation: boolean;
|
||||
canAddProduct: boolean;
|
||||
usageData?: any;
|
||||
}
|
||||
|
||||
export interface SubscriptionInfo {
|
||||
plan: string;
|
||||
status: 'active' | 'inactive' | 'past_due' | 'cancelled';
|
||||
features: Record<string, any>;
|
||||
loading: boolean;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const useSubscription = () => {
|
||||
const [subscriptionInfo, setSubscriptionInfo] = useState<SubscriptionInfo>({
|
||||
plan: 'starter',
|
||||
status: 'active',
|
||||
features: {},
|
||||
loading: true,
|
||||
});
|
||||
|
||||
const currentTenant = useCurrentTenant();
|
||||
const user = useAuthUser();
|
||||
const tenantId = currentTenant?.id || user?.tenant_id;
|
||||
|
||||
// Load subscription data
|
||||
const loadSubscriptionData = useCallback(async () => {
|
||||
if (!tenantId) {
|
||||
setSubscriptionInfo(prev => ({ ...prev, loading: false, error: 'No tenant ID available' }));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setSubscriptionInfo(prev => ({ ...prev, loading: true, error: undefined }));
|
||||
|
||||
const usageSummary = await subscriptionService.getUsageSummary(tenantId);
|
||||
|
||||
setSubscriptionInfo({
|
||||
plan: usageSummary.plan,
|
||||
status: usageSummary.status,
|
||||
features: usageSummary.usage || {},
|
||||
loading: false,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error loading subscription data:', error);
|
||||
setSubscriptionInfo(prev => ({
|
||||
...prev,
|
||||
loading: false,
|
||||
error: 'Failed to load subscription data'
|
||||
}));
|
||||
}
|
||||
}, [tenantId]);
|
||||
|
||||
useEffect(() => {
|
||||
loadSubscriptionData();
|
||||
}, [loadSubscriptionData]);
|
||||
|
||||
// Check if user has a specific feature
|
||||
const hasFeature = useCallback(async (featureName: string): Promise<SubscriptionFeature> => {
|
||||
if (!tenantId) {
|
||||
return { hasFeature: false, reason: 'No tenant ID available' };
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await subscriptionService.hasFeature(tenantId, featureName);
|
||||
return {
|
||||
hasFeature: result.has_feature,
|
||||
featureLevel: result.feature_value,
|
||||
reason: result.reason
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error checking feature:', error);
|
||||
return { hasFeature: false, reason: 'Error checking feature access' };
|
||||
}
|
||||
}, [tenantId]);
|
||||
|
||||
// Check analytics access level
|
||||
const getAnalyticsAccess = useCallback((): { hasAccess: boolean; level: string; reason?: string } => {
|
||||
const { plan } = subscriptionInfo;
|
||||
|
||||
switch (plan) {
|
||||
case 'starter':
|
||||
return { hasAccess: true, level: 'basic' };
|
||||
case 'professional':
|
||||
return { hasAccess: true, level: 'advanced' };
|
||||
case 'enterprise':
|
||||
return { hasAccess: true, level: 'predictive' };
|
||||
default:
|
||||
return { hasAccess: false, level: 'none', reason: 'No valid subscription plan' };
|
||||
}
|
||||
}, [subscriptionInfo.plan]);
|
||||
|
||||
// Check if user can access specific analytics features
|
||||
const canAccessAnalytics = useCallback((requiredLevel: 'basic' | 'advanced' | 'predictive' = 'basic'): boolean => {
|
||||
const { hasAccess, level } = getAnalyticsAccess();
|
||||
|
||||
if (!hasAccess) return false;
|
||||
|
||||
const levelHierarchy = {
|
||||
'basic': 1,
|
||||
'advanced': 2,
|
||||
'predictive': 3
|
||||
};
|
||||
|
||||
return levelHierarchy[level as keyof typeof levelHierarchy] >= levelHierarchy[requiredLevel];
|
||||
}, [getAnalyticsAccess]);
|
||||
|
||||
// Check if user can access forecasting features
|
||||
const canAccessForecasting = useCallback((): boolean => {
|
||||
return canAccessAnalytics('advanced'); // Forecasting requires advanced or higher
|
||||
}, [canAccessAnalytics]);
|
||||
|
||||
// Check if user can access AI insights
|
||||
const canAccessAIInsights = useCallback((): boolean => {
|
||||
return canAccessAnalytics('predictive'); // AI Insights requires enterprise plan
|
||||
}, [canAccessAnalytics]);
|
||||
|
||||
// Check usage limits
|
||||
const checkLimits = useCallback(async (): Promise<SubscriptionLimits> => {
|
||||
if (!tenantId) {
|
||||
return {
|
||||
canAddUser: false,
|
||||
canAddLocation: false,
|
||||
canAddProduct: false
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const [userCheck, locationCheck, productCheck] = await Promise.all([
|
||||
subscriptionService.canAddUser(tenantId),
|
||||
subscriptionService.canAddLocation(tenantId),
|
||||
subscriptionService.canAddProduct(tenantId)
|
||||
]);
|
||||
|
||||
return {
|
||||
canAddUser: userCheck.can_add,
|
||||
canAddLocation: locationCheck.can_add,
|
||||
canAddProduct: productCheck.can_add,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('Error checking limits:', error);
|
||||
return {
|
||||
canAddUser: false,
|
||||
canAddLocation: false,
|
||||
canAddProduct: false
|
||||
};
|
||||
}
|
||||
}, [tenantId]);
|
||||
|
||||
return {
|
||||
subscriptionInfo,
|
||||
hasFeature,
|
||||
getAnalyticsAccess,
|
||||
canAccessAnalytics,
|
||||
canAccessForecasting,
|
||||
canAccessAIInsights,
|
||||
checkLimits,
|
||||
refreshSubscription: loadSubscriptionData,
|
||||
};
|
||||
};
|
||||
|
||||
export default useSubscription;
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { RouteConfig } from '../router/routes.config';
|
||||
import { useSubscription } from './useSubscription';
|
||||
import { useSubscription } from '../api/hooks/subscription';
|
||||
|
||||
export const useSubscriptionAwareRoutes = (routes: RouteConfig[]) => {
|
||||
const { subscriptionInfo, canAccessAnalytics } = useSubscription();
|
||||
|
||||
Reference in New Issue
Block a user