Add improved production UI 3
This commit is contained in:
@@ -19,11 +19,14 @@ import { statusColors } from '../../../styles/colors';
|
||||
import { productionService, type QualityCheckResponse, QualityCheckStatus } from '../../../api';
|
||||
import { ProductionBatchResponse } from '../../../api/types/production';
|
||||
import { useCurrentTenant } from '../../../stores/tenant.store';
|
||||
import { useQualityTemplatesForStage, useExecuteQualityCheck } from '../../../api/hooks/qualityTemplates';
|
||||
import { ProcessStage, type QualityCheckTemplate, type QualityCheckExecutionRequest } from '../../../api/types/qualityTemplates';
|
||||
|
||||
export interface QualityCheckModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
batch: ProductionBatchResponse;
|
||||
processStage?: ProcessStage;
|
||||
onComplete?: (result: QualityCheckResult) => void;
|
||||
}
|
||||
|
||||
@@ -220,54 +223,72 @@ export const QualityCheckModal: React.FC<QualityCheckModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
batch,
|
||||
processStage,
|
||||
onComplete
|
||||
}) => {
|
||||
const currentTenant = useCurrentTenant();
|
||||
const tenantId = currentTenant?.id || '';
|
||||
|
||||
const [activeTab, setActiveTab] = useState('inspection');
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<string>('visual_inspection');
|
||||
const [selectedTemplate, setSelectedTemplate] = useState<QualityCheckTemplate | null>(null);
|
||||
const [currentCriteriaIndex, setCurrentCriteriaIndex] = useState(0);
|
||||
const [results, setResults] = useState<Record<string, QualityCheckResult>>({});
|
||||
const [results, setResults] = useState<Record<string, any>>({});
|
||||
const [finalNotes, setFinalNotes] = useState('');
|
||||
const [photos, setPhotos] = useState<File[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const template = QUALITY_CHECK_TEMPLATES[selectedTemplate as keyof typeof QUALITY_CHECK_TEMPLATES];
|
||||
const currentCriteria = template?.criteria[currentCriteriaIndex];
|
||||
const currentResult = currentCriteria ? results[currentCriteria.id] : undefined;
|
||||
// Get available templates for the current stage
|
||||
const currentStage = processStage || batch.current_process_stage;
|
||||
const {
|
||||
data: templatesData,
|
||||
isLoading: templatesLoading,
|
||||
error: templatesError
|
||||
} = useQualityTemplatesForStage(tenantId, currentStage!, true, {
|
||||
enabled: !!currentStage
|
||||
});
|
||||
|
||||
const updateResult = useCallback((criterionId: string, updates: Partial<QualityCheckResult>) => {
|
||||
// Execute quality check mutation
|
||||
const executeQualityCheckMutation = useExecuteQualityCheck(tenantId);
|
||||
|
||||
// Initialize selected template
|
||||
React.useEffect(() => {
|
||||
if (templatesData?.templates && templatesData.templates.length > 0 && !selectedTemplate) {
|
||||
setSelectedTemplate(templatesData.templates[0]);
|
||||
}
|
||||
}, [templatesData?.templates, selectedTemplate]);
|
||||
|
||||
const availableTemplates = templatesData?.templates || [];
|
||||
|
||||
const updateResult = useCallback((field: string, value: any, score?: number) => {
|
||||
setResults(prev => ({
|
||||
...prev,
|
||||
[criterionId]: {
|
||||
criterionId,
|
||||
value: '',
|
||||
score: 0,
|
||||
pass: false,
|
||||
timestamp: new Date().toISOString(),
|
||||
...prev[criterionId],
|
||||
...updates
|
||||
[field]: {
|
||||
value,
|
||||
score: score !== undefined ? score : (typeof value === 'boolean' ? (value ? 10 : 0) : value),
|
||||
pass_check: score !== undefined ? score >= 7 : (typeof value === 'boolean' ? value : value >= 7),
|
||||
timestamp: new Date().toISOString()
|
||||
}
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const calculateOverallScore = useCallback((): number => {
|
||||
if (!template || Object.keys(results).length === 0) return 0;
|
||||
if (!selectedTemplate || Object.keys(results).length === 0) return 0;
|
||||
|
||||
const totalWeight = template.criteria.reduce((sum, c) => sum + c.weight, 0);
|
||||
const weightedScore = Object.values(results).reduce((sum, result) => {
|
||||
const criterion = template.criteria.find(c => c.id === result.criterionId);
|
||||
return sum + (result.score * (criterion?.weight || 0));
|
||||
}, 0);
|
||||
const templateWeight = selectedTemplate.weight || 1;
|
||||
const resultValues = Object.values(results);
|
||||
|
||||
return totalWeight > 0 ? weightedScore / totalWeight : 0;
|
||||
}, [template, results]);
|
||||
if (resultValues.length === 0) return 0;
|
||||
|
||||
const averageScore = resultValues.reduce((sum: number, result: any) => sum + (result.score || 0), 0) / resultValues.length;
|
||||
return Math.min(10, averageScore * templateWeight);
|
||||
}, [selectedTemplate, results]);
|
||||
|
||||
const getCriticalFailures = useCallback((): string[] => {
|
||||
if (!template) return [];
|
||||
if (!selectedTemplate) return [];
|
||||
|
||||
const failures: string[] = [];
|
||||
template.criteria.forEach(criterion => {
|
||||
selectedTemplate.criteria.forEach(criterion => {
|
||||
if (criterion.isCritical && results[criterion.id]) {
|
||||
const result = results[criterion.id];
|
||||
if (criterion.type === 'boolean' && !result.value) {
|
||||
@@ -279,7 +300,7 @@ export const QualityCheckModal: React.FC<QualityCheckModalProps> = ({
|
||||
});
|
||||
|
||||
return failures;
|
||||
}, [template, results]);
|
||||
}, [selectedTemplate, results]);
|
||||
|
||||
const handlePhotoUpload = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const files = Array.from(event.target.files || []);
|
||||
@@ -287,7 +308,7 @@ export const QualityCheckModal: React.FC<QualityCheckModalProps> = ({
|
||||
}, []);
|
||||
|
||||
const handleComplete = async () => {
|
||||
if (!template) return;
|
||||
if (!selectedTemplate) return;
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
@@ -297,7 +318,7 @@ export const QualityCheckModal: React.FC<QualityCheckModalProps> = ({
|
||||
|
||||
const qualityData: QualityCheckData = {
|
||||
batchId: batch.id,
|
||||
checkType: template.id,
|
||||
checkType: selectedTemplate.id,
|
||||
inspector: currentTenant?.name || 'Inspector',
|
||||
startTime: new Date().toISOString(),
|
||||
results: Object.values(results),
|
||||
@@ -316,7 +337,7 @@ export const QualityCheckModal: React.FC<QualityCheckModalProps> = ({
|
||||
// Create quality check via API
|
||||
const checkData = {
|
||||
batch_id: batch.id,
|
||||
check_type: template.id,
|
||||
check_type: selectedTemplate.id,
|
||||
check_time: new Date().toISOString(),
|
||||
quality_score: overallScore / 10, // Convert to 0-1 scale
|
||||
pass_fail: passed,
|
||||
@@ -434,7 +455,7 @@ export const QualityCheckModal: React.FC<QualityCheckModalProps> = ({
|
||||
|
||||
const overallScore = calculateOverallScore();
|
||||
const criticalFailures = getCriticalFailures();
|
||||
const isComplete = template?.criteria.filter(c => c.required).every(c => results[c.id]) || false;
|
||||
const isComplete = selectedTemplate?.criteria.filter(c => c.required).every(c => results[c.id]) || false;
|
||||
|
||||
const statusIndicator: StatusIndicatorConfig = {
|
||||
color: statusColors.inProgress.primary,
|
||||
@@ -484,11 +505,11 @@ export const QualityCheckModal: React.FC<QualityCheckModalProps> = ({
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="inspection" className="space-y-6">
|
||||
{template && (
|
||||
{selectedTemplate && (
|
||||
<>
|
||||
{/* Progress Indicator */}
|
||||
<div className="grid grid-cols-6 gap-2">
|
||||
{template.criteria.map((criterion, index) => {
|
||||
{selectedTemplate.criteria.map((criterion, index) => {
|
||||
const result = results[criterion.id];
|
||||
const isCompleted = !!result;
|
||||
const isCurrent = index === currentCriteriaIndex;
|
||||
@@ -559,8 +580,8 @@ export const QualityCheckModal: React.FC<QualityCheckModalProps> = ({
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => setCurrentCriteriaIndex(Math.min(template.criteria.length - 1, currentCriteriaIndex + 1))}
|
||||
disabled={currentCriteriaIndex === template.criteria.length - 1}
|
||||
onClick={() => setCurrentCriteriaIndex(Math.min(selectedTemplate.criteria.length - 1, currentCriteriaIndex + 1))}
|
||||
disabled={currentCriteriaIndex === selectedTemplate.criteria.length - 1}
|
||||
>
|
||||
Siguiente
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user