feat(analytics): Improve Production Analytics UI/UX and complete translations

IMPROVEMENTS:
1. Unified styling across all Production Analytics widgets
   - Replaced hardcoded colors with global CSS variable system
   - Improved consistency with design system color palette
   - Enhanced visual hierarchy and spacing
   - Added smooth transitions and hover states

2. Complete Basque (eu) translation
   - Extended from 105 lines to 667 lines (635% increase)
   - Now matches Spanish and English coverage
   - All analytics features fully translated

3. Widget-specific enhancements:
   - LiveBatchTrackerWidget: Better priority colors, improved summary cards
   - QualityScoreTrendsWidget: Enhanced score display, better trend indicators
   - AIInsightsWidget: Unified color scheme, improved insights cards
   - WasteDefectTrackerWidget: Better metric cards, improved recommendations

4. UX improvements:
   - Better mobile responsiveness
   - Consistent border and shadow treatments
   - Improved contrast for better readability
   - Enhanced visual feedback on interactions

TECHNICAL CHANGES:
- Replaced hardcoded colors (text-green-600, etc.) with CSS variables
- Used semantic color system: --color-success, --color-error, etc.
- Added transition-colors for smooth theme switching
- Improved component spacing consistency
- Enhanced accessibility with better contrast
This commit is contained in:
Claude
2025-11-15 14:34:44 +00:00
parent 843cd2bf5c
commit 1d52d01921
5 changed files with 818 additions and 242 deletions

View File

