Simplify cutting page workflow views
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m44s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 1m44s
This commit is contained in:
@@ -16,6 +16,7 @@ type SaveStatus = 'idle' | 'saving' | 'saved' | 'error';
|
|||||||
type CutProductIssue = Exclude<CutIssue, 'missing_yield_rule'>;
|
type CutProductIssue = Exclude<CutIssue, 'missing_yield_rule'>;
|
||||||
type CorrectionIssueFilter = CutProductIssue | 'all';
|
type CorrectionIssueFilter = CutProductIssue | 'all';
|
||||||
type SettingsSection = 'rules' | 'corrections';
|
type SettingsSection = 'rules' | 'corrections';
|
||||||
|
type CuttingView = 'plan' | 'families' | 'issues';
|
||||||
|
|
||||||
const SETTINGS_STORAGE_KEY = 'nexstar_cutting_settings';
|
const SETTINGS_STORAGE_KEY = 'nexstar_cutting_settings';
|
||||||
const coverageTargetOptions = [7, 15, 30, 60];
|
const coverageTargetOptions = [7, 15, 30, 60];
|
||||||
@@ -144,6 +145,7 @@ const Cutting = () => {
|
|||||||
const [itemsPerPage, setItemsPerPage] = useState(10);
|
const [itemsPerPage, setItemsPerPage] = useState(10);
|
||||||
const [isGeneratingOrders, setIsGeneratingOrders] = useState(false);
|
const [isGeneratingOrders, setIsGeneratingOrders] = useState(false);
|
||||||
const [isOrderPreviewOpen, setIsOrderPreviewOpen] = useState(false);
|
const [isOrderPreviewOpen, setIsOrderPreviewOpen] = useState(false);
|
||||||
|
const [activeCuttingView, setActiveCuttingView] = useState<CuttingView>('plan');
|
||||||
const [generationMessage, setGenerationMessage] = useState('');
|
const [generationMessage, setGenerationMessage] = useState('');
|
||||||
const cutConfigSection = searchParams.get('config');
|
const cutConfigSection = searchParams.get('config');
|
||||||
const targetCorrectionSku = (searchParams.get('sku') || '').trim();
|
const targetCorrectionSku = (searchParams.get('sku') || '').trim();
|
||||||
@@ -979,7 +981,59 @@ const Cutting = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!!issueSummaries.length && (
|
<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">Plano</span>
|
||||||
|
<span className="block text-xs font-semibold opacity-80">SKUs para cortar</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span className="text-sm font-bold">{formatNumber(rowsAvailableForOrder.length)}</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="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">
|
<div className="mb-3 flex flex-col gap-3 md:flex-row md:items-center md:justify-between">
|
||||||
<div>
|
<div>
|
||||||
@@ -1002,8 +1056,21 @@ const Cutting = () => {
|
|||||||
<Settings2 className="h-4 w-4 text-brand-primary" />
|
<Settings2 className="h-4 w-4 text-brand-primary" />
|
||||||
Corrigir
|
Corrigir
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
setCutFilter('issues');
|
||||||
|
setCurrentPage(1);
|
||||||
|
setActiveCuttingView('plan');
|
||||||
|
}}
|
||||||
|
className="inline-flex items-center justify-center gap-2 rounded-xl border border-dark-border bg-dark-input px-3 py-2 text-xs font-bold text-dark-text transition-colors hover:border-brand-primary cursor-pointer"
|
||||||
|
>
|
||||||
|
<Search className="h-4 w-4 text-brand-primary" />
|
||||||
|
Ver SKUs
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{issueSummaries.length ? (
|
||||||
<div className="grid grid-cols-1 gap-3 md:grid-cols-4">
|
<div className="grid grid-cols-1 gap-3 md:grid-cols-4">
|
||||||
{issueSummaries.map(summary => (
|
{issueSummaries.map(summary => (
|
||||||
<div
|
<div
|
||||||
@@ -1018,10 +1085,16 @@ const Cutting = () => {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="rounded-xl border border-dark-border bg-dark-input/60 px-4 py-8 text-center">
|
||||||
|
<p className="text-sm font-bold text-dark-text">Nenhuma pendência no plano atual.</p>
|
||||||
|
<p className="mt-1 text-xs font-semibold text-dark-muted">Os SKUs com necessidade já têm os dados mínimos para o cálculo.</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!!cutPlan.familySummaries.length && (
|
{activeCuttingView === 'families' && (
|
||||||
<div className="rounded-2xl border border-dark-border bg-dark-card p-4 shadow-sm">
|
<div className="rounded-2xl border border-dark-border bg-dark-card p-4 shadow-sm">
|
||||||
<div className="mb-3 flex items-center justify-between gap-3">
|
<div className="mb-3 flex items-center justify-between gap-3">
|
||||||
<div>
|
<div>
|
||||||
@@ -1030,6 +1103,7 @@ const Cutting = () => {
|
|||||||
</div>
|
</div>
|
||||||
<Scissors className="h-5 w-5 text-dark-muted" />
|
<Scissors className="h-5 w-5 text-dark-muted" />
|
||||||
</div>
|
</div>
|
||||||
|
{cutPlan.familySummaries.length ? (
|
||||||
<div className="grid grid-cols-1 gap-3 md:grid-cols-2 2xl:grid-cols-5">
|
<div className="grid grid-cols-1 gap-3 md:grid-cols-2 2xl:grid-cols-5">
|
||||||
{cutPlan.familySummaries.map(summary => (
|
{cutPlan.familySummaries.map(summary => (
|
||||||
<div
|
<div
|
||||||
@@ -1052,9 +1126,17 @@ const Cutting = () => {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="rounded-xl border border-dark-border bg-dark-input/60 px-4 py-8 text-center">
|
||||||
|
<p className="text-sm font-bold text-dark-text">Nenhuma família com necessidade no período.</p>
|
||||||
|
<p className="mt-1 text-xs font-semibold text-dark-muted">Ajuste o período ou a cobertura alvo para revisar outro cenário.</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{activeCuttingView === 'plan' && (
|
||||||
|
<>
|
||||||
<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="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">
|
<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" />
|
<Search className="absolute left-3 top-1/2 h-5 w-5 -translate-y-1/2 text-zinc-400 dark:text-dark-muted" />
|
||||||
@@ -1251,6 +1333,8 @@ const Cutting = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user