Fix UI issues
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useCallback, useEffect } from 'react';
|
||||
import { Card, Button, Badge, Input, Modal, Table, Select, DatePicker } from '../../ui';
|
||||
import { productionService, type ProductionBatchResponse, ProductionBatchStatus, ProductionPriority } from '../../../api';
|
||||
import { productionService, type ProductionBatchResponse, ProductionBatchStatus, ProductionPriorityEnum } from '../../../api';
|
||||
import type { ProductionBatch, QualityCheck } from '../../../types/production.types';
|
||||
|
||||
interface BatchTrackerProps {
|
||||
@@ -124,10 +124,10 @@ const STATUS_COLORS = {
|
||||
};
|
||||
|
||||
const PRIORITY_COLORS = {
|
||||
[ProductionPriority.LOW]: 'bg-[var(--bg-tertiary)] text-[var(--text-primary)]',
|
||||
[ProductionPriority.NORMAL]: 'bg-[var(--color-info)]/10 text-[var(--color-info)]',
|
||||
[ProductionPriority.HIGH]: 'bg-[var(--color-primary)]/10 text-[var(--color-primary)]',
|
||||
[ProductionPriority.URGENT]: 'bg-[var(--color-error)]/10 text-[var(--color-error)]',
|
||||
[ProductionPriorityEnum.LOW]: 'bg-[var(--bg-tertiary)] text-[var(--text-primary)]',
|
||||
[ProductionPriorityEnum.NORMAL]: 'bg-[var(--color-info)]/10 text-[var(--color-info)]',
|
||||
[ProductionPriorityEnum.HIGH]: 'bg-[var(--color-primary)]/10 text-[var(--color-primary)]',
|
||||
[ProductionPriorityEnum.URGENT]: 'bg-[var(--color-error)]/10 text-[var(--color-error)]',
|
||||
};
|
||||
|
||||
export const BatchTracker: React.FC<BatchTrackerProps> = ({
|
||||
@@ -390,10 +390,10 @@ export const BatchTracker: React.FC<BatchTrackerProps> = ({
|
||||
<h4 className="font-semibold text-[var(--text-primary)]">{batch.recipe?.name || 'Producto'}</h4>
|
||||
<p className="text-sm text-[var(--text-secondary)]">Lote #{batch.batch_number}</p>
|
||||
<Badge className={PRIORITY_COLORS[batch.priority]} size="sm">
|
||||
{batch.priority === ProductionPriority.LOW && 'Baja'}
|
||||
{batch.priority === ProductionPriority.NORMAL && 'Normal'}
|
||||
{batch.priority === ProductionPriority.HIGH && 'Alta'}
|
||||
{batch.priority === ProductionPriority.URGENT && 'Urgente'}
|
||||
{batch.priority === ProductionPriorityEnum.LOW && 'Baja'}
|
||||
{batch.priority === ProductionPriorityEnum.NORMAL && 'Normal'}
|
||||
{batch.priority === ProductionPriorityEnum.HIGH && 'Alta'}
|
||||
{batch.priority === ProductionPriorityEnum.URGENT && 'Urgente'}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ export interface ModalProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onClos
|
||||
}
|
||||
|
||||
export interface ModalHeaderProps extends HTMLAttributes<HTMLDivElement> {
|
||||
title?: string;
|
||||
title?: string | React.ReactNode;
|
||||
subtitle?: string;
|
||||
showCloseButton?: boolean;
|
||||
onClose?: () => void;
|
||||
@@ -238,12 +238,18 @@ const ModalHeader = forwardRef<HTMLDivElement, ModalHeaderProps>(({
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
{title && (
|
||||
<h2
|
||||
id="modal-title"
|
||||
className="text-lg font-semibold text-[var(--text-primary)]"
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
typeof title === 'string' ? (
|
||||
<h2
|
||||
id="modal-title"
|
||||
className="text-lg font-semibold text-[var(--text-primary)]"
|
||||
>
|
||||
{title}
|
||||
</h2>
|
||||
) : (
|
||||
<div id="modal-title">
|
||||
{title}
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
{subtitle && (
|
||||
<p className="mt-1 text-sm text-[var(--text-secondary)]">
|
||||
|
||||
@@ -283,12 +283,12 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
|
||||
const triggerClasses = [
|
||||
'flex items-center justify-between w-full px-3 py-2',
|
||||
'bg-input-bg border border-input-border rounded-lg',
|
||||
'text-left transition-colors duration-200',
|
||||
'focus:border-input-border-focus focus:ring-1 focus:ring-input-border-focus',
|
||||
'bg-[var(--bg-primary,#ffffff)] border border-[var(--border-primary,#e5e7eb)] rounded-lg',
|
||||
'text-[var(--text-primary,#111827)] text-left transition-colors duration-200',
|
||||
'focus:border-[var(--color-primary,#3b82f6)] focus:ring-1 focus:ring-[var(--color-primary,#3b82f6)]',
|
||||
{
|
||||
'border-input-border-error focus:border-input-border-error focus:ring-input-border-error': hasError,
|
||||
'bg-bg-secondary border-transparent focus:bg-input-bg focus:border-input-border-focus': variant === 'filled',
|
||||
'border-[var(--color-error,#ef4444)] focus:border-[var(--color-error,#ef4444)] focus:ring-[var(--color-error,#ef4444)]': hasError,
|
||||
'bg-[var(--bg-secondary,#f9fafb)] border-transparent focus:bg-[var(--bg-primary,#ffffff)] focus:border-[var(--color-primary,#3b82f6)]': variant === 'filled',
|
||||
'bg-transparent border-none focus:ring-0': variant === 'unstyled',
|
||||
}
|
||||
];
|
||||
@@ -300,7 +300,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
};
|
||||
|
||||
const dropdownClasses = [
|
||||
'absolute z-50 w-full mt-1 bg-dropdown-bg border border-dropdown-border rounded-lg shadow-lg',
|
||||
'absolute z-50 w-full mt-1 bg-[var(--bg-primary,#ffffff)] border border-[var(--border-primary,#e5e7eb)] rounded-lg shadow-lg',
|
||||
'transform transition-all duration-200 ease-out',
|
||||
{
|
||||
'opacity-0 scale-95 pointer-events-none': !isOpen,
|
||||
@@ -317,7 +317,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
|
||||
if (multiple && Array.isArray(currentValue)) {
|
||||
if (currentValue.length === 0) {
|
||||
return <span className="text-input-placeholder">{placeholder}</span>;
|
||||
return <span className="text-[var(--text-tertiary,#6b7280)]">{placeholder}</span>;
|
||||
}
|
||||
|
||||
if (currentValue.length === 1) {
|
||||
@@ -338,7 +338,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
);
|
||||
}
|
||||
|
||||
return <span className="text-input-placeholder">{placeholder}</span>;
|
||||
return <span className="text-[var(--text-tertiary,#6b7280)]">{placeholder}</span>;
|
||||
};
|
||||
|
||||
const renderMultipleValues = () => {
|
||||
@@ -354,14 +354,14 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
{selectedOptions.map(option => (
|
||||
<span
|
||||
key={option.value}
|
||||
className="inline-flex items-center gap-1 px-2 py-1 bg-bg-tertiary text-text-primary rounded text-sm"
|
||||
className="inline-flex items-center gap-1 px-2 py-1 bg-[var(--bg-tertiary,#f3f4f6)] text-[var(--text-primary,#111827)] rounded text-sm"
|
||||
>
|
||||
{option.icon && <span className="text-xs">{option.icon}</span>}
|
||||
<span>{option.label}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => handleRemoveOption(option.value, e)}
|
||||
className="text-text-tertiary hover:text-text-primary transition-colors duration-150"
|
||||
className="text-[var(--text-tertiary,#6b7280)] hover:text-[var(--text-primary,#111827)] transition-colors duration-150"
|
||||
>
|
||||
<svg className="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
@@ -379,7 +379,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
const renderOptions = () => {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="px-3 py-2 text-text-secondary">
|
||||
<div className="px-3 py-2 text-[var(--text-secondary,#4b5563)]">
|
||||
{loadingMessage}
|
||||
</div>
|
||||
);
|
||||
@@ -387,13 +387,13 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
|
||||
if (filteredOptions.length === 0) {
|
||||
return (
|
||||
<div className="px-3 py-2 text-text-secondary">
|
||||
<div className="px-3 py-2 text-[var(--text-secondary,#4b5563)]">
|
||||
{noOptionsMessage}
|
||||
{createable && searchTerm.trim() && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCreate}
|
||||
className="block w-full text-left px-3 py-2 text-color-primary hover:bg-dropdown-item-hover transition-colors duration-150"
|
||||
className="block w-full text-left px-3 py-2 text-[var(--color-primary,#3b82f6)] hover:bg-[var(--bg-secondary,#f9fafb)] transition-colors duration-150"
|
||||
>
|
||||
{createLabel} "{searchTerm.trim()}"
|
||||
</button>
|
||||
@@ -416,8 +416,8 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
className={clsx(
|
||||
'flex items-center justify-between px-3 py-2 cursor-pointer transition-colors duration-150',
|
||||
{
|
||||
'bg-dropdown-item-hover': isHighlighted,
|
||||
'bg-color-primary/10 text-color-primary': isSelected && !multiple,
|
||||
'bg-[var(--bg-secondary,#f9fafb)]': isHighlighted,
|
||||
'bg-[var(--color-primary,#3b82f6)]/10 text-[var(--color-primary,#3b82f6)]': isSelected && !multiple,
|
||||
'opacity-50 cursor-not-allowed': option.disabled,
|
||||
}
|
||||
)}
|
||||
@@ -430,19 +430,22 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
type="checkbox"
|
||||
checked={isSelected}
|
||||
readOnly
|
||||
className="rounded border-input-border text-color-primary focus:ring-color-primary"
|
||||
className="rounded border-[var(--border-primary,#e5e7eb)] text-[var(--color-primary,#3b82f6)] focus:ring-[var(--color-primary,#3b82f6)]"
|
||||
/>
|
||||
)}
|
||||
{option.icon && <span>{option.icon}</span>}
|
||||
<div className="flex-1">
|
||||
<div className="font-medium">{option.label}</div>
|
||||
{option.description && (
|
||||
<div className="text-xs text-text-secondary">{option.description}</div>
|
||||
{option.description &&
|
||||
!option.description.startsWith('descriptions.') &&
|
||||
!option.description.includes('.') &&
|
||||
option.description !== option.value && (
|
||||
<div className="text-xs text-[var(--text-secondary,#4b5563)]">{option.description}</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{isSelected && !multiple && (
|
||||
<svg className="w-4 h-4 text-color-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<svg className="w-4 h-4 text-[var(--color-primary,#3b82f6)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
)}
|
||||
@@ -460,7 +463,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
// Add grouped options
|
||||
Object.entries(groupedOptions.groups).forEach(([groupName, groupOptions]) => {
|
||||
allOptions.push(
|
||||
<div key={groupName} className="px-3 py-1 text-xs font-semibold text-text-tertiary uppercase tracking-wide">
|
||||
<div key={groupName} className="px-3 py-1 text-xs font-semibold text-[var(--text-tertiary,#6b7280)] uppercase tracking-wide">
|
||||
{groupName}
|
||||
</div>
|
||||
);
|
||||
@@ -481,7 +484,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
key="__create__"
|
||||
type="button"
|
||||
onClick={handleCreate}
|
||||
className="block w-full text-left px-3 py-2 text-color-primary hover:bg-dropdown-item-hover transition-colors duration-150 border-t border-border-primary"
|
||||
className="block w-full text-left px-3 py-2 text-[var(--color-primary,#3b82f6)] hover:bg-[var(--bg-secondary,#f9fafb)] transition-colors duration-150 border-t border-[var(--border-primary,#e5e7eb)]"
|
||||
>
|
||||
{createLabel} "{searchTerm.trim()}"
|
||||
</button>
|
||||
@@ -496,11 +499,11 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
{label && (
|
||||
<label
|
||||
htmlFor={selectId}
|
||||
className="block text-sm font-medium text-text-primary mb-2"
|
||||
className="block text-sm font-medium text-[var(--text-primary,#111827)] mb-2"
|
||||
>
|
||||
{label}
|
||||
{isRequired && (
|
||||
<span className="text-color-error ml-1">*</span>
|
||||
<span className="text-[var(--color-error,#ef4444)] ml-1">*</span>
|
||||
)}
|
||||
</label>
|
||||
)}
|
||||
@@ -531,7 +534,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleClear}
|
||||
className="text-text-tertiary hover:text-text-primary transition-colors duration-150 p-1"
|
||||
className="text-[var(--text-tertiary,#6b7280)] hover:text-[var(--text-primary,#111827)] transition-colors duration-150 p-1"
|
||||
tabIndex={-1}
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
@@ -541,7 +544,7 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
)}
|
||||
|
||||
<svg
|
||||
className={clsx('w-4 h-4 text-text-tertiary transition-transform duration-200', {
|
||||
className={clsx('w-4 h-4 text-[var(--text-tertiary,#6b7280)] transition-transform duration-200', {
|
||||
'rotate-180': isOpen,
|
||||
})}
|
||||
fill="none"
|
||||
@@ -555,14 +558,14 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
|
||||
<div className={clsx(dropdownClasses)} style={{ maxHeight: isOpen ? maxHeight : 0 }}>
|
||||
{searchable && (
|
||||
<div className="p-2 border-b border-border-primary">
|
||||
<div className="p-2 border-b border-[var(--border-primary,#e5e7eb)]">
|
||||
<input
|
||||
ref={searchInputRef}
|
||||
type="text"
|
||||
placeholder="Buscar..."
|
||||
value={searchTerm}
|
||||
onChange={handleSearchChange}
|
||||
className="w-full px-3 py-2 border border-input-border rounded bg-input-bg focus:outline-none focus:ring-2 focus:ring-color-primary/20"
|
||||
className="w-full px-3 py-2 border border-[var(--border-primary,#e5e7eb)] rounded bg-[var(--bg-primary,#ffffff)] text-[var(--text-primary,#111827)] focus:outline-none focus:ring-2 focus:ring-[var(--color-primary,#3b82f6)]/20"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
</div>
|
||||
@@ -579,13 +582,13 @@ const Select = forwardRef<HTMLDivElement, SelectProps>(({
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<p className="mt-2 text-sm text-color-error">
|
||||
<p className="mt-2 text-sm text-[var(--color-error,#ef4444)]">
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{helperText && !error && (
|
||||
<p className="mt-2 text-sm text-text-secondary">
|
||||
<p className="mt-2 text-sm text-[var(--text-secondary,#4b5563)]">
|
||||
{helperText}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { LucideIcon, Edit, Eye, X } from 'lucide-react';
|
||||
import Modal, { ModalHeader, ModalBody, ModalFooter } from '../Modal/Modal';
|
||||
import { Button } from '../Button';
|
||||
import { Input } from '../Input';
|
||||
import { Select } from '../Select';
|
||||
import { StatusIndicatorConfig, getStatusColor } from '../StatusCard';
|
||||
import { formatters } from '../Stats/StatsPresets';
|
||||
|
||||
@@ -181,7 +182,11 @@ const renderEditableField = (
|
||||
return (
|
||||
<textarea
|
||||
value={Array.isArray(field.value) ? field.value.join('\n') : String(field.value)}
|
||||
onChange={(e) => onChange?.(e.target.value.split('\n'))}
|
||||
onChange={(e) => {
|
||||
const stringArray = e.target.value.split('\n');
|
||||
// For list type, we'll pass the joined string instead of array to maintain compatibility
|
||||
onChange?.(stringArray.join('\n'));
|
||||
}}
|
||||
placeholder={field.placeholder || 'Una opción por línea'}
|
||||
required={field.required}
|
||||
rows={4}
|
||||
@@ -190,23 +195,15 @@ const renderEditableField = (
|
||||
);
|
||||
case 'select':
|
||||
return (
|
||||
<select
|
||||
<Select
|
||||
value={String(field.value)}
|
||||
onChange={(e) => onChange?.(e.target.value)}
|
||||
required={field.required}
|
||||
className="w-full px-3 py-2 border border-[var(--border-secondary)] rounded-md focus:outline-none focus:ring-2 focus:ring-[var(--color-primary)] focus:border-transparent bg-[var(--bg-primary)]"
|
||||
>
|
||||
{field.placeholder && (
|
||||
<option value="" disabled>
|
||||
{field.placeholder}
|
||||
</option>
|
||||
)}
|
||||
{field.options?.map((option, index) => (
|
||||
<option key={index} value={option.value}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
onChange={(value) => onChange?.(typeof value === 'string' ? value : String(value))}
|
||||
options={field.options || []}
|
||||
placeholder={field.placeholder}
|
||||
isRequired={field.required}
|
||||
variant="outline"
|
||||
size="md"
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user