@@ -131,11 +131,11 @@ export const AIInsightsWidget: React.FC = () => {
const getTypeColor = (type: AIInsight['type']) => {
switch (type) {
case 'optimization': return 'text-green-600';
case 'prediction': return 'text-blue-600';
case 'anomaly': return 'text-red-600';
case 'recommendation': return 'text-purple-600';
default: return 'text-gray-600';
case 'optimization': return 'text-[var(--color-success)]';
case 'prediction': return 'text-[var(--color-info)]';
case 'anomaly': return 'text-[var(--color-error)]';
case 'recommendation': return 'text-[var(--color-secondary)]';
default: return 'text-[var(--text-secondary)]';
}
};
@@ -207,43 +207,43 @@ export const AIInsightsWidget: React.FC = () => {
</div>
}
>
<div className="space-y-6">
<div className="space-y-5">
{/* AI Insights Overview Stats */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="text-center p-4 bg-[var(--bg-secondary)] rounded-lg">
<Brain className="w-8 h-8 mx-auto text-purple-600 mb-2" />
<div className="text-center p-4 bg-[var(--bg-secondary)] rounded-lg border border-[var(--border-primary)] hover:border-[var(--border-focus)] transition-colors">
<Brain className="w-8 h-8 mx-auto text-[var(--color-secondary)] mb-2" />
<p className="text-2xl font-bold text-[var(--text-primary)]">{activeInsights.length}</p>
<p className="text-sm text-[var(--text-secondary)]">{t('ai.stats.active_insights')}</p>
<p className="text-sm text-[var(--text-secondary)] font-medium">{t('ai.stats.active_insights')}</p>
</div>
<div className="text-center p-4 bg-[var(--bg-secondary)] rounded-lg">
<AlertTriangle className="w-8 h-8 mx-auto text-red-600 mb-2" />
<p className="text-2xl font-bold text-[var(--text-primary)]">{highPriorityInsights.length}</p>
<p className="text-sm text-[var(--text-secondary)]">{t('ai.stats.high_priority')}</p>
<div className="text-center p-4 bg-[var(--color-error)]/10 rounded-lg border border-[var(--color-error)]/20 hover:border-[var(--color-error)]/40 transition-colors">
<AlertTriangle className="w-8 h-8 mx-auto text-[var(--color-error)] mb-2" />
<p className="text-2xl font-bold text-[var(--color-error)]">{highPriorityInsights.length}</p>
<p className="text-sm text-[var(--text-secondary)] font-medium">{t('ai.stats.high_priority')}</p>
</div>
<div className="text-center p-4 bg-[var(--bg-secondary)] rounded-lg">
<div className="w-8 h-8 mx-auto bg-green-100 dark:bg-green-900/20 rounded-full flex items-center justify-center mb-2">
<span className="text-green-600 font-bold text-sm"></span>
<div className="text-center p-4 bg-[var(--color-success)]/10 rounded-lg border border-[var(--color-success)]/20 hover:border-[var(--color-success)]/40 transition-colors">
<div className="w-8 h-8 mx-auto bg-[var(--color-success)]/20 rounded-full flex items-center justify-center mb-2">
<span className="text-[var(--color-success)] font-bold text-sm"></span>
</div>
<p className="text-2xl font-bold text-[var(--text-primary)]">{totalPotentialSavings}</p>
<p className="text-sm text-[var(--text-secondary)]">{t('ai.stats.potential_savings')}</p>
<p className="text-2xl font-bold text-[var(--color-success)]">{totalPotentialSavings}</p>
<p className="text-sm text-[var(--text-secondary)] font-medium">{t('ai.stats.potential_savings')}</p>
</div>
<div className="text-center p-4 bg-[var(--bg-secondary)] rounded-lg">
<div className="w-8 h-8 mx-auto bg-blue-100 dark:bg-blue-900/20 rounded-full flex items-center justify-center mb-2">
<span className="text-blue-600 font-bold text-sm">%</span>
<div className="text-center p-4 bg-[var(--color-info)]/10 rounded-lg border border-[var(--color-info)]/20 hover:border-[var(--color-info)]/40 transition-colors">
<div className="w-8 h-8 mx-auto bg-[var(--color-info)]/20 rounded-full flex items-center justify-center mb-2">
<span className="text-[var(--color-info)] font-bold text-sm">%</span>
</div>
<p className="text-2xl font-bold text-[var(--text-primary)]">{avgConfidence.toFixed(0)}%</p>
<p className="text-sm text-[var(--text-secondary)]">{t('ai.stats.avg_confidence')}</p>
<p className="text-2xl font-bold text-[var(--color-info)]">{avgConfidence.toFixed(0)}%</p>
<p className="text-sm text-[var(--text-secondary)] font-medium">{t('ai.stats.avg_confidence')}</p>
</div>
</div>
{/* AI Status */}
<div className="p-4 bg-green-50 dark:bg-green-900/20 rounded-lg">
<div className="p-4 bg-[var(--color-success)]/10 border border-[var(--color-success)]/20 rounded-lg">
<div className="flex items-center space-x-2">
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
<span className="text-sm font-medium text-green-600">
<div className="w-2 h-2 bg-[var(--color-success)] rounded-full animate-pulse"></div>
<span className="text-sm font-semibold text-[var(--color-success)]">
{t('ai.status.active')}
</span>
<span className="text-xs text-[var(--text-secondary)] ml-2">
<span className="text-xs text-[var(--text-secondary)] ml-2 font-medium">
{t('ai.last_updated')}
</span>
</div>
@@ -252,8 +252,8 @@ export const AIInsightsWidget: React.FC = () => {
{/* High Priority Insights */}
{highPriorityInsights.length > 0 && (
<div>
<h4 className="text-sm font-medium text-[var(--text-primary)] mb-3 flex items-center">
<AlertTriangle className="w-4 h-4 mr-2 text-red-600" />
<h4 className="text-sm font-semibold text-[var(--text-primary)] mb-3 flex items-center">
<AlertTriangle className="w-4 h-4 mr-2 text-[var(--color-error)]" />
{t('ai.high_priority_insights')} ({highPriorityInsights.length})
</h4>
<div className="space-y-3">
@@ -262,7 +262,7 @@ export const AIInsightsWidget: React.FC = () => {
const ImpactIcon = getImpactIcon(insight.impact.type);
return (
<div key={insight.id} className="p-4 bg-[var(--bg-secondary)] rounded-lg border border-red-200 dark:border-red-800">
<div key={insight.id} className="p-4 bg-[var(--bg-secondary)] rounded-lg border border-[var(--color-error)]/30 hover:border-[var(--color-error)]/50 transition-all duration-200">
<div className="flex items-start justify-between mb-3">
<div className="flex items-start space-x-3">
<TypeIcon className={`w-5 h-5 mt-1 ${getTypeColor(insight.type)}`} />
@@ -287,14 +287,14 @@ export const AIInsightsWidget: React.FC = () => {
</div>
{/* Impact */}
<div className="flex items-center justify-between p-3 bg-[var(--bg-tertiary)] rounded-lg">
<div className="flex items-center justify-between p-3 bg-[var(--color-success)]/5 border border-[var(--color-success)]/10 rounded-lg">
<div className="flex items-center space-x-2">
<ImpactIcon className="w-4 h-4 text-[var(--color-primary)]" />
<span className="text-sm font-medium text-[var(--text-primary)]">
<span className="text-sm font-semibold text-[var(--text-primary)]">
{t(`ai.impact.${insight.impact.type}`)}
</span>
</div>
<span className="text-lg font-bold text-green-600">
<span className="text-lg font-bold text-[var(--color-success)]">
{formatImpactValue(insight.impact)}
</span>
</div>
@@ -318,17 +318,17 @@ export const AIInsightsWidget: React.FC = () => {
{/* All Insights */}
<div>
<h4 className="text-sm font-medium text-[var(--text-primary)] mb-3 flex items-center">
<Brain className="w-4 h-4 mr-2" />
<h4 className="text-sm font-semibold text-[var(--text-primary)] mb-3 flex items-center">
<Brain className="w-4 h-4 mr-2 text-[var(--color-secondary)]" />
{t('ai.all_insights')} ({activeInsights.length})
</h4>
<div className="space-y-3 max-h-96 overflow-y-auto">
<div className="space-y-2 max-h-96 overflow-y-auto pr-1">
{activeInsights.map((insight) => {
const TypeIcon = getTypeIcon(insight.type);
const ImpactIcon = getImpactIcon(insight.impact.type);
return (
<div key={insight.id} className="p-3 bg-[var(--bg-secondary)] rounded-lg hover:bg-[var(--bg-tertiary)] transition-colors">
<div key={insight.id} className="p-3 bg-[var(--bg-secondary)] rounded-lg border border-[var(--border-primary)] hover:border-[var(--border-focus)] hover:shadow-sm transition-all duration-200">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-3">
<TypeIcon className={`w-4 h-4 ${getTypeColor(insight.type)}`} />
@@ -362,16 +362,16 @@ export const AIInsightsWidget: React.FC = () => {
</div>
{/* AI Performance Summary */}
<div className="p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<div className="flex items-start space-x-2">
<Brain className="w-5 h-5 text-blue-600 mt-0.5" />
<div>
<p className="text-sm font-medium text-blue-600">
<div className="p-4 bg-[var(--color-info)]/10 border border-[var(--color-info)]/20 rounded-lg">
<div className="flex items-start space-x-3">
<Brain className="w-5 h-5 text-[var(--color-info)] mt-0.5" />
<div className="flex-1">
<p className="text-sm font-semibold text-[var(--color-info)] mb-1">
{t('ai.performance.summary')}
</p>
<p className="text-xs text-[var(--text-secondary)] mt-1">
{implementedInsights.length} {t('ai.performance.insights_implemented')},
{totalPotentialSavings > 0 && `${totalPotentialSavings} ${t('ai.performance.in_savings_identified')}`}
<p className="text-xs text-[var(--text-secondary)]">
{implementedInsights.length} {t('ai.performance.insights_implemented')}
{totalPotentialSavings > 0 && `,${totalPotentialSavings} ${t('ai.performance.in_savings_identified')}`}
</p>
</div>
</div>

View File

@@ -1,6 +1,6 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Activity, Clock, AlertCircle, CheckCircle, Play, Pause } from 'lucide-react';
import { Activity, Clock, AlertCircle, CheckCircle, Play, Pause, Zap } from 'lucide-react';
import { AnalyticsWidget } from '../AnalyticsWidget';
import { Badge, ProgressBar, Button } from '../../../../ui';
import { useActiveBatches } from '../../../../../api/hooks/production';
@@ -86,6 +86,19 @@ export const LiveBatchTrackerWidget: React.FC = () => {
return 'info';
};
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'URGENT':
return 'text-[var(--color-error)]';
case 'HIGH':
return 'text-[var(--color-warning)]';
case 'MEDIUM':
return 'text-[var(--color-info)]';
default:
return 'text-[var(--text-secondary)]';
}
};
return (
<AnalyticsWidget
title={t('tracker.live_batch_tracker')}
@@ -110,7 +123,7 @@ export const LiveBatchTrackerWidget: React.FC = () => {
<p className="text-sm">{t('messages.no_active_batches_description')}</p>
</div>
) : (
<div className="space-y-3 max-h-96 overflow-y-auto">
<div className="space-y-3 max-h-96 overflow-y-auto pr-1">
{batches.map((batch) => {
const StatusIcon = getStatusIcon(batch.status);
const progress = calculateProgress(batch);
@@ -119,16 +132,16 @@ export const LiveBatchTrackerWidget: React.FC = () => {
return (
<div
key={batch.id}
className="p-4 bg-[var(--bg-secondary)] rounded-lg border border-[var(--border-primary)] hover:shadow-md transition-all"
className="p-4 bg-[var(--bg-secondary)] rounded-lg border border-[var(--border-primary)] hover:border-[var(--border-focus)] hover:shadow-md transition-all duration-200"
>
{/* Header */}
<div className="flex items-start justify-between mb-3">
<div className="flex items-start space-x-3 flex-1">
<div className="w-8 h-8 bg-[var(--color-primary)]/10 rounded-lg flex items-center justify-center mt-0.5">
<div className="w-8 h-8 bg-[var(--color-primary)]/10 rounded-lg flex items-center justify-center flex-shrink-0 mt-0.5">
<StatusIcon className="w-4 h-4 text-[var(--color-primary)]" />
</div>
<div className="flex-1 min-w-0">
<h4 className="font-semibold text-[var(--text-primary)] mb-1">
<h4 className="font-semibold text-[var(--text-primary)] mb-1 truncate">
{batch.product_name}
</h4>
<p className="text-sm text-[var(--text-secondary)] mb-1">
@@ -144,7 +157,7 @@ export const LiveBatchTrackerWidget: React.FC = () => {
</div>
</div>
</div>
<Badge variant={getStatusBadgeVariant(batch.status)}>
<Badge variant={getStatusBadgeVariant(batch.status)} className="flex-shrink-0">
{t(`status.${batch.status.toLowerCase()}`)}
</Badge>
</div>
@@ -152,11 +165,11 @@ export const LiveBatchTrackerWidget: React.FC = () => {
{/* Progress Bar */}
{batch.status !== ProductionStatus.PENDING && (
<div className="mb-3">
<div className="flex items-center justify-between mb-1">
<span className="text-xs text-[var(--text-secondary)]">
<div className="flex items-center justify-between mb-1.5">
<span className="text-xs font-medium text-[var(--text-secondary)]">
{t('tracker.progress')}
</span>
<span className="text-xs font-medium text-[var(--text-primary)]">
<span className="text-xs font-semibold text-[var(--text-primary)]">
{progress}%
</span>
</div>
@@ -169,33 +182,29 @@ export const LiveBatchTrackerWidget: React.FC = () => {
)}
{/* Details */}
<div className="flex items-center justify-between text-xs">
<div className="flex items-center space-x-4">
<div className="flex items-center justify-between text-xs flex-wrap gap-2">
<div className="flex items-center space-x-3">
{batch.priority && (
<span className={`font-medium ${
batch.priority === 'URGENT' ? 'text-red-600' :
batch.priority === 'HIGH' ? 'text-orange-600' :
batch.priority === 'MEDIUM' ? 'text-blue-600' :
'text-gray-600'
}`}>
<span className={`font-semibold ${getPriorityColor(batch.priority)}`}>
{t(`priority.${batch.priority.toLowerCase()}`)}
</span>
)}
{batch.is_rush_order && (
<span className="text-red-600 font-medium">
<span className="text-[var(--color-error)] font-semibold flex items-center">
<Zap className="w-3 h-3 mr-1" />
{t('batch.rush_order')}
</span>
)}
</div>
<div className="flex items-center space-x-1 text-[var(--text-secondary)]">
<Clock className="w-3 h-3" />
<div className="flex items-center space-x-1.5 text-[var(--text-secondary)]">
<Clock className="w-3.5 h-3.5" />
<span>
{eta ? (
<span className={eta.includes('delayed') ? 'text-red-600' : ''}>
<span className={eta.includes('delayed') ? 'text-[var(--color-error)] font-medium' : 'font-medium'}>
ETA: {eta}
</span>
) : (
t('tracker.pending_start')
<span className="font-medium">{t('tracker.pending_start')}</span>
)}
</span>
</div>
@@ -203,15 +212,19 @@ export const LiveBatchTrackerWidget: React.FC = () => {
{/* Equipment & Staff */}
{(batch.equipment_used?.length > 0 || batch.staff_assigned?.length > 0) && (
<div className="mt-2 pt-2 border-t border-[var(--border-primary)] text-xs text-[var(--text-tertiary)]">
<div className="mt-3 pt-3 border-t border-[var(--border-secondary)] text-xs">
{batch.equipment_used?.length > 0 && (
<div className="mb-1">
{t('batch.equipment')}: {batch.equipment_used.join(', ')}
<div className="mb-1.5 flex items-start">
<span className="text-[var(--text-secondary)] font-medium mr-2">{t('batch.equipment')}:</span>
<span className="text-[var(--text-tertiary)] flex-1">{batch.equipment_used.join(', ')}</span>
</div>
)}
{batch.staff_assigned?.length > 0 && (
<div>
{t('batch.staff')}: {batch.staff_assigned.length} {t('common.assigned')}
<div className="flex items-center">
<span className="text-[var(--text-secondary)] font-medium mr-2">{t('batch.staff')}:</span>
<span className="text-[var(--text-tertiary)]">
{batch.staff_assigned.length} {t('common.assigned')}
</span>
</div>
)}
</div>
@@ -224,31 +237,31 @@ export const LiveBatchTrackerWidget: React.FC = () => {
{/* Summary */}
{batches.length > 0 && (
<div className="pt-3 border-t border-[var(--border-primary)]">
<div className="grid grid-cols-4 gap-4 text-center text-xs">
<div>
<p className="font-medium text-green-600">
<div className="pt-4 mt-2 border-t border-[var(--border-primary)]">
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3 text-center text-xs">
<div className="p-3 bg-[var(--color-success)]/10 rounded-lg border border-[var(--color-success)]/20">
<p className="text-2xl font-bold text-[var(--color-success)] mb-1">
{batches.filter(b => b.status === ProductionStatus.COMPLETED).length}
</p>
<p className="text-[var(--text-tertiary)]">{t('status.completed')}</p>
<p className="text-[var(--text-secondary)] font-medium">{t('status.completed')}</p>
</div>
<div>
<p className="font-medium text-blue-600">
<div className="p-3 bg-[var(--color-info)]/10 rounded-lg border border-[var(--color-info)]/20">
<p className="text-2xl font-bold text-[var(--color-info)] mb-1">
{batches.filter(b => b.status === ProductionStatus.IN_PROGRESS).length}
</p>
<p className="text-[var(--text-tertiary)]">{t('status.in_progress')}</p>
<p className="text-[var(--text-secondary)] font-medium">{t('status.in_progress')}</p>
</div>
<div>
<p className="font-medium text-orange-600">
<div className="p-3 bg-[var(--color-warning)]/10 rounded-lg border border-[var(--color-warning)]/20">
<p className="text-2xl font-bold text-[var(--color-warning)] mb-1">
{batches.filter(b => b.status === ProductionStatus.PENDING).length}
</p>
<p className="text-[var(--text-tertiary)]">{t('status.pending')}</p>
<p className="text-[var(--text-secondary)] font-medium">{t('status.pending')}</p>
</div>
<div>
<p className="font-medium text-red-600">
<div className="p-3 bg-[var(--color-error)]/10 rounded-lg border border-[var(--color-error)]/20">
<p className="text-2xl font-bold text-[var(--color-error)] mb-1">
{batches.filter(b => [ProductionStatus.FAILED, ProductionStatus.CANCELLED].includes(b.status)).length}
</p>
<p className="text-[var(--text-tertiary)]">{t('tracker.issues')}</p>
<p className="text-[var(--text-secondary)] font-medium">{t('tracker.issues')}</p>
</div>
</div>
</div>

View File

@@ -61,7 +61,7 @@ export const QualityScoreTrendsWidget: React.FC = () => {
id: 'quality-trend',
name: t('quality.daily_quality_score'),
type: 'line',
color: '#16a34a',
color: 'var(--color-success)',
data: scores
}
];
@@ -83,7 +83,7 @@ export const QualityScoreTrendsWidget: React.FC = () => {
id: 'quality-distribution',
name: t('quality.score_distribution'),
type: 'bar',
color: '#d97706',
color: 'var(--color-primary)',
data: distribution.map(item => ({
x: item.range,
y: item.count,
@@ -94,17 +94,17 @@ export const QualityScoreTrendsWidget: React.FC = () => {
};
const getScoreColor = (score: number) => {
if (score >= 9) return 'text-green-600';
if (score >= 8) return 'text-blue-600';
if (score >= 7) return 'text-orange-600';
return 'text-red-600';
if (score >= 9) return 'text-[var(--color-success)]';
if (score >= 8) return 'text-[var(--color-info)]';
if (score >= 7) return 'text-[var(--color-warning)]';
return 'text-[var(--color-error)]';
};
const getScoreStatus = (score: number) => {
if (score >= 9) return { status: 'excellent', bgColor: 'bg-green-100 dark:bg-green-900/20' };
if (score >= 8) return { status: 'good', bgColor: 'bg-blue-100 dark:bg-blue-900/20' };
if (score >= 7) return { status: 'acceptable', bgColor: 'bg-orange-100 dark:bg-orange-900/20' };
return { status: 'needs_improvement', bgColor: 'bg-red-100 dark:bg-red-900/20' };
if (score >= 9) return { status: 'excellent', bgColor: 'bg-[var(--color-success)]/10 border border-[var(--color-success)]/20' };
if (score >= 8) return { status: 'good', bgColor: 'bg-[var(--color-info)]/10 border border-[var(--color-info)]/20' };
if (score >= 7) return { status: 'acceptable', bgColor: 'bg-[var(--color-warning)]/10 border border-[var(--color-warning)]/20' };
return { status: 'needs_improvement', bgColor: 'bg-[var(--color-error)]/10 border border-[var(--color-error)]/20' };
};
const scoreStatus = getScoreStatus(qualityData.averageScore);
@@ -124,23 +124,24 @@ export const QualityScoreTrendsWidget: React.FC = () => {
</Button>
}
>
<div className="space-y-4">
<div className="space-y-5">
{/* Quality Score Display */}
<div className="text-center">
<div className="flex items-center justify-center space-x-2 mb-2">
<Star className={`w-6 h-6 ${getScoreColor(qualityData.averageScore)}`} />
<span className="text-3xl font-bold text-[var(--text-primary)]">
{qualityData.averageScore.toFixed(1)}/10
<div className="text-center p-4 bg-[var(--bg-secondary)] rounded-lg border border-[var(--border-primary)]">
<div className="flex items-center justify-center space-x-3 mb-3">
<Star className={`w-8 h-8 fill-current ${getScoreColor(qualityData.averageScore)}`} />
<span className="text-4xl font-bold text-[var(--text-primary)]">
{qualityData.averageScore.toFixed(1)}
</span>
<span className="text-xl text-[var(--text-secondary)] font-medium">/10</span>
</div>
<div className={`inline-flex items-center space-x-2 px-3 py-1 rounded-full text-sm ${scoreStatus.bgColor}`}>
<div className={`inline-flex items-center space-x-2 px-4 py-1.5 rounded-full text-sm font-medium ${scoreStatus.bgColor}`}>
<span className={getScoreColor(qualityData.averageScore)}>
{t(`quality.${scoreStatus.status}`)}
</span>
</div>
<div className="flex items-center justify-center space-x-1 mt-2">
<TrendIcon className={`w-4 h-4 ${qualityData.trend >= 0 ? 'text-green-600' : 'text-red-600'}`} />
<span className={`text-sm ${qualityData.trend >= 0 ? 'text-green-600' : 'text-red-600'}`}>
<div className="flex items-center justify-center space-x-1.5 mt-3 pt-3 border-t border-[var(--border-secondary)]">
<TrendIcon className={`w-4 h-4 ${qualityData.trend >= 0 ? 'text-[var(--color-success)]' : 'text-[var(--color-error)]'}`} />
<span className={`text-sm font-semibold ${qualityData.trend >= 0 ? 'text-[var(--color-success)]' : 'text-[var(--color-error)]'}`}>
{qualityData.trend > 0 ? '+' : ''}{qualityData.trend.toFixed(1)} {t('quality.vs_last_week')}
</span>
</div>
@@ -148,17 +149,17 @@ export const QualityScoreTrendsWidget: React.FC = () => {
{/* Key Metrics */}
<div className="grid grid-cols-2 gap-4">
<div className="p-4 bg-[var(--bg-secondary)] rounded-lg text-center">
<div className="text-2xl font-bold text-[var(--text-primary)] mb-1">
<div className="p-4 bg-[var(--bg-secondary)] rounded-lg border border-[var(--border-primary)] text-center hover:border-[var(--border-focus)] transition-colors">
<div className="text-3xl font-bold text-[var(--text-primary)] mb-1">
{qualityData.totalChecks}
</div>
<p className="text-sm text-[var(--text-secondary)]">{t('stats.total_checks')}</p>
<p className="text-sm text-[var(--text-secondary)] font-medium">{t('stats.total_checks')}</p>
</div>
<div className="p-4 bg-[var(--bg-secondary)] rounded-lg text-center">
<div className="text-2xl font-bold text-green-600 mb-1">
<div className="p-4 bg-[var(--color-success)]/10 rounded-lg border border-[var(--color-success)]/20 text-center hover:border-[var(--color-success)]/40 transition-colors">
<div className="text-3xl font-bold text-[var(--color-success)] mb-1">
{qualityData.passRate.toFixed(1)}%
</div>
<p className="text-sm text-[var(--text-secondary)]">{t('stats.pass_rate')}</p>
<p className="text-sm text-[var(--text-secondary)] font-medium">{t('stats.pass_rate')}</p>
</div>
</div>
@@ -197,36 +198,36 @@ export const QualityScoreTrendsWidget: React.FC = () => {
{/* Quality Insights */}
<div className="grid grid-cols-3 gap-3 text-center">
<div className="p-3 bg-[var(--bg-secondary)] rounded-lg">
<p className="text-lg font-bold text-green-600">
<div className="p-3 bg-[var(--color-success)]/10 rounded-lg border border-[var(--color-success)]/20">
<p className="text-2xl font-bold text-[var(--color-success)] mb-1">
{Math.round(qualityData.totalChecks * 0.35)}
</p>
<p className="text-xs text-[var(--text-secondary)]">{t('quality.excellent_scores')}</p>
<p className="text-xs text-[var(--text-secondary)] font-medium">{t('quality.excellent_scores')}</p>
</div>
<div className="p-3 bg-[var(--bg-secondary)] rounded-lg">
<p className="text-lg font-bold text-blue-600">
<div className="p-3 bg-[var(--color-info)]/10 rounded-lg border border-[var(--color-info)]/20">
<p className="text-2xl font-bold text-[var(--color-info)] mb-1">
{Math.round(qualityData.totalChecks * 0.30)}
</p>
<p className="text-xs text-[var(--text-secondary)]">{t('quality.good_scores')}</p>
<p className="text-xs text-[var(--text-secondary)] font-medium">{t('quality.good_scores')}</p>
</div>
<div className="p-3 bg-[var(--bg-secondary)] rounded-lg">
<p className="text-lg font-bold text-red-600">
<div className="p-3 bg-[var(--color-error)]/10 rounded-lg border border-[var(--color-error)]/20">
<p className="text-2xl font-bold text-[var(--color-error)] mb-1">
{Math.round(qualityData.totalChecks * 0.15)}
</p>
<p className="text-xs text-[var(--text-secondary)]">{t('quality.needs_improvement')}</p>
<p className="text-xs text-[var(--text-secondary)] font-medium">{t('quality.needs_improvement')}</p>
</div>
</div>
{/* Quality Recommendations */}
{qualityData.averageScore < 8 && (
<div className="p-3 bg-orange-50 dark:bg-orange-900/20 rounded-lg">
<div className="flex items-start space-x-2">
<Star className="w-4 h-4 mt-0.5 text-orange-600" />
<div>
<p className="text-sm font-medium text-orange-600">
<div className="p-4 bg-[var(--color-warning)]/10 border border-[var(--color-warning)]/20 rounded-lg">
<div className="flex items-start space-x-3">
<Star className="w-5 h-5 mt-0.5 text-[var(--color-warning)]" />
<div className="flex-1">
<p className="text-sm font-semibold text-[var(--color-warning)] mb-1">
{t('quality.recommendations.improve_quality')}
</p>
<p className="text-xs text-[var(--text-secondary)] mt-1">
<p className="text-xs text-[var(--text-secondary)]">
{t('quality.recommendations.focus_consistency')}
</p>
</div>
@@ -235,14 +236,14 @@ export const QualityScoreTrendsWidget: React.FC = () => {
)}
{qualityData.averageScore >= 9 && (
<div className="p-3 bg-green-50 dark:bg-green-900/20 rounded-lg">
<div className="flex items-start space-x-2">
<Award className="w-4 h-4 mt-0.5 text-green-600" />
<div>
<p className="text-sm font-medium text-green-600">
<div className="p-4 bg-[var(--color-success)]/10 border border-[var(--color-success)]/20 rounded-lg">
<div className="flex items-start space-x-3">
<Award className="w-5 h-5 mt-0.5 text-[var(--color-success)]" />
<div className="flex-1">
<p className="text-sm font-semibold text-[var(--color-success)] mb-1">
{t('quality.recommendations.excellent_quality')}
</p>
<p className="text-xs text-[var(--text-secondary)] mt-1">
<p className="text-xs text-[var(--text-secondary)]">
{t('quality.recommendations.maintain_standards')}
</p>
</div>

View File

@@ -154,10 +154,10 @@ export const WasteDefectTrackerWidget: React.FC = () => {
const getWasteStatus = () => {
const totalWastePercentage = wasteData.wastePercentage + wasteData.defectPercentage;
if (totalWastePercentage <= 3) return { status: 'excellent', color: 'text-green-600', bgColor: 'bg-green-100 dark:bg-green-900/20' };
if (totalWastePercentage <= 5) return { status: 'good', color: 'text-blue-600', bgColor: 'bg-blue-100 dark:bg-blue-900/20' };
if (totalWastePercentage <= 8) return { status: 'warning', color: 'text-orange-600', bgColor: 'bg-orange-100 dark:bg-orange-900/20' };
return { status: 'critical', color: 'text-red-600', bgColor: 'bg-red-100 dark:bg-red-900/20' };
if (totalWastePercentage <= 3) return { status: 'excellent', color: 'text-[var(--color-success)]', bgColor: 'bg-[var(--color-success)]/10 border border-[var(--color-success)]/20' };
if (totalWastePercentage <= 5) return { status: 'good', color: 'text-[var(--color-info)]', bgColor: 'bg-[var(--color-info)]/10 border border-[var(--color-info)]/20' };
if (totalWastePercentage <= 8) return { status: 'warning', color: 'text-[var(--color-warning)]', bgColor: 'bg-[var(--color-warning)]/10 border border-[var(--color-warning)]/20' };
return { status: 'critical', color: 'text-[var(--color-error)]', bgColor: 'bg-[var(--color-error)]/10 border border-[var(--color-error)]/20' };
};
const wasteStatus = getWasteStatus();
@@ -177,43 +177,43 @@ export const WasteDefectTrackerWidget: React.FC = () => {
</Button>
}
>
<div className="space-y-4">
<div className="space-y-5">
{/* Overall Waste Metrics */}
<div className="grid grid-cols-3 gap-4">
<div className="p-3 bg-[var(--bg-secondary)] rounded-lg text-center">
<div className="p-4 bg-[var(--color-error)]/10 border border-[var(--color-error)]/20 rounded-lg text-center hover:border-[var(--color-error)]/40 transition-colors">
<div className="flex items-center justify-center space-x-2 mb-1">
<Trash2 className="w-4 h-4 text-red-600" />
<span className="text-lg font-bold text-[var(--text-primary)]">
<Trash2 className="w-5 h-5 text-[var(--color-error)]" />
<span className="text-2xl font-bold text-[var(--color-error)]">
{totalWastePercentage.toFixed(1)}%
</span>
</div>
<p className="text-xs text-[var(--text-secondary)]">{t('quality.total_waste')}</p>
<p className="text-xs text-[var(--text-secondary)] font-medium">{t('quality.total_waste')}</p>
</div>
<div className="p-3 bg-[var(--bg-secondary)] rounded-lg text-center">
<div className="p-4 bg-[var(--color-warning)]/10 border border-[var(--color-warning)]/20 rounded-lg text-center hover:border-[var(--color-warning)]/40 transition-colors">
<div className="flex items-center justify-center space-x-2 mb-1">
<AlertTriangle className="w-4 h-4 text-orange-600" />
<span className="text-lg font-bold text-[var(--text-primary)]">
<AlertTriangle className="w-5 h-5 text-[var(--color-warning)]" />
<span className="text-2xl font-bold text-[var(--color-warning)]">
{wasteData.totalDefects}
</span>
</div>
<p className="text-xs text-[var(--text-secondary)]">{t('quality.total_defects')}</p>
<p className="text-xs text-[var(--text-secondary)] font-medium">{t('quality.total_defects')}</p>
</div>
<div className="p-3 bg-[var(--bg-secondary)] rounded-lg text-center">
<div className="p-4 bg-[var(--color-success)]/10 border border-[var(--color-success)]/20 rounded-lg text-center hover:border-[var(--color-success)]/40 transition-colors">
<div className="flex items-center justify-center space-x-2 mb-1">
<TrendingDown className="w-4 h-4 text-green-600" />
<span className="text-lg font-bold text-[var(--text-primary)]">
<TrendingDown className="w-5 h-5 text-[var(--color-success)]" />
<span className="text-2xl font-bold text-[var(--color-success)]">
{totalWasteCost.toFixed(0)}
</span>
</div>
<p className="text-xs text-[var(--text-secondary)]">{t('cost.waste_cost')}</p>
<p className="text-xs text-[var(--text-secondary)] font-medium">{t('cost.waste_cost')}</p>
</div>
</div>
{/* Waste Status */}
<div className={`p-3 rounded-lg ${wasteStatus.bgColor}`}>
<div className={`p-4 rounded-lg ${wasteStatus.bgColor}`}>
<div className="flex items-center space-x-2">
<AlertTriangle className={`w-4 h-4 ${wasteStatus.color}`} />
<span className={`text-sm font-medium ${wasteStatus.color}`}>
<AlertTriangle className={`w-5 h-5 ${wasteStatus.color}`} />
<span className={`text-sm font-semibold ${wasteStatus.color}`}>
{t(`quality.status.${wasteStatus.status}`)}
</span>
</div>
@@ -285,28 +285,28 @@ export const WasteDefectTrackerWidget: React.FC = () => {
</div>
{/* Reduction Recommendations */}
<div className="space-y-2">
<div className="space-y-3">
{wasteSources.filter(s => s.severity === 'high').length > 0 && (
<div className="flex items-start space-x-2 p-3 bg-red-50 dark:bg-red-900/20 rounded-lg">
<AlertTriangle className="w-4 h-4 mt-0.5 text-red-600" />
<div>
<p className="text-sm font-medium text-red-600">
<div className="flex items-start space-x-3 p-4 bg-[var(--color-error)]/10 border border-[var(--color-error)]/20 rounded-lg">
<AlertTriangle className="w-5 h-5 mt-0.5 text-[var(--color-error)]" />
<div className="flex-1">
<p className="text-sm font-semibold text-[var(--color-error)] mb-1">
{t('quality.recommendations.high_waste_detected')}
</p>
<p className="text-xs text-[var(--text-secondary)] mt-1">
<p className="text-xs text-[var(--text-secondary)]">
{t('quality.recommendations.check_temperature_timing')}
</p>
</div>
</div>
)}
<div className="flex items-start space-x-2 p-3 bg-blue-50 dark:bg-blue-900/20 rounded-lg">
<Target className="w-4 h-4 mt-0.5 text-blue-600" />
<div>
<p className="text-sm font-medium text-blue-600">
<div className="flex items-start space-x-3 p-4 bg-[var(--color-info)]/10 border border-[var(--color-info)]/20 rounded-lg">
<Target className="w-5 h-5 mt-0.5 text-[var(--color-info)]" />
<div className="flex-1">
<p className="text-sm font-semibold text-[var(--color-info)] mb-1">
{t('quality.recommendations.improvement_opportunity')}
</p>
<p className="text-xs text-[var(--text-secondary)] mt-1">
<p className="text-xs text-[var(--text-secondary)]">
{totalWastePercentage > 5
? t('quality.recommendations.reduce_waste_target', { target: '3%' })
: t('quality.recommendations.maintain_quality_standards')