feat: Add comprehensive i18n support to QualityTemplateWizard

Added full internationalization support for the Quality Template wizard:

Translation keys added (en/es/eu):
- Scoring methods (weighted average, pass/fail, percentage, points-based)
- Advanced fields (check points, parameters, thresholds, scoring criteria)
- Section headers (check points config, advanced config, responsibility, control settings)
- Control settings (active template, photo evidence, critical control point, notify on failure)

Component updates:
- Translated all hardcoded strings in QualityTemplateWizard.tsx
- Implemented useTranslation hook
- Updated all labels, placeholders, tooltips, and section headers
- Added translations for scoring configuration section
- Translated advanced options including JSONB configuration fields
- Translated responsibility & requirements section
- Translated control settings checkboxes

Follows established pattern from InventoryWizard.tsx for consistency.
This commit is contained in:
Claude
2025-11-10 13:16:49 +00:00
parent ebabe4cd40
commit 45d18ef980
4 changed files with 198 additions and 70 deletions

View File

@@ -1,4 +1,5 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { WizardStep, WizardStepProps } from '../../../ui/WizardModal/WizardModal';
import { AdvancedOptionsSection } from '../../../ui/AdvancedOptionsSection';
import Tooltip from '../../../ui/Tooltip/Tooltip';
@@ -11,6 +12,7 @@ interface WizardDataProps extends WizardStepProps {
// Single comprehensive step with all fields
const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataChange }) => {
const { t } = useTranslation('wizards');
const [templateData, setTemplateData] = useState({
// Required fields
name: data.name || '',
@@ -60,10 +62,10 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
<div className="space-y-6">
<div className="text-center pb-4 border-b border-[var(--border-primary)]">
<h3 className="text-lg font-semibold text-[var(--text-primary)] mb-2">
Quality Template Details
{t('qualityTemplate.templateDetails')}
</h3>
<p className="text-sm text-[var(--text-secondary)]">
Fill in the required information to create a quality check template
{t('qualityTemplate.fillRequiredInfo')}
</p>
</div>
@@ -71,40 +73,40 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="md:col-span-2">
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Name *
{t('qualityTemplate.fields.name')} *
</label>
<input
type="text"
value={templateData.name}
onChange={(e) => handleDataChange({ ...templateData, name: e.target.value })}
placeholder="E.g., Bread Quality Control, Hygiene Inspection"
placeholder={t('qualityTemplate.fields.namePlaceholder')}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
/>
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Check Type *
{t('qualityTemplate.fields.checkType')} *
</label>
<select
value={templateData.checkType}
onChange={(e) => handleDataChange({ ...templateData, checkType: e.target.value })}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
>
<option value="product_quality">Product Quality</option>
<option value="process_hygiene">Process Hygiene</option>
<option value="equipment">Equipment</option>
<option value="safety">Safety</option>
<option value="cleaning">Cleaning</option>
<option value="temperature">Temperature Control</option>
<option value="documentation">Documentation</option>
<option value="product_quality">{t('qualityTemplate.checkTypes.product_quality')}</option>
<option value="process_hygiene">{t('qualityTemplate.checkTypes.process_hygiene')}</option>
<option value="equipment">{t('qualityTemplate.checkTypes.equipment')}</option>
<option value="safety">{t('qualityTemplate.checkTypes.safety')}</option>
<option value="cleaning">{t('qualityTemplate.checkTypes.cleaning')}</option>
<option value="temperature">{t('qualityTemplate.checkTypes.temperature')}</option>
<option value="documentation">{t('qualityTemplate.checkTypes.documentation')}</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Weight *
<Tooltip content="Importance weight for scoring (0.0-10.0)">
{t('qualityTemplate.fields.weight')} *
<Tooltip content={t('qualityTemplate.fields.weightTooltip')}>
<Info className="inline w-4 h-4 ml-1 text-[var(--text-tertiary)]" />
</Tooltip>
</label>
@@ -123,12 +125,12 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
{/* Basic Information */}
<div className="border-t border-[var(--border-primary)] pt-4">
<h4 className="text-sm font-semibold text-[var(--text-primary)] mb-3">Basic Information</h4>
<h4 className="text-sm font-semibold text-[var(--text-primary)] mb-3">{t('qualityTemplate.sections.basicInformation')}</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Template Code (Optional)
<Tooltip content="Leave empty to auto-generate from backend, or enter custom code">
{t('qualityTemplate.fields.templateCode')} ({t('common.optional')})
<Tooltip content={t('qualityTemplate.fields.templateCodeTooltip')}>
<Info className="inline w-4 h-4 ml-1 text-[var(--text-tertiary)]" />
</Tooltip>
</label>
@@ -136,14 +138,14 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
type="text"
value={templateData.templateCode}
onChange={(e) => handleDataChange({ ...templateData, templateCode: e.target.value })}
placeholder="Leave empty for auto-generation"
placeholder={t('qualityTemplate.fields.templateCodePlaceholder')}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
/>
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Version
{t('qualityTemplate.fields.version')}
</label>
<input
type="text"
@@ -156,12 +158,12 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
<div className="md:col-span-2">
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Description
{t('qualityTemplate.fields.description')}
</label>
<textarea
value={templateData.description}
onChange={(e) => handleDataChange({ ...templateData, description: e.target.value })}
placeholder="Detailed description of the quality check template"
placeholder={t('qualityTemplate.fields.descriptionPlaceholder')}
rows={2}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
/>
@@ -169,8 +171,8 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
<div className="md:col-span-2">
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Applicable Stages
<Tooltip content="Comma-separated list of production stages: e.g., mixing, proofing, baking, cooling">
{t('qualityTemplate.fields.applicableStages')}
<Tooltip content={t('qualityTemplate.fields.applicableStagesTooltip')}>
<Info className="inline w-4 h-4 ml-1 text-[var(--text-tertiary)]" />
</Tooltip>
</label>
@@ -178,7 +180,7 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
type="text"
value={templateData.applicableStages}
onChange={(e) => handleDataChange({ ...templateData, applicableStages: e.target.value })}
placeholder="mixing, proofing, baking, cooling"
placeholder={t('qualityTemplate.fields.applicablePlaceholder')}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
/>
</div>
@@ -187,28 +189,28 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
{/* Scoring Configuration */}
<div className="border-t border-[var(--border-primary)] pt-4">
<h4 className="text-sm font-semibold text-[var(--text-primary)] mb-3">Scoring Configuration</h4>
<h4 className="text-sm font-semibold text-[var(--text-primary)] mb-3">{t('qualityTemplate.sections.scoringConfiguration')}</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Scoring Method
{t('qualityTemplate.scoringMethods.scoringMethod')}
</label>
<select
value={templateData.scoringMethod}
onChange={(e) => handleDataChange({ ...templateData, scoringMethod: e.target.value })}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
>
<option value="weighted_average">Weighted Average</option>
<option value="pass_fail">Pass/Fail</option>
<option value="percentage">Percentage</option>
<option value="points">Points-based</option>
<option value="weighted_average">{t('qualityTemplate.scoringMethods.weightedAverage')}</option>
<option value="pass_fail">{t('qualityTemplate.scoringMethods.passFail')}</option>
<option value="percentage">{t('qualityTemplate.scoringMethods.percentage')}</option>
<option value="points">{t('qualityTemplate.scoringMethods.pointsBased')}</option>
</select>
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Pass Threshold (%)
<Tooltip content="Minimum score required to pass (0-100)">
{t('qualityTemplate.advancedFields.passThresholdPercent')}
<Tooltip content={t('tooltips.passThreshold')}>
<Info className="inline w-4 h-4 ml-1 text-[var(--text-tertiary)]" />
</Tooltip>
</label>
@@ -226,8 +228,8 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Frequency (days)
<Tooltip content="How often this check should be performed (leave empty for batch-based)">
{t('qualityTemplate.advancedFields.frequencyDays')}
<Tooltip content={t('tooltips.frequencyDays')}>
<Info className="inline w-4 h-4 ml-1 text-[var(--text-tertiary)]" />
</Tooltip>
</label>
@@ -235,7 +237,7 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
type="number"
value={templateData.frequencyDays}
onChange={(e) => handleDataChange({ ...templateData, frequencyDays: e.target.value })}
placeholder="Leave empty for batch-based"
placeholder={t('qualityTemplate.advancedFields.frequencyPlaceholder')}
min="1"
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
/>
@@ -249,7 +251,7 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
className="rounded border-[var(--border-secondary)] text-[var(--color-primary)] focus:ring-[var(--color-primary)]"
/>
<label className="text-sm text-[var(--text-secondary)]">
Required Check
{t('qualityTemplate.advancedFields.requiredCheck')}
</label>
</div>
</div>
@@ -257,26 +259,26 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
{/* Advanced Options */}
<AdvancedOptionsSection
title="Advanced Options"
description="Optional fields for comprehensive quality template configuration"
title={t('qualityTemplate.sections.advancedOptions')}
description={t('qualityTemplate.sections.advancedOptionsDescription')}
>
{/* Check Points Configuration */}
<div className="space-y-4">
<h5 className="text-xs font-semibold text-[var(--text-secondary)] uppercase tracking-wider">
Check Points Configuration
{t('qualityTemplate.sections.checkPointsConfiguration')}
</h5>
<div className="grid grid-cols-1 gap-4">
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Check Points (JSON Array)
<Tooltip content='Array of check points: [{"name": "Visual Check", "description": "...", "weight": 1.0}]'>
{t('qualityTemplate.advancedFields.checkPointsJsonArray')}
<Tooltip content={t('qualityTemplate.advancedFields.checkPointsTooltip')}>
<Info className="inline w-4 h-4 ml-1 text-[var(--text-tertiary)]" />
</Tooltip>
</label>
<textarea
value={templateData.checkPoints}
onChange={(e) => handleDataChange({ ...templateData, checkPoints: e.target.value })}
placeholder='[{"name": "Visual Inspection", "description": "Check appearance", "expected_value": "Golden brown", "measurement_type": "visual", "is_critical": false, "weight": 1.0}]'
placeholder={t('qualityTemplate.advancedFields.checkPointsPlaceholder')}
rows={4}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)] font-mono text-xs"
/>
@@ -284,12 +286,12 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Acceptance Criteria
{t('qualityTemplate.advancedFields.acceptanceCriteria')}
</label>
<textarea
value={templateData.acceptanceCriteria}
onChange={(e) => handleDataChange({ ...templateData, acceptanceCriteria: e.target.value })}
placeholder="E.g., Golden uniform color, fluffy texture, no burns..."
placeholder={t('qualityTemplate.advancedFields.acceptanceCriteriaPlaceholder')}
rows={2}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
/>
@@ -300,20 +302,20 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
{/* JSONB Configuration Fields */}
<div className="space-y-4 border-t border-[var(--border-primary)] pt-4">
<h5 className="text-xs font-semibold text-[var(--text-secondary)] uppercase tracking-wider">
Advanced Configuration (JSONB)
{t('qualityTemplate.sections.advancedConfiguration')}
</h5>
<div className="grid grid-cols-1 gap-4">
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Parameters (JSON)
<Tooltip content='Template parameters: {"temp_min": 75, "temp_max": 85, "humidity": 65}'>
{t('qualityTemplate.advancedFields.parametersJson')}
<Tooltip content={t('qualityTemplate.advancedFields.parametersTooltip')}>
<Info className="inline w-4 h-4 ml-1 text-[var(--text-tertiary)]" />
</Tooltip>
</label>
<textarea
value={templateData.parameters}
onChange={(e) => handleDataChange({ ...templateData, parameters: e.target.value })}
placeholder='{"temp_min": 75, "temp_max": 85, "humidity": 65}'
placeholder={t('qualityTemplate.advancedFields.parametersPlaceholder')}
rows={2}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)] font-mono text-xs"
/>
@@ -321,15 +323,15 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Thresholds (JSON)
<Tooltip content='Threshold values: {"critical": 90, "warning": 70, "acceptable": 50}'>
{t('qualityTemplate.advancedFields.thresholdsJson')}
<Tooltip content={t('qualityTemplate.advancedFields.thresholdsTooltip')}>
<Info className="inline w-4 h-4 ml-1 text-[var(--text-tertiary)]" />
</Tooltip>
</label>
<textarea
value={templateData.thresholds}
onChange={(e) => handleDataChange({ ...templateData, thresholds: e.target.value })}
placeholder='{"critical": 90, "warning": 70, "acceptable": 50}'
placeholder={t('qualityTemplate.advancedFields.thresholdsPlaceholder')}
rows={2}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)] font-mono text-xs"
/>
@@ -337,15 +339,15 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Scoring Criteria (JSON)
<Tooltip content='Custom scoring criteria: {"appearance": 30, "texture": 30, "taste": 40}'>
{t('qualityTemplate.advancedFields.scoringCriteriaJson')}
<Tooltip content={t('qualityTemplate.advancedFields.scoringCriteriaTooltip')}>
<Info className="inline w-4 h-4 ml-1 text-[var(--text-tertiary)]" />
</Tooltip>
</label>
<textarea
value={templateData.scoringCriteria}
onChange={(e) => handleDataChange({ ...templateData, scoringCriteria: e.target.value })}
placeholder='{"appearance": 30, "texture": 30, "taste": 40}'
placeholder={t('qualityTemplate.advancedFields.scoringCriteriaPlaceholder')}
rows={2}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)] font-mono text-xs"
/>
@@ -356,43 +358,43 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
{/* Responsibility & Requirements */}
<div className="space-y-4 border-t border-[var(--border-primary)] pt-4">
<h5 className="text-xs font-semibold text-[var(--text-secondary)] uppercase tracking-wider">
Responsibility & Requirements
{t('qualityTemplate.sections.responsibilityRequirements')}
</h5>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Responsible Role/Person
{t('qualityTemplate.advancedFields.responsibleRole')}
</label>
<input
type="text"
value={templateData.responsibleRole}
onChange={(e) => handleDataChange({ ...templateData, responsibleRole: e.target.value })}
placeholder="E.g., Production Manager, Baker"
placeholder={t('qualityTemplate.advancedFields.responsibleRolePlaceholder')}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
/>
</div>
<div>
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Required Equipment/Tools
{t('qualityTemplate.advancedFields.requiredEquipment')}
</label>
<input
type="text"
value={templateData.requiredEquipment}
onChange={(e) => handleDataChange({ ...templateData, requiredEquipment: e.target.value })}
placeholder="E.g., Thermometer, scale, timer"
placeholder={t('qualityTemplate.advancedFields.requiredEquipmentPlaceholder')}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
/>
</div>
<div className="md:col-span-2">
<label className="block text-sm font-medium text-[var(--text-secondary)] mb-2">
Specific Conditions or Notes
{t('qualityTemplate.advancedFields.specificConditions')}
</label>
<textarea
value={templateData.specificConditions}
onChange={(e) => handleDataChange({ ...templateData, specificConditions: e.target.value })}
placeholder="E.g., Only applicable on humid days, check 30 min after baking..."
placeholder={t('qualityTemplate.advancedFields.specificConditionsPlaceholder')}
rows={2}
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-lg focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] bg-[var(--bg-primary)] text-[var(--text-primary)]"
/>
@@ -403,7 +405,7 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
{/* Control Settings */}
<div className="space-y-4 border-t border-[var(--border-primary)] pt-4">
<h5 className="text-xs font-semibold text-[var(--text-secondary)] uppercase tracking-wider">
Control Settings
{t('qualityTemplate.sections.controlSettings')}
</h5>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="flex items-center gap-2">
@@ -414,7 +416,7 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
className="rounded border-[var(--border-secondary)] text-[var(--color-primary)] focus:ring-[var(--color-primary)]"
/>
<label className="text-sm text-[var(--text-secondary)]">
Active Template
{t('qualityTemplate.advancedFields.activeTemplate')}
</label>
</div>
@@ -426,7 +428,7 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
className="rounded border-[var(--border-secondary)] text-[var(--color-primary)] focus:ring-[var(--color-primary)]"
/>
<label className="text-sm text-[var(--text-secondary)]">
Requires Photo Evidence
{t('qualityTemplate.advancedFields.requiresPhotoEvidence')}
</label>
</div>
@@ -438,7 +440,7 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
className="rounded border-[var(--border-secondary)] text-[var(--color-primary)] focus:ring-[var(--color-primary)]"
/>
<label className="text-sm text-[var(--text-secondary)]">
Critical Control Point (CCP)
{t('qualityTemplate.advancedFields.criticalControlPoint')}
</label>
</div>
@@ -450,7 +452,7 @@ const QualityTemplateDetailsStep: React.FC<WizardDataProps> = ({ data, onDataCha
className="rounded border-[var(--border-secondary)] text-[var(--color-primary)] focus:ring-[var(--color-primary)]"
/>
<label className="text-sm text-[var(--text-secondary)]">
Notify on Failure
{t('qualityTemplate.advancedFields.notifyOnFailure')}
</label>
</div>
</div>
@@ -466,7 +468,7 @@ export const QualityTemplateWizardSteps = (
): WizardStep[] => [
{
id: 'template-details',
title: 'Template Details',
title: 'qualityTemplate.advancedFields.templateDetailsTitle',
component: (props) => <QualityTemplateDetailsStep {...props} data={data} onDataChange={setData} />,
validate: () => {
return !!(data.name && data.checkType && data.weight);