Fix few issues
This commit is contained in:
@@ -4,6 +4,7 @@ import { StatusCard, StatusIndicatorConfig } from '../../ui/StatusCard/StatusCar
|
||||
import { statusColors } from '../../../styles/colors';
|
||||
import { ProductionBatchResponse, ProductionStatus, ProductionPriority } from '../../../api/types/production';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TextOverflowPrevention, safeText } from '../../../utils/textUtils';
|
||||
|
||||
export interface ProductionStatusCardProps {
|
||||
batch: ProductionBatchResponse;
|
||||
@@ -76,7 +77,7 @@ export const ProductionStatusCard: React.FC<ProductionStatusCardProps> = ({
|
||||
|
||||
return {
|
||||
color: getStatusColorForProduction(status),
|
||||
text: t(`production:status.${status}`),
|
||||
text: t(`production:status.${status.toLowerCase()}`),
|
||||
icon: Icon,
|
||||
isCritical,
|
||||
isHighlight
|
||||
@@ -157,7 +158,7 @@ export const ProductionStatusCard: React.FC<ProductionStatusCardProps> = ({
|
||||
|
||||
if (onView) {
|
||||
actions.push({
|
||||
label: 'Ver Detalles',
|
||||
label: 'Ver',
|
||||
icon: Eye,
|
||||
priority: 'primary' as const,
|
||||
onClick: () => onView(batch)
|
||||
@@ -166,7 +167,7 @@ export const ProductionStatusCard: React.FC<ProductionStatusCardProps> = ({
|
||||
|
||||
if (batch.status === ProductionStatus.PENDING && onStart) {
|
||||
actions.push({
|
||||
label: 'Iniciar Producción',
|
||||
label: 'Iniciar',
|
||||
icon: Play,
|
||||
priority: 'primary' as const,
|
||||
onClick: () => onStart(batch)
|
||||
@@ -205,7 +206,7 @@ export const ProductionStatusCard: React.FC<ProductionStatusCardProps> = ({
|
||||
if (batch.status === ProductionStatus.QUALITY_CHECK && onQualityCheck) {
|
||||
console.log('ProductionStatusCard - Adding quality check button for batch:', batch.batch_number);
|
||||
actions.push({
|
||||
label: 'Control Calidad',
|
||||
label: 'Calidad',
|
||||
icon: Package,
|
||||
priority: 'primary' as const,
|
||||
onClick: () => onQualityCheck(batch)
|
||||
@@ -268,16 +269,18 @@ export const ProductionStatusCard: React.FC<ProductionStatusCardProps> = ({
|
||||
}
|
||||
|
||||
if (batch.staff_assigned && batch.staff_assigned.length > 0) {
|
||||
metadata.push(`Personal: ${batch.staff_assigned.join(', ')}`);
|
||||
const staff = TextOverflowPrevention.production.staffList(batch.staff_assigned);
|
||||
metadata.push(`Personal: ${staff}`);
|
||||
}
|
||||
|
||||
if (batch.equipment_used && batch.equipment_used.length > 0) {
|
||||
metadata.push(`Equipos: ${batch.equipment_used.join(', ')}`);
|
||||
const equipment = TextOverflowPrevention.production.equipmentList(batch.equipment_used);
|
||||
metadata.push(`Equipos: ${equipment}`);
|
||||
}
|
||||
|
||||
const qualityInfo = getQualityStatusInfo(batch);
|
||||
if (qualityInfo) {
|
||||
metadata.push(qualityInfo);
|
||||
metadata.push(safeText(qualityInfo, qualityInfo, 50));
|
||||
}
|
||||
|
||||
if (batch.priority === ProductionPriority.URGENT) {
|
||||
@@ -299,21 +302,22 @@ export const ProductionStatusCard: React.FC<ProductionStatusCardProps> = ({
|
||||
if (batch.actual_quantity && batch.planned_quantity) {
|
||||
const completionPercent = Math.round((batch.actual_quantity / batch.planned_quantity) * 100);
|
||||
return {
|
||||
label: 'Progreso Cantidad',
|
||||
label: 'Progreso',
|
||||
value: `${batch.actual_quantity}/${batch.planned_quantity} (${completionPercent}%)`
|
||||
};
|
||||
}
|
||||
|
||||
if (batch.staff_assigned && batch.staff_assigned.length > 0) {
|
||||
const staff = TextOverflowPrevention.mobile.staff(batch.staff_assigned);
|
||||
return {
|
||||
label: 'Personal Asignado',
|
||||
value: batch.staff_assigned.join(', ')
|
||||
label: 'Personal',
|
||||
value: staff
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
label: 'Prioridad',
|
||||
value: t(`production:priority.${batch.priority}`)
|
||||
value: t(`production:priority.${batch.priority.toLowerCase()}`)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -324,10 +328,10 @@ export const ProductionStatusCard: React.FC<ProductionStatusCardProps> = ({
|
||||
<StatusCard
|
||||
id={batch.id}
|
||||
statusIndicator={statusConfig}
|
||||
title={batch.product_name}
|
||||
subtitle={`Lote: ${batch.batch_number}`}
|
||||
title={TextOverflowPrevention.production.productName(batch.product_name)}
|
||||
subtitle={`Lote: ${TextOverflowPrevention.production.batchNumber(batch.batch_number)}`}
|
||||
primaryValue={batch.planned_quantity}
|
||||
primaryValueLabel="unidades"
|
||||
primaryValueLabel="uds"
|
||||
secondaryInfo={getSecondaryInfo()}
|
||||
progress={progress !== undefined ? {
|
||||
label: getProgressLabel(batch),
|
||||
|
||||
@@ -18,13 +18,12 @@ import {
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Card,
|
||||
Badge,
|
||||
Select,
|
||||
StatusCard,
|
||||
Modal,
|
||||
StatsGrid
|
||||
StatsGrid,
|
||||
SearchAndFilter,
|
||||
type FilterConfig
|
||||
} from '../../ui';
|
||||
import { LoadingSpinner } from '../../ui';
|
||||
import { PageHeader } from '../../layout';
|
||||
@@ -285,59 +284,45 @@ export const QualityTemplateManager: React.FC<QualityTemplateManagerProps> = ({
|
||||
columns={4}
|
||||
/>
|
||||
|
||||
{/* Filters */}
|
||||
<Card className="p-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-[var(--text-tertiary)]" />
|
||||
<Input
|
||||
placeholder="Buscar plantillas..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Select
|
||||
value={selectedCheckType}
|
||||
onChange={(e) => setSelectedCheckType(e.target.value as QualityCheckType | '')}
|
||||
placeholder="Tipo de control"
|
||||
>
|
||||
<option value="">Todos los tipos</option>
|
||||
{Object.entries(QUALITY_CHECK_TYPE_CONFIG).map(([type, config]) => (
|
||||
<option key={type} value={type}>
|
||||
{config.label}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
<Select
|
||||
value={selectedStage}
|
||||
onChange={(e) => setSelectedStage(e.target.value as ProcessStage | '')}
|
||||
placeholder="Etapa del proceso"
|
||||
>
|
||||
<option value="">Todas las etapas</option>
|
||||
{Object.entries(PROCESS_STAGE_LABELS).map(([stage, label]) => (
|
||||
<option key={stage} value={stage}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
|
||||
<div className="flex items-center space-x-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="activeOnly"
|
||||
checked={showActiveOnly}
|
||||
onChange={(e) => setShowActiveOnly(e.target.checked)}
|
||||
className="rounded border-[var(--border-primary)]"
|
||||
/>
|
||||
<label htmlFor="activeOnly" className="text-sm text-[var(--text-secondary)]">
|
||||
Solo activas
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
{/* Search and Filter Controls */}
|
||||
<SearchAndFilter
|
||||
searchValue={searchTerm}
|
||||
onSearchChange={setSearchTerm}
|
||||
searchPlaceholder="Buscar plantillas..."
|
||||
filters={[
|
||||
{
|
||||
key: 'checkType',
|
||||
label: 'Tipo de control',
|
||||
type: 'dropdown',
|
||||
value: selectedCheckType,
|
||||
onChange: (value) => setSelectedCheckType(value as QualityCheckType | ''),
|
||||
placeholder: 'Todos los tipos',
|
||||
options: Object.entries(QUALITY_CHECK_TYPE_CONFIG).map(([type, config]) => ({
|
||||
value: type,
|
||||
label: config.label
|
||||
}))
|
||||
},
|
||||
{
|
||||
key: 'stage',
|
||||
label: 'Etapa del proceso',
|
||||
type: 'dropdown',
|
||||
value: selectedStage,
|
||||
onChange: (value) => setSelectedStage(value as ProcessStage | ''),
|
||||
placeholder: 'Todas las etapas',
|
||||
options: Object.entries(PROCESS_STAGE_LABELS).map(([stage, label]) => ({
|
||||
value: stage,
|
||||
label: label
|
||||
}))
|
||||
},
|
||||
{
|
||||
key: 'activeOnly',
|
||||
label: 'Solo activas',
|
||||
type: 'checkbox',
|
||||
value: showActiveOnly,
|
||||
onChange: (value) => setShowActiveOnly(value as boolean)
|
||||
}
|
||||
] as FilterConfig[]}
|
||||
/>
|
||||
|
||||
{/* Templates Grid */}
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
|
||||
@@ -18,30 +18,33 @@ interface CreateRecipeModalProps {
|
||||
*/
|
||||
// Custom Ingredients Component for AddModal
|
||||
const IngredientsComponent: React.FC<{
|
||||
value: RecipeIngredientCreate[];
|
||||
value: RecipeIngredientCreate[] | undefined | null;
|
||||
onChange: (value: RecipeIngredientCreate[]) => void;
|
||||
availableIngredients: Array<{value: string; label: string}>;
|
||||
unitOptions: Array<{value: MeasurementUnit; label: string}>;
|
||||
}> = ({ value, onChange, availableIngredients, unitOptions }) => {
|
||||
// Ensure value is always an array
|
||||
const ingredientsArray = Array.isArray(value) ? value : [];
|
||||
|
||||
const addIngredient = () => {
|
||||
const newIngredient: RecipeIngredientCreate = {
|
||||
ingredient_id: '',
|
||||
quantity: 1,
|
||||
unit: MeasurementUnit.GRAMS,
|
||||
ingredient_order: value.length + 1,
|
||||
ingredient_order: ingredientsArray.length + 1,
|
||||
is_optional: false
|
||||
};
|
||||
onChange([...value, newIngredient]);
|
||||
onChange([...ingredientsArray, newIngredient]);
|
||||
};
|
||||
|
||||
const removeIngredient = (index: number) => {
|
||||
if (value.length > 1) {
|
||||
onChange(value.filter((_, i) => i !== index));
|
||||
if (ingredientsArray.length > 1) {
|
||||
onChange(ingredientsArray.filter((_, i) => i !== index));
|
||||
}
|
||||
};
|
||||
|
||||
const updateIngredient = (index: number, field: keyof RecipeIngredientCreate, newValue: any) => {
|
||||
const updated = value.map((ingredient, i) =>
|
||||
const updated = ingredientsArray.map((ingredient, i) =>
|
||||
i === index ? { ...ingredient, [field]: newValue } : ingredient
|
||||
);
|
||||
onChange(updated);
|
||||
@@ -62,14 +65,14 @@ const IngredientsComponent: React.FC<{
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 max-h-64 overflow-y-auto">
|
||||
{value.map((ingredient, index) => (
|
||||
{ingredientsArray.map((ingredient, index) => (
|
||||
<div key={index} className="p-3 border border-[var(--border-secondary)] rounded-lg bg-[var(--bg-secondary)]/50 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-[var(--text-primary)]">Ingrediente #{index + 1}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeIngredient(index)}
|
||||
disabled={value.length <= 1}
|
||||
disabled={ingredientsArray.length <= 1}
|
||||
className="p-1 text-red-500 hover:text-red-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
|
||||
Reference in New Issue
Block a user