All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m18s
202 lines
9.1 KiB
TypeScript
202 lines
9.1 KiB
TypeScript
import { useState } from 'react';
|
|
import { RotateCcw, Save, X } from 'lucide-react';
|
|
import { CUT_FAMILY_RULES, type CutFamilyKey } from '../analytics/cutting';
|
|
import { editableProductTypeOptions, getProductTypeConfig, resolveProductType, type ProductTypeKey } from '../productClassification';
|
|
import type { CutProductOverride } from '../types';
|
|
import ProductTypeBadge from './ProductTypeBadge';
|
|
|
|
type SkuPlanningModalProduct = {
|
|
id: string;
|
|
name: string;
|
|
color?: string;
|
|
size?: string;
|
|
};
|
|
|
|
type SkuPlanningModalProps = {
|
|
product: SkuPlanningModalProduct;
|
|
override?: CutProductOverride;
|
|
isSaving?: boolean;
|
|
onClose: () => void;
|
|
onSave: (override: CutProductOverride | null) => void | Promise<void>;
|
|
};
|
|
|
|
const familyOptions: Array<{ value: CutFamilyKey | ''; label: string }> = [
|
|
{ value: '', label: 'Auto' },
|
|
...CUT_FAMILY_RULES.map(rule => ({ value: rule.key, label: `${rule.materialLabel} · ${rule.label}` })),
|
|
{ value: 'OUTROS', label: 'Sem regra' }
|
|
];
|
|
|
|
const buildOverride = ({
|
|
familyKey,
|
|
color,
|
|
size,
|
|
productType,
|
|
planningNotes
|
|
}: Required<Pick<CutProductOverride, 'color' | 'size' | 'planningNotes'>> & {
|
|
familyKey: CutFamilyKey | '';
|
|
productType: ProductTypeKey | '';
|
|
}): CutProductOverride | null => {
|
|
const next: CutProductOverride = {
|
|
familyKey,
|
|
color: color.trim(),
|
|
size: size.trim().toUpperCase(),
|
|
productType,
|
|
planningNotes: planningNotes.trim()
|
|
};
|
|
|
|
if (!next.familyKey && !next.color && !next.size && !next.productType && !next.planningNotes) {
|
|
return null;
|
|
}
|
|
|
|
return next;
|
|
};
|
|
|
|
const SkuPlanningModal = ({ product, override, isSaving = false, onClose, onSave }: SkuPlanningModalProps) => {
|
|
const [familyKey, setFamilyKey] = useState<CutFamilyKey | ''>(override?.familyKey || '');
|
|
const [color, setColor] = useState(override?.color || '');
|
|
const [size, setSize] = useState(override?.size || '');
|
|
const [productType, setProductType] = useState<ProductTypeKey | ''>(override?.productType || '');
|
|
const [planningNotes, setPlanningNotes] = useState(override?.planningNotes || '');
|
|
|
|
const resolvedType = resolveProductType(product.name, { productType });
|
|
const resolvedConfig = getProductTypeConfig(resolvedType);
|
|
|
|
const handleSave = () => {
|
|
void onSave(buildOverride({ familyKey, color, size, productType, planningNotes }));
|
|
};
|
|
|
|
const handleClear = () => {
|
|
void onSave(null);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className="fixed inset-0 z-50 flex h-dvh items-center justify-center overflow-y-auto bg-zinc-950/80 p-4 backdrop-blur-md dark:bg-black/75"
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-label={`Editar planejamento do SKU ${product.id}`}
|
|
onClick={onClose}
|
|
>
|
|
<div
|
|
className="w-full max-w-2xl rounded-2xl border border-dark-border bg-dark-card shadow-2xl"
|
|
onClick={(event) => event.stopPropagation()}
|
|
>
|
|
<div className="flex items-start justify-between gap-4 border-b border-dark-border p-5">
|
|
<div className="min-w-0">
|
|
<div className="font-mono text-[10px] font-bold uppercase tracking-widest text-dark-muted">SKU #{product.id}</div>
|
|
<h2 className="mt-1 truncate text-lg font-bold text-dark-text" title={product.name}>{product.name}</h2>
|
|
<div className="mt-2 flex flex-wrap items-center gap-2">
|
|
<ProductTypeBadge type={resolvedType} />
|
|
<span className="text-xs font-semibold text-dark-muted">{resolvedConfig.description}</span>
|
|
</div>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
onClick={onClose}
|
|
className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-xl border border-dark-border bg-dark-input text-dark-muted transition-colors hover:border-brand-primary hover:text-dark-text cursor-pointer"
|
|
title="Fechar"
|
|
aria-label="Fechar"
|
|
>
|
|
<X className="h-4 w-4" />
|
|
</button>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 gap-4 p-5 md:grid-cols-2">
|
|
<label className="space-y-2">
|
|
<span className="text-xs font-bold uppercase tracking-widest text-dark-muted">Tipo de produto</span>
|
|
<select
|
|
value={productType}
|
|
onChange={(event) => setProductType(event.target.value as ProductTypeKey | '')}
|
|
className="h-10 w-full rounded-lg border border-dark-border bg-dark-input px-3 text-sm font-bold text-dark-text outline-none transition-colors focus:border-brand-primary cursor-pointer"
|
|
>
|
|
<option value="">Auto ({getProductTypeConfig(resolveProductType(product.name)).label})</option>
|
|
{editableProductTypeOptions.map(option => (
|
|
<option key={option.value} value={option.value}>{option.label}</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
|
|
<label className="space-y-2">
|
|
<span className="text-xs font-bold uppercase tracking-widest text-dark-muted">Família de corte</span>
|
|
<select
|
|
value={familyKey}
|
|
onChange={(event) => setFamilyKey(event.target.value as CutFamilyKey | '')}
|
|
className="h-10 w-full rounded-lg border border-dark-border bg-dark-input px-3 text-sm font-bold text-dark-text outline-none transition-colors focus:border-brand-primary cursor-pointer"
|
|
>
|
|
{familyOptions.map(option => (
|
|
<option key={option.value || 'auto'} value={option.value}>{option.label}</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
|
|
<label className="space-y-2">
|
|
<span className="text-xs font-bold uppercase tracking-widest text-dark-muted">Cor</span>
|
|
<input
|
|
type="text"
|
|
value={color}
|
|
onChange={(event) => setColor(event.target.value)}
|
|
placeholder={product.color || 'Auto pelo nome'}
|
|
className="h-10 w-full rounded-lg border border-dark-border bg-dark-input px-3 text-sm font-bold text-dark-text outline-none transition-colors placeholder:text-dark-muted focus:border-brand-primary"
|
|
/>
|
|
</label>
|
|
|
|
<label className="space-y-2">
|
|
<span className="text-xs font-bold uppercase tracking-widest text-dark-muted">Tamanho</span>
|
|
<input
|
|
type="text"
|
|
value={size}
|
|
onChange={(event) => setSize(event.target.value)}
|
|
placeholder={product.size || 'Auto pelo nome'}
|
|
className="h-10 w-full rounded-lg border border-dark-border bg-dark-input px-3 text-sm font-bold uppercase text-dark-text outline-none transition-colors placeholder:normal-case placeholder:text-dark-muted focus:border-brand-primary"
|
|
/>
|
|
</label>
|
|
|
|
<label className="space-y-2 md:col-span-2">
|
|
<span className="text-xs font-bold uppercase tracking-widest text-dark-muted">Notas de planejamento</span>
|
|
<textarea
|
|
value={planningNotes}
|
|
onChange={(event) => setPlanningNotes(event.target.value)}
|
|
rows={3}
|
|
placeholder="Ex.: revisar consumo, material usado, regra temporária..."
|
|
className="w-full resize-none rounded-lg border border-dark-border bg-dark-input px-3 py-2 text-sm font-semibold text-dark-text outline-none transition-colors placeholder:text-dark-muted focus:border-brand-primary"
|
|
/>
|
|
</label>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-2 border-t border-dark-border p-5 sm:flex-row sm:items-center sm:justify-between">
|
|
<button
|
|
type="button"
|
|
onClick={handleClear}
|
|
disabled={isSaving}
|
|
className="inline-flex items-center justify-center gap-2 rounded-xl border border-dark-border bg-dark-input px-4 py-2.5 text-sm font-bold text-dark-muted transition-colors hover:border-red-400/40 hover:text-red-300 disabled:cursor-not-allowed disabled:opacity-50 cursor-pointer"
|
|
>
|
|
<RotateCcw className="h-4 w-4" />
|
|
Limpar override
|
|
</button>
|
|
<div className="flex gap-2 sm:justify-end">
|
|
<button
|
|
type="button"
|
|
onClick={onClose}
|
|
disabled={isSaving}
|
|
className="inline-flex flex-1 items-center justify-center rounded-xl border border-dark-border bg-dark-input px-4 py-2.5 text-sm font-bold text-dark-text transition-colors hover:border-brand-primary disabled:cursor-not-allowed disabled:opacity-50 sm:flex-none cursor-pointer"
|
|
>
|
|
Cancelar
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={handleSave}
|
|
disabled={isSaving}
|
|
className="inline-flex flex-1 items-center justify-center gap-2 rounded-xl border border-brand-primary/30 bg-brand-primary/15 px-4 py-2.5 text-sm font-bold text-brand-primary transition-colors hover:border-brand-primary disabled:cursor-not-allowed disabled:opacity-50 sm:flex-none cursor-pointer"
|
|
>
|
|
<Save className="h-4 w-4" />
|
|
{isSaving ? 'Salvando' : 'Salvar'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SkuPlanningModal;
|