feat: ordenacao por marca no kanban e regra de quadradinhos de contagem no pdf
This commit is contained in:
@@ -8,7 +8,7 @@ import { Badge } from '@/components/ui/badge';
|
||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/components/ui/alert-dialog';
|
||||
import { ItemPrioridade } from '@/hooks/useItensPrioridadeFabricacao';
|
||||
import { usePrioridades } from '@/hooks/usePrioridades';
|
||||
import { Edit, Trash2, GripVertical, X, Search } from 'lucide-react';
|
||||
import { Edit, Trash2, GripVertical, X, Search, ArrowUpDown } from 'lucide-react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
interface KanbanPrioridadesFabricacaoProps {
|
||||
@@ -32,6 +32,8 @@ export const KanbanPrioridadesFabricacao: React.FC<KanbanPrioridadesFabricacaoPr
|
||||
const [editingItem, setEditingItem] = useState<string | null>(null);
|
||||
const [editQuantity, setEditQuantity] = useState<number>(1);
|
||||
const [showDeleteAllDialog, setShowDeleteAllDialog] = useState<string | null>(null);
|
||||
const [showSortDialog, setShowSortDialog] = useState<string | null>(null);
|
||||
const [isSorting, setIsSorting] = useState(false);
|
||||
const [filtrosPecas, setFiltrosPecas] = useState<{ [key: string]: string }>({
|
||||
P1: '',
|
||||
P2: '',
|
||||
@@ -202,6 +204,45 @@ export const KanbanPrioridadesFabricacao: React.FC<KanbanPrioridadesFabricacaoPr
|
||||
setShowDeleteAllDialog(null);
|
||||
};
|
||||
|
||||
const handleOrdenarPorMarca = async (codigo: string) => {
|
||||
const itens = itensPorPrioridade[codigo] || [];
|
||||
if (itens.length <= 1) {
|
||||
setShowSortDialog(null);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsSorting(true);
|
||||
// Ordenação sequencial natural por Marca (tag)
|
||||
const itensOrdenadosPorMarca = [...itens].sort((a, b) => {
|
||||
const marcaA = a.peca?.marca || '';
|
||||
const marcaB = b.peca?.marca || '';
|
||||
return marcaA.localeCompare(marcaB, undefined, { numeric: true, sensitivity: 'base' });
|
||||
});
|
||||
|
||||
const itensOrdenados = itensOrdenadosPorMarca.map((item, index) => ({
|
||||
id: item.id,
|
||||
ordem_fabricacao: index + 1
|
||||
}));
|
||||
|
||||
const primeiroItem = itens[0];
|
||||
if (primeiroItem?.prioridade_fabricacao_id) {
|
||||
const sucesso = await onReorderItems(primeiroItem.prioridade_fabricacao_id, itensOrdenados);
|
||||
if (sucesso) {
|
||||
toast.success(`Peças da prioridade ${codigo} ordenadas sequencialmente por marca!`);
|
||||
} else {
|
||||
toast.error(`Não foi possível salvar a nova ordenação da prioridade ${codigo}`);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro ao ordenar peças por marca:', error);
|
||||
toast.error('Ocorreu um erro ao tentar ordenar as peças');
|
||||
} finally {
|
||||
setIsSorting(false);
|
||||
setShowSortDialog(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DragDropContext onDragEnd={handleDragEnd}>
|
||||
@@ -243,9 +284,9 @@ export const KanbanPrioridadesFabricacao: React.FC<KanbanPrioridadesFabricacaoPr
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Campo de filtro */}
|
||||
<div className="mb-3">
|
||||
<div className="relative">
|
||||
{/* Campo de filtro e botão de ordenação por marca */}
|
||||
<div className="mb-3 flex items-center gap-1.5">
|
||||
<div className="relative flex-1">
|
||||
<Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Filtrar peça..."
|
||||
@@ -254,6 +295,17 @@ export const KanbanPrioridadesFabricacao: React.FC<KanbanPrioridadesFabricacaoPr
|
||||
className="pl-8 h-8 text-sm bg-background/50 border-input/50 focus:bg-background focus:border-input"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setShowSortDialog(codigo)}
|
||||
disabled={itens.length <= 1 || isSorting}
|
||||
className="h-8 px-2 bg-background/80 hover:bg-background border-input/50 text-xs font-medium flex items-center gap-1 shrink-0"
|
||||
title="Ordenar sequencialmente por marca (tag)"
|
||||
>
|
||||
<ArrowUpDown className="h-3.5 w-3.5 text-primary" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Área de drop dos itens */}
|
||||
@@ -377,6 +429,7 @@ export const KanbanPrioridadesFabricacao: React.FC<KanbanPrioridadesFabricacaoPr
|
||||
</DragDropContext>
|
||||
|
||||
{/* Dialog de confirmação para excluir todas as peças */}
|
||||
{/* Dialog de confirmação para exclusão de todas as peças */}
|
||||
<AlertDialog open={showDeleteAllDialog !== null} onOpenChange={handleCancelDeleteAll}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
@@ -407,6 +460,32 @@ export const KanbanPrioridadesFabricacao: React.FC<KanbanPrioridadesFabricacaoPr
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
{/* Dialog de confirmação para ordenação por Marca */}
|
||||
<AlertDialog open={showSortDialog !== null} onOpenChange={() => setShowSortDialog(null)}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Confirmar Ordenação por Marca</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Atenção: A ordem manual atual das peças da prioridade <strong>{showSortDialog}</strong> será perdida e substituída pela sequência ordenada de <strong>Marca (Tag)</strong>.
|
||||
<br /><br />
|
||||
Tem certeza que deseja aplicar esta ordenação?
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel onClick={() => setShowSortDialog(null)} disabled={isSorting}>
|
||||
Cancelar
|
||||
</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => showSortDialog && handleOrdenarPorMarca(showSortDialog)}
|
||||
disabled={isSorting}
|
||||
className="bg-primary hover:bg-primary/90"
|
||||
>
|
||||
{isSorting ? 'Ordenando...' : 'Ordenar por Marca'}
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -48,59 +48,98 @@ export const PrioridadesPDFTemplate: React.FC<PrioridadesPDFTemplateProps> = ({
|
||||
};
|
||||
|
||||
const generateTickBoxes = (quantity: number) => {
|
||||
const boxes = [];
|
||||
|
||||
if (quantity > 10) {
|
||||
const numBigBoxes = Math.floor(quantity / 5);
|
||||
const numSmallBoxes = quantity % 5;
|
||||
|
||||
// Quadrados com "5"
|
||||
for (let i = 0; i < numBigBoxes; i++) {
|
||||
boxes.push(
|
||||
<svg
|
||||
key={`big-${i}`}
|
||||
width="13"
|
||||
height="13"
|
||||
viewBox="0 0 13 13"
|
||||
style={{ display: 'inline-block', verticalAlign: '-1px', marginRight: '2px' }}
|
||||
>
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#f3f4f6" stroke="#4b5563" strokeWidth="1" />
|
||||
<text x="6.5" y="9.5" textAnchor="middle" fontSize="8.5" fontFamily="Arial, sans-serif" fontWeight="bold" fill="#4b5563">5</text>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
// Quadrados unitários restantes
|
||||
for (let i = 0; i < numSmallBoxes; i++) {
|
||||
boxes.push(
|
||||
<svg
|
||||
key={`small-${i}`}
|
||||
width="13"
|
||||
height="13"
|
||||
viewBox="0 0 13 13"
|
||||
style={{ display: 'inline-block', verticalAlign: '-1px', marginRight: '2px' }}
|
||||
>
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#ffffff" stroke="#4b5563" strokeWidth="1" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
if (!quantity || quantity <= 0) return null;
|
||||
|
||||
let qtd50 = 0;
|
||||
let qtd10 = 0;
|
||||
let qtd5 = 0;
|
||||
let qtd1 = 0;
|
||||
|
||||
if (quantity <= 10) {
|
||||
qtd1 = quantity;
|
||||
} else if (quantity <= 50) {
|
||||
qtd5 = Math.floor(quantity / 5);
|
||||
qtd1 = quantity % 5;
|
||||
} else if (quantity <= 100) {
|
||||
qtd10 = Math.floor(quantity / 10);
|
||||
const resto10 = quantity % 10;
|
||||
qtd5 = Math.floor(resto10 / 5);
|
||||
qtd1 = resto10 % 5;
|
||||
} else {
|
||||
// Apenas quadrados unitários
|
||||
for (let i = 0; i < quantity; i++) {
|
||||
boxes.push(
|
||||
<svg
|
||||
key={i}
|
||||
width="13"
|
||||
height="13"
|
||||
viewBox="0 0 13 13"
|
||||
style={{ display: 'inline-block', verticalAlign: '-1px', marginRight: '2px' }}
|
||||
>
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#ffffff" stroke="#4b5563" strokeWidth="1" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
// Acima de 100 unidades
|
||||
qtd50 = Math.floor(quantity / 50);
|
||||
const resto50 = quantity % 50;
|
||||
qtd10 = Math.floor(resto50 / 10);
|
||||
const resto10 = resto50 % 10;
|
||||
qtd5 = Math.floor(resto10 / 5);
|
||||
qtd1 = resto10 % 5;
|
||||
}
|
||||
|
||||
|
||||
const boxes: React.ReactNode[] = [];
|
||||
|
||||
// Quadrados de peso 50
|
||||
for (let i = 0; i < qtd50; i++) {
|
||||
boxes.push(
|
||||
<svg
|
||||
key={`box50-${i}`}
|
||||
width="13"
|
||||
height="13"
|
||||
viewBox="0 0 13 13"
|
||||
style={{ display: 'inline-block', verticalAlign: '-1px', marginRight: '2px' }}
|
||||
>
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#f3f4f6" stroke="#4b5563" strokeWidth="1" />
|
||||
<text x="6.5" y="9.2" textAnchor="middle" fontSize="7" fontFamily="Arial, sans-serif" fontWeight="bold" fill="#374151">50</text>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
// Quadrados de peso 10
|
||||
for (let i = 0; i < qtd10; i++) {
|
||||
boxes.push(
|
||||
<svg
|
||||
key={`box10-${i}`}
|
||||
width="13"
|
||||
height="13"
|
||||
viewBox="0 0 13 13"
|
||||
style={{ display: 'inline-block', verticalAlign: '-1px', marginRight: '2px' }}
|
||||
>
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#f3f4f6" stroke="#4b5563" strokeWidth="1" />
|
||||
<text x="6.5" y="9.2" textAnchor="middle" fontSize="7" fontFamily="Arial, sans-serif" fontWeight="bold" fill="#374151">10</text>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
// Quadrados de peso 5
|
||||
for (let i = 0; i < qtd5; i++) {
|
||||
boxes.push(
|
||||
<svg
|
||||
key={`box5-${i}`}
|
||||
width="13"
|
||||
height="13"
|
||||
viewBox="0 0 13 13"
|
||||
style={{ display: 'inline-block', verticalAlign: '-1px', marginRight: '2px' }}
|
||||
>
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#f3f4f6" stroke="#4b5563" strokeWidth="1" />
|
||||
<text x="6.5" y="9.5" textAnchor="middle" fontSize="8.5" fontFamily="Arial, sans-serif" fontWeight="bold" fill="#4b5563">5</text>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
// Quadrados unitários (vazios)
|
||||
for (let i = 0; i < qtd1; i++) {
|
||||
boxes.push(
|
||||
<svg
|
||||
key={`box1-${i}`}
|
||||
width="13"
|
||||
height="13"
|
||||
viewBox="0 0 13 13"
|
||||
style={{ display: 'inline-block', verticalAlign: '-1px', marginRight: '2px' }}
|
||||
>
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#ffffff" stroke="#4b5563" strokeWidth="1" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
return <span style={{ display: 'inline-block', verticalAlign: 'middle', marginLeft: '4px' }}>{boxes}</span>;
|
||||
};
|
||||
|
||||
@@ -221,7 +260,7 @@ export const PrioridadesPDFTemplate: React.FC<PrioridadesPDFTemplateProps> = ({
|
||||
>
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#ffffff" stroke="#4b5563" strokeWidth="1" />
|
||||
</svg>
|
||||
<span>indicam o controle de peças fabricadas.</span>
|
||||
<span>indicam o controle de peças (quadrados com número = lotes de 50, 10 ou 5 unidades; vazios = 1 unidade).</span>
|
||||
</div>
|
||||
|
||||
{/* Itens por Prioridade */}
|
||||
|
||||
@@ -54,34 +54,74 @@ const PrioridadesFabricacao = () => {
|
||||
};
|
||||
|
||||
const generateTickBoxesHTML = (quantity: number): string => {
|
||||
let boxesHtml = '';
|
||||
if (quantity > 10) {
|
||||
const numBigBoxes = Math.floor(quantity / 5);
|
||||
const numSmallBoxes = quantity % 5;
|
||||
for (let i = 0; i < numBigBoxes; i++) {
|
||||
boxesHtml += `
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" style="display: inline-block; vertical-align: -1px; margin-right: 2px;">
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#f3f4f6" stroke="#4b5563" stroke-width="1" />
|
||||
<text x="6.5" y="9.5" text-anchor="middle" font-size="8.5" font-family="Arial, sans-serif" font-weight="bold" fill="#4b5563">5</text>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
for (let i = 0; i < numSmallBoxes; i++) {
|
||||
boxesHtml += `
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" style="display: inline-block; vertical-align: -1px; margin-right: 2px;">
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#ffffff" stroke="#4b5563" stroke-width="1" />
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
if (!quantity || quantity <= 0) return '';
|
||||
|
||||
let qtd50 = 0;
|
||||
let qtd10 = 0;
|
||||
let qtd5 = 0;
|
||||
let qtd1 = 0;
|
||||
|
||||
if (quantity <= 10) {
|
||||
qtd1 = quantity;
|
||||
} else if (quantity <= 50) {
|
||||
qtd5 = Math.floor(quantity / 5);
|
||||
qtd1 = quantity % 5;
|
||||
} else if (quantity <= 100) {
|
||||
qtd10 = Math.floor(quantity / 10);
|
||||
const resto10 = quantity % 10;
|
||||
qtd5 = Math.floor(resto10 / 5);
|
||||
qtd1 = resto10 % 5;
|
||||
} else {
|
||||
for (let i = 0; i < quantity; i++) {
|
||||
boxesHtml += `
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" style="display: inline-block; vertical-align: -1px; margin-right: 2px;">
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#ffffff" stroke="#4b5563" stroke-width="1" />
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
// Acima de 100 unidades
|
||||
qtd50 = Math.floor(quantity / 50);
|
||||
const resto50 = quantity % 50;
|
||||
qtd10 = Math.floor(resto50 / 10);
|
||||
const resto10 = resto50 % 10;
|
||||
qtd5 = Math.floor(resto10 / 5);
|
||||
qtd1 = resto10 % 5;
|
||||
}
|
||||
|
||||
let boxesHtml = '';
|
||||
|
||||
// Quadrados com "50"
|
||||
for (let i = 0; i < qtd50; i++) {
|
||||
boxesHtml += `
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" style="display: inline-block; vertical-align: -1px; margin-right: 2px;">
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#f3f4f6" stroke="#4b5563" stroke-width="1" />
|
||||
<text x="6.5" y="9.2" text-anchor="middle" font-size="7" font-family="Arial, sans-serif" font-weight="bold" fill="#374151">50</text>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
// Quadrados com "10"
|
||||
for (let i = 0; i < qtd10; i++) {
|
||||
boxesHtml += `
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" style="display: inline-block; vertical-align: -1px; margin-right: 2px;">
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#f3f4f6" stroke="#4b5563" stroke-width="1" />
|
||||
<text x="6.5" y="9.2" text-anchor="middle" font-size="7" font-family="Arial, sans-serif" font-weight="bold" fill="#374151">10</text>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
// Quadrados com "5"
|
||||
for (let i = 0; i < qtd5; i++) {
|
||||
boxesHtml += `
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" style="display: inline-block; vertical-align: -1px; margin-right: 2px;">
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#f3f4f6" stroke="#4b5563" stroke-width="1" />
|
||||
<text x="6.5" y="9.5" text-anchor="middle" font-size="8.5" font-family="Arial, sans-serif" font-weight="bold" fill="#4b5563">5</text>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
// Quadrados unitários (vazios)
|
||||
for (let i = 0; i < qtd1; i++) {
|
||||
boxesHtml += `
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" style="display: inline-block; vertical-align: -1px; margin-right: 2px;">
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#ffffff" stroke="#4b5563" stroke-width="1" />
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
return `<span style="display: inline-block; vertical-align: middle; margin-left: 4px;">${boxesHtml}</span>`;
|
||||
};
|
||||
|
||||
@@ -309,7 +349,7 @@ const PrioridadesFabricacao = () => {
|
||||
<svg width="13" height="13" viewBox="0 0 13 13" style="display: inline-block; vertical-align: -2px; margin: 0 4px;">
|
||||
<rect x="0.5" y="0.5" width="12" height="12" rx="1.5" fill="#ffffff" stroke="#4b5563" stroke-width="1" />
|
||||
</svg>
|
||||
<span>indicam o controle de peças fabricadas.</span>
|
||||
<span>indicam o controle de peças (quadrados com número = lotes de 50, 10 ou 5 unidades; vazios = 1 unidade).</span>
|
||||
</div>
|
||||
|
||||
<div id="main-container" class="space-y-8">
|
||||
|
||||
Reference in New Issue
Block a user