diff --git a/frontend/src/components/domain/dashboard/ConfigurationProgressWidget.tsx b/frontend/src/components/domain/dashboard/ConfigurationProgressWidget.tsx new file mode 100644 index 00000000..bedfd538 --- /dev/null +++ b/frontend/src/components/domain/dashboard/ConfigurationProgressWidget.tsx @@ -0,0 +1,298 @@ +import React, { useMemo } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useTranslation } from 'react-i18next'; +import { useCurrentTenant } from '../../../stores/tenant.store'; +import { useIngredients } from '../../../api/hooks/inventory'; +import { useSuppliers } from '../../../api/hooks/suppliers'; +import { useRecipes } from '../../../api/hooks/recipes'; +import { useQualityTemplates } from '../../../api/hooks/qualityTemplates'; +import { CheckCircle2, Circle, AlertCircle, ChevronRight, Package, Users, BookOpen, Shield } from 'lucide-react'; + +interface ConfigurationSection { + id: string; + title: string; + icon: React.ElementType; + path: string; + count: number; + minimum: number; + recommended: number; + isOptional?: boolean; + isComplete: boolean; + nextAction?: string; +} + +export const ConfigurationProgressWidget: React.FC = () => { + const { t } = useTranslation(); + const navigate = useNavigate(); + const currentTenant = useCurrentTenant(); + const tenantId = currentTenant?.id || ''; + + // Fetch configuration data + const { data: ingredients = [], isLoading: loadingIngredients } = useIngredients(tenantId, {}, { enabled: !!tenantId }); + const { data: suppliersData, isLoading: loadingSuppliers } = useSuppliers(tenantId, { enabled: !!tenantId }); + const suppliers = suppliersData?.suppliers || []; + const { data: recipesData, isLoading: loadingRecipes } = useRecipes(tenantId, { enabled: !!tenantId }); + const recipes = recipesData?.recipes || []; + const { data: qualityData, isLoading: loadingQuality } = useQualityTemplates(tenantId, { enabled: !!tenantId }); + const qualityTemplates = qualityData?.templates || []; + + const isLoading = loadingIngredients || loadingSuppliers || loadingRecipes || loadingQuality; + + // Calculate configuration sections + const sections: ConfigurationSection[] = useMemo(() => [ + { + id: 'inventory', + title: t('dashboard:config.inventory', 'Inventory'), + icon: Package, + path: '/app/operations/inventory', + count: ingredients.length, + minimum: 3, + recommended: 10, + isComplete: ingredients.length >= 3, + nextAction: ingredients.length < 3 ? t('dashboard:config.add_ingredients', 'Add at least {{count}} ingredients', { count: 3 - ingredients.length }) : undefined + }, + { + id: 'suppliers', + title: t('dashboard:config.suppliers', 'Suppliers'), + icon: Users, + path: '/app/operations/suppliers', + count: suppliers.length, + minimum: 1, + recommended: 3, + isComplete: suppliers.length >= 1, + nextAction: suppliers.length < 1 ? t('dashboard:config.add_supplier', 'Add your first supplier') : undefined + }, + { + id: 'recipes', + title: t('dashboard:config.recipes', 'Recipes'), + icon: BookOpen, + path: '/app/operations/recipes', + count: recipes.length, + minimum: 1, + recommended: 3, + isComplete: recipes.length >= 1, + nextAction: recipes.length < 1 ? t('dashboard:config.add_recipe', 'Create your first recipe') : undefined + }, + { + id: 'quality', + title: t('dashboard:config.quality', 'Quality Standards'), + icon: Shield, + path: '/app/operations/production/quality', + count: qualityTemplates.length, + minimum: 0, + recommended: 2, + isOptional: true, + isComplete: true, // Optional, so always "complete" + nextAction: qualityTemplates.length < 2 ? t('dashboard:config.add_quality', 'Add quality checks (optional)') : undefined + } + ], [ingredients.length, suppliers.length, recipes.length, qualityTemplates.length, t]); + + // Calculate overall progress + const { completedSections, totalSections, progressPercentage, nextIncompleteSection } = useMemo(() => { + const requiredSections = sections.filter(s => !s.isOptional); + const completed = requiredSections.filter(s => s.isComplete).length; + const total = requiredSections.length; + const percentage = Math.round((completed / total) * 100); + const nextIncomplete = sections.find(s => !s.isComplete && !s.isOptional); + + return { + completedSections: completed, + totalSections: total, + progressPercentage: percentage, + nextIncompleteSection: nextIncomplete + }; + }, [sections]); + + const isFullyConfigured = progressPercentage === 100; + + // Determine unlocked features + const unlockedFeatures = useMemo(() => { + const features: string[] = []; + if (ingredients.length >= 3) features.push(t('dashboard:config.features.inventory_tracking', 'Inventory Tracking')); + if (suppliers.length >= 1 && ingredients.length >= 3) features.push(t('dashboard:config.features.purchase_orders', 'Purchase Orders')); + if (recipes.length >= 1 && ingredients.length >= 3) features.push(t('dashboard:config.features.production_planning', 'Production Planning')); + if (recipes.length >= 1 && ingredients.length >= 3 && suppliers.length >= 1) features.push(t('dashboard:config.features.cost_analysis', 'Cost Analysis')); + return features; + }, [ingredients.length, suppliers.length, recipes.length, t]); + + if (isLoading) { + return ( +
+ {t('dashboard:config.subtitle', 'Configure essential features to get started')} +
++ 👉 {t('dashboard:config.next_step', 'Next Step')} +
++ {nextIncompleteSection.nextAction} +
+ ++ 🎉 {t('dashboard:config.features_unlocked', 'Features Unlocked!')} +
+