Improve users and purchase needs UI
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 35s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 35s
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
||||
Warehouse,
|
||||
} from 'lucide-react';
|
||||
import { buildConsumptionReferencePath } from '../catalogLinks';
|
||||
import PaginationControls from '../components/PaginationControls';
|
||||
import { adjustSupplyLotInventory, approveSupplyReceipt, consumeSupplyLotForProduction, createSupplyFabricPlan, createSupplyReceipt, deleteSupplyFabricPlan, deleteSupplyReceipt, fetchSupplySummary } from '../dataService';
|
||||
import type { SupplyFabricPlan, SupplyLot, SupplyMovement, SupplyPurchaseNeed, SupplyReceipt, SupplySummary } from '../types';
|
||||
|
||||
@@ -1148,6 +1149,8 @@ const PurchaseNeedsScreen = () => {
|
||||
const [summary, setSummary] = useState<SupplySummary>(emptySupplySummary);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [search, setSearch] = useState('');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [itemsPerPage, setItemsPerPage] = useState(10);
|
||||
|
||||
const loadSummary = async () => {
|
||||
const nextSummary = await fetchSupplySummary();
|
||||
@@ -1178,6 +1181,10 @@ const PurchaseNeedsScreen = () => {
|
||||
const visibleNeeds = summary.purchaseNeeds.filter(need => (
|
||||
!normalizedSearch || normalizeSearch(`${need.material} ${need.suppliers.join(' ')} ${need.colors.join(' ')}`).includes(normalizedSearch)
|
||||
));
|
||||
const totalPages = Math.ceil(visibleNeeds.length / itemsPerPage);
|
||||
const safeCurrentPage = Math.min(currentPage, totalPages || 1);
|
||||
const startIndex = (safeCurrentPage - 1) * itemsPerPage;
|
||||
const paginatedNeeds = visibleNeeds.slice(startIndex, startIndex + itemsPerPage);
|
||||
const purchaseItemCount = summary.purchaseNeeds.filter(need => need.purchaseKg > 0).length;
|
||||
const suggestedPurchaseSummary = summarizePurchaseNeeds(summary.purchaseNeeds);
|
||||
const pendingSupplierCount = new Set(summary.purchaseNeeds.flatMap(need => need.suppliers)).size;
|
||||
@@ -1230,7 +1237,15 @@ const PurchaseNeedsScreen = () => {
|
||||
</div>
|
||||
<label className="relative mt-4 block">
|
||||
<Search className="pointer-events-none absolute left-3 top-3 h-4 w-4 text-dark-muted" />
|
||||
<input value={search} onChange={(event) => setSearch(event.target.value)} className={`${inputClassName} pl-9`} placeholder="Buscar por material, fornecedor ou cor..." />
|
||||
<input
|
||||
value={search}
|
||||
onChange={(event) => {
|
||||
setSearch(event.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className={`${inputClassName} pl-9`}
|
||||
placeholder="Buscar por material, fornecedor ou cor..."
|
||||
/>
|
||||
</label>
|
||||
{isLoading ? (
|
||||
<div className={emptyStateClassName}>
|
||||
@@ -1239,20 +1254,33 @@ const PurchaseNeedsScreen = () => {
|
||||
</div>
|
||||
) : visibleNeeds.length ? (
|
||||
<div className="mt-4 overflow-hidden rounded-xl border border-dark-border">
|
||||
<div className="hidden grid-cols-[1.3fr_110px_110px_110px_110px_110px] border-b border-dark-border bg-dark-input/40 px-4 py-3 text-xs font-bold uppercase tracking-widest text-dark-muted lg:grid">
|
||||
<span>Material</span>
|
||||
<span>Planejado</span>
|
||||
<span>Estoque</span>
|
||||
<span>Pendente</span>
|
||||
<span>Comprar</span>
|
||||
<span>Cobertura</span>
|
||||
</div>
|
||||
<div className="divide-y divide-dark-border">
|
||||
{visibleNeeds.map(need => {
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full min-w-[920px] table-fixed border-collapse">
|
||||
<colgroup>
|
||||
<col />
|
||||
<col className="w-[120px]" />
|
||||
<col className="w-[120px]" />
|
||||
<col className="w-[120px]" />
|
||||
<col className="w-[120px]" />
|
||||
<col className="w-[150px]" />
|
||||
</colgroup>
|
||||
<thead className="border-b border-dark-border bg-dark-input/40 text-xs font-bold uppercase tracking-widest text-dark-muted">
|
||||
<tr>
|
||||
<th scope="col" className="px-4 py-3 text-left">Material</th>
|
||||
<th scope="col" className="px-4 py-3 text-right">Planejado</th>
|
||||
<th scope="col" className="px-4 py-3 text-right">Estoque</th>
|
||||
<th scope="col" className="px-4 py-3 text-right">Pendente</th>
|
||||
<th scope="col" className="px-4 py-3 text-right">Comprar</th>
|
||||
<th scope="col" className="px-4 py-3 text-left">Cobertura</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-dark-border bg-dark-card">
|
||||
{paginatedNeeds.map(need => {
|
||||
const referenceProduct = need.products?.[0];
|
||||
return (
|
||||
<div key={need.material} className="grid grid-cols-1 gap-3 bg-dark-card px-4 py-4 lg:grid-cols-[1.3fr_110px_110px_110px_110px_110px] lg:items-center">
|
||||
<div>
|
||||
<tr key={need.material}>
|
||||
<td className="px-4 py-4 align-middle">
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-bold text-dark-text">{need.material}</p>
|
||||
<div className="mt-1 flex flex-wrap items-center gap-2">
|
||||
<p className="text-xs font-semibold text-dark-muted">
|
||||
@@ -1277,12 +1305,14 @@ const PurchaseNeedsScreen = () => {
|
||||
{need.products.length > 2 ? ` +${need.products.length - 2}` : ''}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<p className="text-sm font-bold text-dark-text">{formatNeedQuantity(need, need.plannedKg)}</p>
|
||||
<p className="text-sm font-bold text-dark-text">{formatNeedQuantity(need, need.stockKg)}</p>
|
||||
<p className="text-sm font-bold text-dark-text">{formatNeedQuantity(need, need.pendingKg)}</p>
|
||||
<p className={`text-sm font-bold ${need.purchaseKg > 0 ? 'text-red-300' : 'text-emerald-300'}`}>{formatNeedQuantity(need, need.purchaseKg)}</p>
|
||||
<span className={`w-fit rounded-full border px-2.5 py-1 text-xs font-bold ${
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-4 text-right align-middle text-sm font-bold text-dark-text">{formatNeedQuantity(need, need.plannedKg)}</td>
|
||||
<td className="px-4 py-4 text-right align-middle text-sm font-bold text-dark-text">{formatNeedQuantity(need, need.stockKg)}</td>
|
||||
<td className="px-4 py-4 text-right align-middle text-sm font-bold text-dark-text">{formatNeedQuantity(need, need.pendingKg)}</td>
|
||||
<td className={`px-4 py-4 text-right align-middle text-sm font-bold ${need.purchaseKg > 0 ? 'text-amber-300' : 'text-emerald-300'}`}>{formatNeedQuantity(need, need.purchaseKg)}</td>
|
||||
<td className="px-4 py-4 align-middle">
|
||||
<span className={`inline-flex whitespace-nowrap rounded-full border px-2.5 py-1 text-xs font-bold ${
|
||||
need.missingReference
|
||||
? 'border-red-400/30 bg-red-400/10 text-red-300'
|
||||
: need.status === 'critical'
|
||||
@@ -1293,10 +1323,30 @@ const PurchaseNeedsScreen = () => {
|
||||
}`}>
|
||||
{need.missingReference ? 'Sem referência' : purchaseStatusLabels[need.status]}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<PaginationControls
|
||||
totalItems={visibleNeeds.length}
|
||||
currentPage={safeCurrentPage}
|
||||
totalPages={totalPages}
|
||||
pageSize={itemsPerPage}
|
||||
pageSizeOptions={[10, 20, 50, 100]}
|
||||
itemLabel="materiais"
|
||||
pageSizeLabel="itens por página"
|
||||
startIndex={startIndex}
|
||||
endIndex={Math.min(startIndex + itemsPerPage, visibleNeeds.length)}
|
||||
onPageChange={setCurrentPage}
|
||||
onPageSizeChange={(pageSize) => {
|
||||
setItemsPerPage(pageSize);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="border-t border-dark-border px-4 py-3"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className={emptyStateClassName}>
|
||||
@@ -1306,10 +1356,6 @@ const PurchaseNeedsScreen = () => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={`${panelClassName} p-5`}>
|
||||
<h2 className="text-base font-bold text-dark-text">Como o fluxo está conectado</h2>
|
||||
<p className="mt-2 text-sm font-semibold text-dark-muted">Plano de malha cria demanda. Recebimento pendente entra como material a receber. Aprovar recebimento move para estoque e movimentações.</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user