Simplify cutting plan layout
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 47s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 47s
This commit is contained in:
@@ -1040,58 +1040,47 @@ const Cutting = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-4">
|
||||
<div className="rounded-2xl border border-dark-border bg-dark-card p-5 shadow-sm">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<p className="text-xs font-bold uppercase tracking-widest text-dark-muted">Necessidade bruta</p>
|
||||
<p className="mt-2 text-3xl font-bold text-red-300">{formatNumber(cutExecutionSummary.rawUnits)}</p>
|
||||
<p className="mt-1 text-xs font-semibold text-dark-muted">{formatNumber(cutPlan.summary.skuCount)} SKUs com necessidade</p>
|
||||
</div>
|
||||
<div className="rounded-xl border border-red-400/25 bg-red-400/10 p-3 text-red-300">
|
||||
<Scissors className="h-5 w-5" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-dark-border bg-dark-card p-1.5 shadow-sm">
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
<button type="button" onClick={() => setActiveCuttingView('plan')} aria-pressed={activeCuttingView === 'plan'} className={`inline-flex h-10 items-center gap-2 rounded-xl px-3 text-sm font-bold transition-colors cursor-pointer ${activeCuttingView === 'plan' ? 'bg-brand-primary/15 text-brand-primary' : 'text-dark-muted hover:bg-dark-input hover:text-dark-text'}`}>
|
||||
<Scissors className="h-4 w-4" /> Necessidades <span className="text-xs opacity-80">{formatNumber(cutPlan.summary.skuCount)}</span>
|
||||
</button>
|
||||
<button type="button" onClick={() => setActiveCuttingView('families')} aria-pressed={activeCuttingView === 'families'} className={`inline-flex h-10 items-center gap-2 rounded-xl px-3 text-sm font-bold transition-colors cursor-pointer ${activeCuttingView === 'families' ? 'bg-brand-primary/15 text-brand-primary' : 'text-dark-muted hover:bg-dark-input hover:text-dark-text'}`}>
|
||||
<Layers3 className="h-4 w-4" /> Famílias <span className="text-xs opacity-80">{formatNumber(cutPlan.familySummaries.length)}</span>
|
||||
</button>
|
||||
<button type="button" onClick={() => setActiveCuttingView('issues')} aria-pressed={activeCuttingView === 'issues'} className={`inline-flex h-10 items-center gap-2 rounded-xl px-3 text-sm font-bold transition-colors cursor-pointer ${activeCuttingView === 'issues' ? 'bg-amber-400/10 text-amber-300' : 'text-dark-muted hover:bg-dark-input hover:text-dark-text'}`}>
|
||||
<AlertTriangle className="h-4 w-4" /> Pendências <span className="text-xs opacity-80">{formatNumber(cutPlan.summary.rowsWithIssues)}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl border border-dark-border bg-dark-card p-5 shadow-sm">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<p className="text-xs font-bold uppercase tracking-widest text-dark-muted">Pronto para corte</p>
|
||||
<p className="mt-2 text-3xl font-bold text-emerald-300">{formatNumber(cutExecutionSummary.readyUnits)}</p>
|
||||
<p className="mt-1 text-xs font-semibold text-dark-muted">{formatNumber(cutExecutionSummary.readyRows)} SKUs liberados para gerar OP</p>
|
||||
</div>
|
||||
<div className="rounded-xl border border-brand-primary/25 bg-brand-primary/10 p-3 text-brand-primary">
|
||||
<Layers3 className="h-5 w-5" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl border border-dark-border bg-dark-card p-5 shadow-sm">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<p className="text-xs font-bold uppercase tracking-widest text-dark-muted">Bloqueios</p>
|
||||
<p className="mt-2 text-3xl font-bold text-amber-300">{formatNumber(cutExecutionSummary.blockedRows)}</p>
|
||||
<p className="mt-1 text-xs font-semibold text-dark-muted">{formatNumber(cutExecutionSummary.blockedByMaterial)} material · {formatNumber(cutExecutionSummary.blockedByData)} dados</p>
|
||||
</div>
|
||||
<div className="rounded-xl border border-amber-400/25 bg-amber-400/10 p-3 text-amber-300">
|
||||
<AlertTriangle className="h-5 w-5" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="rounded-2xl border border-dark-border bg-dark-card p-5 shadow-sm">
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<p className="text-xs font-bold uppercase tracking-widest text-dark-muted">Famílias</p>
|
||||
<p className="mt-2 text-3xl font-bold text-brand-primary">{formatNumber(cutPlan.summary.familiesWithNeed)}</p>
|
||||
<p className="mt-1 text-xs font-semibold text-dark-muted">{cutPlan.summary.estimatedRolls === null ? 'Rendimento ainda não configurado' : `${formatNumber(cutPlan.summary.estimatedRolls)} rolos estimados`}</p>
|
||||
</div>
|
||||
<div className="rounded-xl border border-brand-primary/25 bg-brand-primary/10 p-3 text-brand-primary">
|
||||
<Layers3 className="h-5 w-5" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 divide-x divide-y divide-dark-border overflow-hidden rounded-2xl border border-dark-border bg-dark-card shadow-sm lg:grid-cols-4 lg:divide-y-0">
|
||||
{[
|
||||
{ id: 'all', label: 'Necessidade', value: `${formatNumber(cutExecutionSummary.rawUnits)} un.`, detail: `${formatNumber(cutPlan.summary.skuCount)} SKUs`, tone: 'text-dark-text' },
|
||||
{ id: 'ready', label: 'Pronto para corte', value: `${formatNumber(cutExecutionSummary.readyUnits)} un.`, detail: `${formatNumber(cutExecutionSummary.readyRows)} SKUs liberados`, tone: 'text-emerald-300' },
|
||||
{ id: 'blocked', label: 'Bloqueados', value: formatNumber(cutExecutionSummary.blockedRows), detail: `${formatNumber(cutExecutionSummary.blockedByMaterial)} material · ${formatNumber(cutExecutionSummary.blockedByData)} dados`, tone: cutExecutionSummary.blockedRows ? 'text-amber-300' : 'text-dark-text' },
|
||||
{ id: 'families', label: 'Famílias', value: formatNumber(cutPlan.summary.familiesWithNeed), detail: cutPlan.summary.estimatedRolls === null ? 'Rendimento a configurar' : `${formatNumber(cutPlan.summary.estimatedRolls)} rolos estimados`, tone: 'text-dark-text' }
|
||||
].map(metric => (
|
||||
<button
|
||||
key={metric.id}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (metric.id === 'families') {
|
||||
setActiveCuttingView('families');
|
||||
return;
|
||||
}
|
||||
setActiveCuttingView('plan');
|
||||
setExecutionFilter(metric.id as ExecutionFilter);
|
||||
setCutFilter('need');
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className={`p-4 text-left transition-colors hover:bg-dark-input/60 cursor-pointer sm:px-5 ${metric.id === 'families' ? activeCuttingView === 'families' ? 'bg-brand-primary/[0.07]' : '' : activeCuttingView === 'plan' && executionFilter === metric.id ? 'bg-brand-primary/[0.07]' : ''}`}
|
||||
>
|
||||
<p className="text-[10px] font-bold uppercase tracking-widest text-dark-muted">{metric.label}</p>
|
||||
<p className={`mt-1 text-xl font-bold ${metric.tone}`}>{metric.value}</p>
|
||||
<p className="mt-1 truncate text-xs font-semibold text-dark-muted">{metric.detail}</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{cutExecutionSummary.rawUnits > 0 && cutExecutionSummary.readyUnits === 0 && (
|
||||
@@ -1104,58 +1093,6 @@ const Cutting = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="rounded-2xl border border-dark-border bg-dark-card p-2 shadow-sm">
|
||||
<div className="grid grid-cols-1 gap-2 md:grid-cols-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveCuttingView('plan')}
|
||||
aria-pressed={activeCuttingView === 'plan'}
|
||||
className={`flex items-center justify-between gap-3 rounded-xl border px-4 py-3 text-left transition-colors cursor-pointer ${activeCuttingView === 'plan' ? 'border-brand-primary/40 bg-brand-primary/15 text-brand-primary' : 'border-transparent text-dark-muted hover:bg-dark-input hover:text-dark-text'}`}
|
||||
>
|
||||
<span className="flex items-center gap-3">
|
||||
<Scissors className="h-4 w-4" />
|
||||
<span>
|
||||
<span className="block text-sm font-bold">Necessidades</span>
|
||||
<span className="block text-xs font-semibold opacity-80">Demanda por SKU</span>
|
||||
</span>
|
||||
</span>
|
||||
<span className="text-sm font-bold">{formatNumber(cutPlan.summary.skuCount)}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveCuttingView('families')}
|
||||
aria-pressed={activeCuttingView === 'families'}
|
||||
className={`flex items-center justify-between gap-3 rounded-xl border px-4 py-3 text-left transition-colors cursor-pointer ${activeCuttingView === 'families' ? 'border-brand-primary/40 bg-brand-primary/15 text-brand-primary' : 'border-transparent text-dark-muted hover:bg-dark-input hover:text-dark-text'}`}
|
||||
>
|
||||
<span className="flex items-center gap-3">
|
||||
<Layers3 className="h-4 w-4" />
|
||||
<span>
|
||||
<span className="block text-sm font-bold">Famílias</span>
|
||||
<span className="block text-xs font-semibold opacity-80">Resumo de corte</span>
|
||||
</span>
|
||||
</span>
|
||||
<span className="text-sm font-bold">{formatNumber(cutPlan.familySummaries.length)}</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveCuttingView('issues')}
|
||||
aria-pressed={activeCuttingView === 'issues'}
|
||||
className={`flex items-center justify-between gap-3 rounded-xl border px-4 py-3 text-left transition-colors cursor-pointer ${activeCuttingView === 'issues' ? 'border-amber-400/40 bg-amber-400/10 text-amber-300' : 'border-transparent text-dark-muted hover:bg-dark-input hover:text-dark-text'}`}
|
||||
>
|
||||
<span className="flex items-center gap-3">
|
||||
<AlertTriangle className="h-4 w-4" />
|
||||
<span>
|
||||
<span className="block text-sm font-bold">Pendências</span>
|
||||
<span className="block text-xs font-semibold opacity-80">Dados a revisar</span>
|
||||
</span>
|
||||
</span>
|
||||
<span className="text-sm font-bold">{formatNumber(cutPlan.summary.rowsWithIssues)}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{activeCuttingView === 'issues' && (
|
||||
<div className="rounded-2xl border border-dark-border bg-dark-card p-4 shadow-sm">
|
||||
<div className="mb-3 flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||
@@ -1260,29 +1197,6 @@ const Cutting = () => {
|
||||
|
||||
{activeCuttingView === 'plan' && (
|
||||
<>
|
||||
<div className="grid grid-cols-2 gap-3 xl:grid-cols-4">
|
||||
{[
|
||||
{ id: 'all' as const, label: 'Fila de corte', detail: `${formatNumber(cutPlan.summary.skuCount)} SKUs com necessidade`, value: cutExecutionSummary.rawUnits, tone: 'default' },
|
||||
{ id: 'ready' as const, label: 'Pronto para cortar', detail: `${formatNumber(cutExecutionSummary.readyRows)} SKUs liberados`, value: cutExecutionSummary.readyUnits, tone: 'ready' },
|
||||
{ id: 'blocked' as const, label: 'Falta material', detail: `${formatNumber(cutExecutionSummary.blockedByMaterial)} SKUs bloqueados`, value: cutExecutionSummary.blockedByMaterial, tone: 'blocked' },
|
||||
{ id: 'data' as const, label: 'Dados pendentes', detail: 'Material disponível, cadastro incompleto', value: cutExecutionSummary.blockedByData, tone: 'data' },
|
||||
].map(option => (
|
||||
<button
|
||||
key={option.id}
|
||||
type="button"
|
||||
onClick={() => {
|
||||
setExecutionFilter(option.id);
|
||||
setCutFilter('need');
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className={`rounded-2xl border p-4 text-left shadow-sm transition-colors cursor-pointer ${executionFilter === option.id ? option.tone === 'ready' ? 'border-emerald-400/45 bg-emerald-400/[0.07]' : option.tone === 'blocked' ? 'border-red-400/45 bg-red-400/[0.07]' : option.tone === 'data' ? 'border-amber-400/45 bg-amber-400/[0.07]' : 'border-brand-primary/45 bg-brand-primary/[0.07]' : 'border-dark-border bg-dark-card hover:border-brand-primary/35'}`}
|
||||
>
|
||||
<p className="text-xs font-bold uppercase tracking-widest text-dark-muted">{option.label}</p>
|
||||
<p className={`mt-2 text-2xl font-bold ${option.tone === 'ready' ? 'text-emerald-300' : option.tone === 'blocked' ? 'text-red-300' : option.tone === 'data' ? 'text-amber-300' : 'text-dark-text'}`}>{formatNumber(option.value)}{option.id === 'all' || option.id === 'ready' ? ' un.' : ''}</p>
|
||||
<p className="mt-1 truncate text-xs font-semibold text-dark-muted">{option.detail}</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-3 rounded-2xl border border-dark-border bg-dark-card p-4 shadow-sm xl:grid-cols-[1fr_170px_170px_170px_190px]">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-zinc-400 dark:text-dark-muted" />
|
||||
|
||||
Reference in New Issue
Block a user