Add improved production UI 4

This commit is contained in:
Urtzi Alfaro
2025-09-23 22:11:34 +02:00
parent 7892c5a739
commit 87310ced5f
17 changed files with 1658 additions and 296 deletions

View File

@@ -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';

View File

@@ -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;

View File

@@ -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();