Files
Portalweb/src/app/orders/find/page.tsx
2025-07-16 09:39:32 -03:00

425 lines
16 KiB
TypeScript

"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,
});
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>
);
}