Refocus cutting plan table

This commit is contained in:
Cauê Faleiros
2026-08-11 11:08:04 -03:00
parent 5f7f9d94d4
commit 8a46069d8e

View File

@@ -1,6 +1,6 @@
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import { Link, useOutletContext, useSearchParams } from 'react-router-dom'; import { Link, useOutletContext, useSearchParams } from 'react-router-dom';
import { AlertTriangle, ArrowLeft, ClipboardList, Download, Eye, Layers3, Pencil, RotateCcw, Ruler, Save as SaveIcon, Scissors, Search, Settings2, X } from 'lucide-react'; import { AlertTriangle, ArrowLeft, ClipboardList, Download, Layers3, Pencil, RotateCcw, Ruler, Save as SaveIcon, Scissors, Search, Settings2, X } from 'lucide-react';
import DateRangePicker from '../components/DateRangePicker'; import DateRangePicker from '../components/DateRangePicker';
import PaginationControls from '../components/PaginationControls'; import PaginationControls from '../components/PaginationControls';
import ProductColorBadge from '../components/ProductColorBadge'; import ProductColorBadge from '../components/ProductColorBadge';
@@ -668,7 +668,7 @@ const Cutting = () => {
const renderCutStatus = (row: CutPlanSkuRow) => { const renderCutStatus = (row: CutPlanSkuRow) => {
const readiness = materialReadinessByProductId.get(row.id); const readiness = materialReadinessByProductId.get(row.id);
if (readiness?.status === 'ready') { if (readiness?.status === 'ready') {
return <span className="inline-flex rounded-full border border-emerald-400/30 bg-emerald-400/10 px-2.5 py-1 text-xs font-bold text-emerald-300">Pronto para cortar</span>; return <span className="inline-flex h-2.5 w-2.5 rounded-full bg-emerald-400 shadow-[0_0_0_4px_rgba(52,211,153,0.10)]" title="Pronto para cortar" aria-label="Pronto para cortar" />;
} }
if (readiness?.status === 'material_ready') { if (readiness?.status === 'material_ready') {
return <span className="inline-flex max-w-full items-center gap-1.5 rounded-full border border-amber-400/30 bg-amber-400/10 px-2.5 py-1 text-xs font-bold text-amber-300" title={row.issues.map(issue => issueLabels[issue]).join(', ')}><AlertTriangle className="h-3.5 w-3.5 shrink-0" /><span className="truncate">Dados pendentes</span></span>; return <span className="inline-flex max-w-full items-center gap-1.5 rounded-full border border-amber-400/30 bg-amber-400/10 px-2.5 py-1 text-xs font-bold text-amber-300" title={row.issues.map(issue => issueLabels[issue]).join(', ')}><AlertTriangle className="h-3.5 w-3.5 shrink-0" /><span className="truncate">Dados pendentes</span></span>;
@@ -1355,30 +1355,31 @@ const Cutting = () => {
) : ( ) : (
<div className={`overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-dark-border dark:bg-dark-card ${isRefreshing ? 'refreshing-content' : ''}`} aria-busy={isRefreshing}> <div className={`overflow-hidden rounded-2xl border border-zinc-200 bg-white shadow-sm dark:border-dark-border dark:bg-dark-card ${isRefreshing ? 'refreshing-content' : ''}`} aria-busy={isRefreshing}>
<div className="overflow-x-auto"> <div className="overflow-x-auto">
<table className="w-full min-w-[940px] table-fixed text-left text-sm"> <table className="w-full min-w-[900px] table-fixed text-left text-sm">
<colgroup> <colgroup>
<col className="w-[380px]" /> <col className="w-[40%]" />
<col className="w-[120px]" /> <col className="w-[15%]" />
<col className="w-[120px]" /> <col className="w-[25%]" />
<col className="w-[140px]" /> <col className="w-[12%]" />
<col className="w-[220px]" /> <col className="w-[8%]" />
<col className="w-[100px]" />
</colgroup> </colgroup>
<thead className="border-b border-zinc-100 bg-zinc-50 text-zinc-500 dark:border-dark-border dark:bg-dark-header dark:text-dark-muted"> <thead className="sticky top-0 z-10 border-b border-zinc-100 bg-zinc-50 text-zinc-500 dark:border-dark-border dark:bg-dark-header dark:text-dark-muted">
<tr> <tr>
<th className="px-4 py-4 text-[10px] font-bold uppercase tracking-wider">Produto</th> <th className="px-4 py-4 text-[10px] font-bold uppercase tracking-wider">Produto</th>
<th className="px-4 py-4 text-[10px] font-bold uppercase tracking-wider">Demanda</th> <th className="px-4 py-4 text-[10px] font-bold uppercase tracking-wider">Cortar agora</th>
<th className="px-4 py-4 text-[10px] font-bold uppercase tracking-wider">Estoque</th> <th className="px-4 py-4 text-[10px] font-bold uppercase tracking-wider">Demanda / disponível</th>
<th className="px-4 py-4 text-[10px] font-bold uppercase tracking-wider">Necessidade</th> <th className="px-4 py-4 text-center text-[10px] font-bold uppercase tracking-wider">Status</th>
<th className="px-4 py-4 text-[10px] font-bold uppercase tracking-wider">Situação para corte</th>
<th className="px-4 py-4 text-right text-[10px] font-bold uppercase tracking-wider">Ações</th> <th className="px-4 py-4 text-right text-[10px] font-bold uppercase tracking-wider">Ações</th>
</tr> </tr>
</thead> </thead>
<tbody className="divide-y divide-zinc-100 dark:divide-dark-border"> <tbody className="divide-y divide-zinc-100 dark:divide-dark-border">
{paginatedRows.map(row => ( {paginatedRows.map(row => {
<tr key={row.id} className="transition-colors hover:bg-zinc-50/80 dark:hover:bg-dark-input/50"> const readiness = materialReadinessByProductId.get(row.id);
const needsAttention = readiness?.status !== 'ready';
return (
<tr key={row.id} className={`transition-colors hover:bg-zinc-50/80 dark:hover:bg-dark-input/50 ${needsAttention ? 'border-l-2 border-amber-400/70' : ''}`}>
<td className="max-w-0 px-4 py-2.5"> <td className="max-w-0 px-4 py-2.5">
<div className="truncate font-semibold text-zinc-900 dark:text-dark-text" title={row.name}>{row.name}</div> <Link to={`/products/${row.id}`} className="block truncate font-semibold text-zinc-900 transition-colors hover:text-brand-primary dark:text-dark-text" title={`Ver ${row.name}`}>{row.name}</Link>
<div className="mt-1 flex flex-wrap items-center gap-1.5"> <div className="mt-1 flex flex-wrap items-center gap-1.5">
<span className="font-mono text-[10px] font-medium text-zinc-400 dark:text-dark-muted">#{row.id}</span> <span className="font-mono text-[10px] font-medium text-zinc-400 dark:text-dark-muted">#{row.id}</span>
<span className={`inline-flex rounded-full border px-2 py-0.5 text-[10px] font-bold ${familyStyles[row.family.key]}`}>{row.family.materialLabel}</span> <span className={`inline-flex rounded-full border px-2 py-0.5 text-[10px] font-bold ${familyStyles[row.family.key]}`}>{row.family.materialLabel}</span>
@@ -1387,21 +1388,17 @@ const Cutting = () => {
</div> </div>
<div className="mt-1 text-[10px] font-medium text-zinc-400 dark:text-dark-muted">Média: {formatNumber(row.dailySales, 2)} un./dia · Cobertura: {formatDays(row.daysOfCover)}</div> <div className="mt-1 text-[10px] font-medium text-zinc-400 dark:text-dark-muted">Média: {formatNumber(row.dailySales, 2)} un./dia · Cobertura: {formatDays(row.daysOfCover)}</div>
</td> </td>
<td className="px-4 py-2.5 font-bold whitespace-nowrap text-zinc-900 dark:text-dark-text">{formatNumber(row.projectedDemand, 1)} un.</td>
<td className="px-4 py-2.5 whitespace-nowrap"> <td className="px-4 py-2.5 whitespace-nowrap">
<div className="font-bold text-zinc-900 dark:text-dark-text">{formatNumber(row.stock)} un.</div> <div className="text-lg font-bold text-red-300">{formatNumber(row.suggestedCutQuantity)} un.</div>
{!!row.openProductionQuantity && ( <div className="mt-1 text-[10px] font-semibold text-dark-muted">necessidade calculada</div>
<div className="text-[10px] font-semibold text-dark-muted">Disp. {formatNumber(row.availableQuantity)} un.</div>
)}
</td> </td>
<td className="px-4 py-2.5 whitespace-nowrap"> <td className="px-4 py-2.5 whitespace-nowrap">
<span className={row.suggestedCutQuantity > 0 ? 'font-bold text-red-300' : 'font-bold text-emerald-300'}> <div className="text-sm font-bold text-dark-text">Demanda {formatNumber(row.projectedDemand, 1)} un.</div>
{formatNumber(row.suggestedCutQuantity)} un. <div className="mt-1 text-xs font-semibold text-dark-muted">Disponível {formatNumber(row.availableQuantity)} un.{row.openProductionQuantity ? ` · ${formatNumber(row.openProductionQuantity)} em OP` : ''}</div>
</span>
</td> </td>
<td className="px-4 py-2.5 align-middle">{renderCutStatus(row)}</td> <td className="px-4 py-2.5 text-center align-middle">{renderCutStatus(row)}</td>
<td className="px-4 py-2.5 text-right"> <td className="px-4 py-2.5 text-right">
<div className="flex justify-end gap-2"> <div className="flex justify-end">
<button <button
type="button" type="button"
onClick={() => openQuickCutEdit(row)} onClick={() => openQuickCutEdit(row)}
@@ -1411,18 +1408,10 @@ const Cutting = () => {
> >
<Pencil className="h-3.5 w-3.5" /> <Pencil className="h-3.5 w-3.5" />
</button> </button>
<Link
to={`/products/${row.id}`}
className="inline-flex h-8 w-8 items-center justify-center rounded-lg bg-brand-primary/10 text-brand-primary transition-opacity hover:opacity-80"
title={`Ver SKU ${row.id}`}
aria-label={`Ver SKU ${row.id}`}
>
<Eye className="h-3.5 w-3.5" />
</Link>
</div> </div>
</td> </td>
</tr> </tr>
))} )})}
</tbody> </tbody>
</table> </table>
</div> </div>