diff --git a/frontend/src/components/domain/setup-wizard/steps/QualitySetupStep.tsx b/frontend/src/components/domain/setup-wizard/steps/QualitySetupStep.tsx index 428da360..7093adbb 100644 --- a/frontend/src/components/domain/setup-wizard/steps/QualitySetupStep.tsx +++ b/frontend/src/components/domain/setup-wizard/steps/QualitySetupStep.tsx @@ -1,12 +1,135 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import type { SetupStepProps } from '../SetupWizard'; +import { useQualityTemplates, useCreateQualityTemplate } from '../../../../api/hooks/qualityTemplates'; +import { useCurrentTenant } from '../../../../stores/tenant.store'; +import { useAuthUser } from '../../../../stores/auth.store'; +import { QualityCheckType, ProcessStage } from '../../../../api/types/qualityTemplates'; +import type { QualityCheckTemplateCreate } from '../../../../api/types/qualityTemplates'; -export const QualitySetupStep: React.FC = () => { +export const QualitySetupStep: React.FC = ({ onUpdate }) => { const { t } = useTranslation(); + // Get tenant ID and user + const currentTenant = useCurrentTenant(); + const user = useAuthUser(); + const tenantId = currentTenant?.id || user?.tenant_id || ''; + const userId = user?.id || ''; + + // Fetch quality templates + const { data: templatesData, isLoading } = useQualityTemplates(tenantId); + const templates = templatesData?.templates || []; + + // Mutations + const createTemplateMutation = useCreateQualityTemplate(tenantId); + + // Form state + const [isAdding, setIsAdding] = useState(false); + const [formData, setFormData] = useState({ + name: '', + check_type: QualityCheckType.VISUAL, + description: '', + applicable_stages: [] as ProcessStage[], + is_required: false, + is_critical: false, + }); + const [errors, setErrors] = useState>({}); + + // Notify parent when count changes + useEffect(() => { + const count = templates.length; + onUpdate?.({ + itemsCount: count, + canContinue: count >= 2, + }); + }, [templates.length, onUpdate]); + + // Validation + const validateForm = (): boolean => { + const newErrors: Record = {}; + + if (!formData.name.trim()) { + newErrors.name = t('setup_wizard:quality.errors.name_required', 'Name is required'); + } + + if (formData.applicable_stages.length === 0) { + newErrors.stages = t('setup_wizard:quality.errors.stages_required', 'At least one stage is required'); + } + + setErrors(newErrors); + return Object.keys(newErrors).length === 0; + }; + + // Form handlers + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!validateForm()) return; + + try { + const templateData: QualityCheckTemplateCreate = { + name: formData.name, + check_type: formData.check_type, + description: formData.description || undefined, + applicable_stages: formData.applicable_stages, + is_required: formData.is_required, + is_critical: formData.is_critical, + is_active: true, + weight: formData.is_critical ? 10 : 5, + created_by: userId, + }; + + await createTemplateMutation.mutateAsync(templateData); + + // Reset form + resetForm(); + } catch (error) { + console.error('Error saving quality template:', error); + } + }; + + const resetForm = () => { + setFormData({ + name: '', + check_type: QualityCheckType.VISUAL, + description: '', + applicable_stages: [], + is_required: false, + is_critical: false, + }); + setErrors({}); + setIsAdding(false); + }; + + const toggleStage = (stage: ProcessStage) => { + const stages = formData.applicable_stages.includes(stage) + ? formData.applicable_stages.filter((s) => s !== stage) + : [...formData.applicable_stages, stage]; + setFormData({ ...formData, applicable_stages: stages }); + }; + + const checkTypeOptions = [ + { value: QualityCheckType.VISUAL, label: t('quality:type.visual', 'Visual Inspection'), icon: 'πŸ‘οΈ' }, + { value: QualityCheckType.MEASUREMENT, label: t('quality:type.measurement', 'Measurement'), icon: 'πŸ“' }, + { value: QualityCheckType.TEMPERATURE, label: t('quality:type.temperature', 'Temperature'), icon: '🌑️' }, + { value: QualityCheckType.WEIGHT, label: t('quality:type.weight', 'Weight'), icon: 'βš–οΈ' }, + { value: QualityCheckType.TIMING, label: t('quality:type.timing', 'Timing'), icon: '⏱️' }, + { value: QualityCheckType.CHECKLIST, label: t('quality:type.checklist', 'Checklist'), icon: 'βœ…' }, + ]; + + const stageOptions = [ + { value: ProcessStage.MIXING, label: t('quality:stage.mixing', 'Mixing') }, + { value: ProcessStage.PROOFING, label: t('quality:stage.proofing', 'Proofing') }, + { value: ProcessStage.SHAPING, label: t('quality:stage.shaping', 'Shaping') }, + { value: ProcessStage.BAKING, label: t('quality:stage.baking', 'Baking') }, + { value: ProcessStage.COOLING, label: t('quality:stage.cooling', 'Cooling') }, + { value: ProcessStage.FINISHING, label: t('quality:stage.finishing', 'Finishing') }, + { value: ProcessStage.PACKAGING, label: t('quality:stage.packaging', 'Packaging') }, + ]; + return (
+ {/* Why This Matters */}

@@ -19,20 +142,264 @@ export const QualitySetupStep: React.FC = () => {

-
-
- βœ… -
-

- {t('setup_wizard:quality.placeholder_title', 'Quality Standards')} -

-

- {t('setup_wizard:quality.placeholder_desc', 'This feature will be implemented in Phase 3')} -

-

- {t('setup_wizard:quality.min_required', 'Minimum required: 2 quality checks')} -

+ {/* Optional badge */} +
+ + {t('setup_wizard:optional', 'Optional')} + + + {t('setup_wizard:quality.optional_note', 'You can skip this and configure quality checks later')} +
+ + {/* Progress indicator */} +
+
+ + + + + {t('setup_wizard:quality.added_count', { count: templates.length, defaultValue: '{{count}} quality check added' })} + +
+ {templates.length >= 2 ? ( +
+ + + + {t('setup_wizard:quality.minimum_met', 'Minimum requirement met')} +
+ ) : ( +
+ {t('setup_wizard:quality.need_more', 'Need {{count}} more', { count: 2 - templates.length })} +
+ )} +
+ + {/* Templates list */} + {templates.length > 0 && ( +
+

+ {t('setup_wizard:quality.your_checks', 'Your Quality Checks')} +

+
+ {templates.map((template) => ( +
+
+
+
{template.name}
+ {template.is_critical && ( + + {t('quality:critical', 'Critical')} + + )} + {template.is_required && ( + + {t('quality:required', 'Required')} + + )} +
+
+ + {checkTypeOptions.find(opt => opt.value === template.check_type)?.label || template.check_type} + + {template.applicable_stages && template.applicable_stages.length > 0 && ( + + {template.applicable_stages.length} {t('quality:stages', 'stage(s)')} + + )} +
+
+
+ ))} +
+
+ )} + + {/* Add form */} + {isAdding ? ( +
+
+

+ {t('setup_wizard:quality.add_check', 'Add Quality Check')} +

+ +
+ +
+ {/* Name */} +
+ + setFormData({ ...formData, name: e.target.value })} + className={`w-full px-3 py-2 bg-[var(--bg-primary)] border ${errors.name ? 'border-[var(--color-error)]' : 'border-[var(--border-secondary)]'} rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] text-[var(--text-primary)]`} + placeholder={t('setup_wizard:quality.placeholders.name', 'e.g., Crust color check, Dough temperature')} + /> + {errors.name &&

{errors.name}

} +
+ + {/* Check Type */} +
+ +
+ {checkTypeOptions.map((option) => ( + + ))} +
+
+ + {/* Description */} +
+ +