first
This commit is contained in:
432
src/app/orders/find/page.tsx
Normal file
432
src/app/orders/find/page.tsx
Normal file
@@ -0,0 +1,432 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/src/components/ui/card";
|
||||
import { Button } from "@/src/components/ui/button";
|
||||
import { Search, RefreshCw, ChevronUp, ChevronDown } from "lucide-react";
|
||||
import { OrdersTable } from "@/src/components/orders/OrdersTable";
|
||||
import { ErrorMessage } from "@/src/components/ui/error-message";
|
||||
import { useStores } from "@/src/hooks/useStores";
|
||||
import { useOrderSearch } from "@/src/hooks/useOrderSearch";
|
||||
import { FilterInput } from "@/src/components/orders/FilterInput";
|
||||
import { FilterSelect } from "@/src/components/orders/FilterSelect";
|
||||
import { STATUS_OPTIONS } from "@/src/constants/status-options";
|
||||
import { DELIVERY_STATUS_OPTIONS } from "@/src/constants/delivery-status-options";
|
||||
import { Spinner } from "@/src/components/ui/spinner";
|
||||
import { DateRangeFilter } from "@/src/components/orders/DateRangeFilter";
|
||||
import { MultiFilterSelect } from "@/src/components/orders/MultiFilterSelect";
|
||||
import { CustomerSearchInput } from "@/src/components/orders/CustomerSearchInput";
|
||||
import { useCallback, useState, useEffect } from "react";
|
||||
import { User, useAuthValidation } from "../../../hooks/useAuthValidation";
|
||||
import { SellerSearchInput } from "@/src/components/orders/SellerSearchInput";
|
||||
|
||||
/**
|
||||
* Página de Consulta de Pedidos
|
||||
* Permite ao usuário pesquisar pedidos com base em vários filtros
|
||||
*
|
||||
* @returns Componente da página de consulta de pedidos
|
||||
*/
|
||||
|
||||
export default function FindOrdersPage() {
|
||||
const {
|
||||
user,
|
||||
isAuthenticated,
|
||||
isLoading: authLoading,
|
||||
error: authError,
|
||||
decodedToken,
|
||||
} = useAuthValidation({
|
||||
authServiceUrl: process.env.NEXT_PUBLIC_AUTH_SERVICE_URL!,
|
||||
token: "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMzg1IiwidXNlcm5hbWUiOiJKT0VMU09OLlIiLCJyb2xlIjpudWxsLCJpYXQiOjE3NDg4MjQzNDEsImV4cCI6MTc1MzIzMDc0MX0.H4nRp72NTEzHBKij67BgW4NO8Pot9AqPKlaynne694c",
|
||||
autoRedirect: false,
|
||||
});
|
||||
|
||||
|
||||
|
||||
console.log("User:", user);
|
||||
console.log("Decoded Token:", decodedToken);
|
||||
console.log("Auth Loading:", authLoading);
|
||||
console.log("Auth Error:", authError);
|
||||
console.log("Is Authenticated:", isAuthenticated);
|
||||
|
||||
|
||||
|
||||
|
||||
const [isFiltersExpanded, setIsFiltersExpanded] = useState(true);
|
||||
const { storeOptions, loading: loadingStores, stores } = useStores(true);
|
||||
const [usePreloadClients, setUsePreloadClients] = useState(false);
|
||||
const [status, setStatus] = useState("");
|
||||
const [leadtime, setLeadtime] = useState("");
|
||||
|
||||
const {
|
||||
orders,
|
||||
loading,
|
||||
dateError,
|
||||
hasSearched,
|
||||
selectedOrderId,
|
||||
orderItems,
|
||||
cutitens,
|
||||
loadingItems,
|
||||
itemsError,
|
||||
currentPage,
|
||||
totalPages,
|
||||
currentOrders,
|
||||
indexOfFirstOrder,
|
||||
indexOfLastOrder,
|
||||
searchParams,
|
||||
setSearchParams,
|
||||
handleSearch,
|
||||
handleRowClick,
|
||||
goToNextPage,
|
||||
goToPreviousPage,
|
||||
goToPage,
|
||||
loadMoreOrders,
|
||||
visibleOrdersCount,
|
||||
handleInputChange,
|
||||
handleCustomerId,
|
||||
handleCustomerFilter,
|
||||
handleCustomerSelect,
|
||||
handleMultiInputChange,
|
||||
} = useOrderSearch(8);
|
||||
|
||||
/**
|
||||
* Limpa todos os filtros de pesquisa
|
||||
* @returns {void}
|
||||
*/
|
||||
const handleClearFilters = useCallback(() => {
|
||||
setSearchParams({});
|
||||
}, [setSearchParams]);
|
||||
|
||||
/**
|
||||
* Alterna o modo de pré-carregamento de clientes
|
||||
* Quando ativado, carrega clientes antecipadamente para melhorar o desempenho da busca
|
||||
* @returns {void}
|
||||
*/
|
||||
const togglePreloadMode = useCallback(() => {
|
||||
setUsePreloadClients((prev) => !prev);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<Card className="overflow-hidden">
|
||||
<CardHeader
|
||||
className="flex flex-row items-center justify-between space-y-0 py-1 px-4 white-slate-80 cursor-pointer"
|
||||
onClick={() => setIsFiltersExpanded(!isFiltersExpanded)}
|
||||
>
|
||||
<CardTitle className="text-lg font-bold">
|
||||
Consulta de Pedidos
|
||||
</CardTitle>
|
||||
<Button variant="ghost" size="sm" className="h-6 px-1">
|
||||
{isFiltersExpanded ? (
|
||||
<ChevronUp className="h-4 w-4" />
|
||||
) : (
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
</CardHeader>
|
||||
|
||||
{isFiltersExpanded && (
|
||||
<CardContent className="p-0">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-x-2 gap-y-2 items-start p-2">
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div className="filter-field">
|
||||
<FilterSelect
|
||||
id="codfilial"
|
||||
label="Filial de Venda"
|
||||
value={searchParams.codfilial}
|
||||
onValueChange={(value) =>
|
||||
handleInputChange("codfilial", value)
|
||||
}
|
||||
placeholder="Selecione a filial"
|
||||
disabled={loadingStores}
|
||||
options={storeOptions}
|
||||
loading={loadingStores}
|
||||
loadingText="Carregando filiais..."
|
||||
className="w-full"
|
||||
aria-label="Filial de Venda"
|
||||
/>
|
||||
</div>
|
||||
<div className="filter-field">
|
||||
<CustomerSearchInput
|
||||
id="customerSearch"
|
||||
label="Cliente"
|
||||
placeholder="Buscar por codigo, nome ou CPF/CNPJ"
|
||||
value={
|
||||
searchParams.name ||
|
||||
searchParams.customerId ||
|
||||
searchParams.document ||
|
||||
""
|
||||
}
|
||||
onChange={handleCustomerFilter}
|
||||
onSelect={handleCustomerSelect}
|
||||
selectedCustomer={searchParams.selectedCustomer || null}
|
||||
preloadClients={usePreloadClients}
|
||||
maxPreloadedClients={2000}
|
||||
aria-label="Buscar Cliente"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<div className="filter-small-field">
|
||||
<FilterInput
|
||||
id="orderId"
|
||||
label="Número do Pedido"
|
||||
type="number"
|
||||
placeholder="Número do pedido"
|
||||
value={searchParams.orderId}
|
||||
onChange={(value) => handleInputChange("orderId", value)}
|
||||
className="w-full"
|
||||
aria-label="Número do Pedido"
|
||||
/>
|
||||
</div>
|
||||
<div className="filter-small-field">
|
||||
<FilterInput
|
||||
id="invoiceNumber"
|
||||
label="Nota Fiscal"
|
||||
placeholder="Número da NF"
|
||||
value={searchParams.invoiceNumber || ""}
|
||||
onChange={(value) =>
|
||||
handleInputChange("invoiceNumber", value)
|
||||
}
|
||||
className="w-full"
|
||||
aria-label="Número da Nota Fiscal"
|
||||
/>
|
||||
</div>
|
||||
<div className="filter-small-field">
|
||||
<FilterInput
|
||||
id="shippimentId"
|
||||
label=" Carregamento"
|
||||
placeholder="Carregamento"
|
||||
value={searchParams.shippimentId || ""}
|
||||
onChange={(value) =>
|
||||
handleInputChange("shippimentId", value)
|
||||
}
|
||||
className="w-full"
|
||||
aria-label="Carregamento"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-4">
|
||||
<div className="filter-small-field">
|
||||
<MultiFilterSelect
|
||||
id="orderType"
|
||||
label="Tipo de Venda"
|
||||
placeholder="Tipo de venda"
|
||||
values={
|
||||
searchParams.type
|
||||
? Array.isArray(searchParams.type)
|
||||
? searchParams.type
|
||||
: [searchParams.type]
|
||||
: []
|
||||
}
|
||||
onValuesChange={(values) =>
|
||||
handleMultiInputChange("type", values)
|
||||
}
|
||||
options={[
|
||||
{ label: "TV1 - Retira Imediata", value: "1" },
|
||||
{ label: "TV7 - Faturamento", value: "7" },
|
||||
{ label: "TV8 - Entrega", value: "8" },
|
||||
{ label: "TV10 - Transferência", value: "10" },
|
||||
]}
|
||||
className="w-full"
|
||||
aria-label="Tipo de Venda"
|
||||
/>
|
||||
</div>
|
||||
<div className="filter-small-field">
|
||||
<MultiFilterSelect
|
||||
id="deliveryType"
|
||||
label="Tipo de Entrega"
|
||||
placeholder="Tipo de entrega"
|
||||
values={
|
||||
searchParams.deliveryType
|
||||
? Array.isArray(searchParams.deliveryType)
|
||||
? searchParams.deliveryType
|
||||
: [searchParams.deliveryType]
|
||||
: []
|
||||
}
|
||||
onValuesChange={(values) =>
|
||||
handleMultiInputChange("deliveryType", values)
|
||||
}
|
||||
options={[
|
||||
{ label: "Retira Imediata", value: "RI" },
|
||||
{ label: "Entrega", value: "EN" },
|
||||
{ label: "Entrega Futura", value: "EF" },
|
||||
{ label: "Retira Posterior", value: "RP" },
|
||||
]}
|
||||
className="w-full"
|
||||
aria-label="Tipo de Entrega"
|
||||
/>
|
||||
</div>
|
||||
<div className="filter-small-field">
|
||||
<MultiFilterSelect
|
||||
id="status"
|
||||
label="Situação"
|
||||
values={
|
||||
Array.isArray(searchParams.status)
|
||||
? searchParams.status
|
||||
: searchParams.status
|
||||
? [searchParams.status]
|
||||
: []
|
||||
}
|
||||
onValuesChange={(values) =>
|
||||
handleMultiInputChange("status", values)
|
||||
}
|
||||
placeholder=" Situação"
|
||||
options={STATUS_OPTIONS}
|
||||
className="w-full"
|
||||
aria-label="Situação do Pedido"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div className="filter-field">
|
||||
<FilterSelect
|
||||
id="stockId"
|
||||
label="Filial de Estoque"
|
||||
value={searchParams.stockId}
|
||||
onValueChange={(value) =>
|
||||
handleInputChange("stockId", value)
|
||||
}
|
||||
placeholder="Filial de estoque"
|
||||
options={storeOptions}
|
||||
loading={loadingStores}
|
||||
loadingText="Carregando filiais..."
|
||||
className="w-full"
|
||||
aria-label="Filial de Estoque"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="filter-field">
|
||||
<SellerSearchInput
|
||||
id="sellerSearch"
|
||||
label="Nome do Vendedor"
|
||||
placeholder="Buscar por nome ou código do vendedor"
|
||||
value={searchParams.sellerName || ""}
|
||||
onChange={(value) => handleInputChange("sellerName", value)}
|
||||
onSelect={(seller) => {
|
||||
if (seller) {
|
||||
if (seller.code) {
|
||||
handleInputChange("sellerId", seller.code);
|
||||
} else {
|
||||
handleInputChange("sellerId", seller.id.toString());
|
||||
}
|
||||
handleInputChange("sellerName", seller.name);
|
||||
} else {
|
||||
handleInputChange("sellerId", "");
|
||||
handleInputChange("sellerName", "");
|
||||
}
|
||||
}}
|
||||
selectedSeller={searchParams.sellerName ? {
|
||||
id: parseInt(String(searchParams.sellerId || "0"), 10) || 0,
|
||||
name: searchParams.sellerName,
|
||||
code: (searchParams.sellerId || "").toString()
|
||||
} : undefined}
|
||||
aria-label="Buscar Vendedor"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="filter-field">
|
||||
<DateRangeFilter
|
||||
startDateId="createDateIni"
|
||||
endDateId="createDateEnd"
|
||||
label="Período (Obrigatório)"
|
||||
startValue={searchParams.createDateIni}
|
||||
endValue={searchParams.createDateEnd}
|
||||
onStartChange={(value) =>
|
||||
handleInputChange("createDateIni", value)
|
||||
}
|
||||
onEndChange={(value) =>
|
||||
handleInputChange("createDateEnd", value)
|
||||
}
|
||||
aria-label="Período"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-4">
|
||||
<div className="flex flex-col space-y-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-end gap-2 mx-4 pb-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleClearFilters}
|
||||
title="Limpar filtros"
|
||||
aria-label="Limpar todos os filtros de pesquisa"
|
||||
className="h-8"
|
||||
>
|
||||
<RefreshCw className="mr-2 h-4 w-4" />
|
||||
Limpar
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant={"blue"}
|
||||
onClick={handleSearch}
|
||||
disabled={loading}
|
||||
className="h-8 px-4"
|
||||
aria-label="Pesquisar pedidos com os filtros selecionados"
|
||||
>
|
||||
{loading ? (
|
||||
<Spinner size="sm" />
|
||||
) : (
|
||||
<Search className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
Pesquisar
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{dateError && <ErrorMessage message={dateError} />}
|
||||
</CardContent>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{loading ? (
|
||||
<Card className="overflow-hidden">
|
||||
<CardContent className="flex h-40 items-center justify-center p-4">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Spinner size="md" />
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Buscando pedidos...
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : hasSearched ? (
|
||||
<Card className="overflow-hidden">
|
||||
<CardContent className="p-0">
|
||||
<OrdersTable
|
||||
orders={orders}
|
||||
currentOrders={currentOrders}
|
||||
selectedOrderId={selectedOrderId}
|
||||
orderItems={orderItems}
|
||||
cutitens={cutitens}
|
||||
loadingItems={loadingItems}
|
||||
itemsError={itemsError}
|
||||
handleRowClick={handleRowClick}
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
indexOfFirstOrder={indexOfFirstOrder}
|
||||
indexOfLastOrder={indexOfLastOrder}
|
||||
goToPreviousPage={goToPreviousPage}
|
||||
goToNextPage={goToNextPage}
|
||||
goToPage={goToPage}
|
||||
loadMoreOrders={loadMoreOrders}
|
||||
visibleOrdersCount={visibleOrdersCount}
|
||||
stores={stores}
|
||||
transfers={[]}
|
||||
status={status}
|
||||
leadtime={leadtime}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user