import React, { useState, useEffect } from "react"; import { productService, ClasseMercadologica, } from "../src/services/product.service"; import { authService } from "../src/services/auth.service"; import { CustomAutocomplete } from "./ui/autocomplete"; interface DepartmentItem { name: string; url: string; path: string; // Adicionar path para seguir padrão do Angular subcategories: DepartmentItem[]; } interface FilterSidebarProps { selectedDepartment: string; onDepartmentChange: (department: string) => void; selectedBrands: string[]; onBrandsChange: (brands: string[]) => void; filters: { onlyInStock: boolean; stockBranch: string; onPromotion: boolean; discountRange: [number, number]; priceDropped: boolean; opportunities: boolean; unmissableOffers: boolean; }; onFiltersChange: (filters: FilterSidebarProps["filters"]) => void; onApplyFilters: () => void; brands?: string[]; // Marcas podem ser passadas como prop opcional onBrandsLoaded?: (brands: string[]) => void; // Callback quando marcas são carregadas } const FilterSidebar: React.FC = ({ selectedDepartment, onDepartmentChange, selectedBrands, onBrandsChange, filters, onFiltersChange, onApplyFilters, brands: brandsProp, onBrandsLoaded, }) => { const [expandedDepartments, setExpandedDepartments] = useState([]); const [departments, setDepartments] = useState([]); const [brands, setBrands] = useState(brandsProp || []); const [stores, setStores] = useState< Array<{ id: string; name: string; shortName: string }> >([]); const [loadingDepartments, setLoadingDepartments] = useState(true); const [loadingBrands, setLoadingBrands] = useState(false); const [loadingStores, setLoadingStores] = useState(true); const [error, setError] = useState(null); // Sincronizar marcas quando a prop mudar useEffect(() => { if (brandsProp) { setBrands(brandsProp); if (onBrandsLoaded) { onBrandsLoaded(brandsProp); } } }, [brandsProp, onBrandsLoaded]); /** * Carrega as filiais disponíveis para o usuário */ useEffect(() => { const loadStores = async () => { try { setLoadingStores(true); const storesData = await productService.getStores(); setStores(storesData); } catch (err: any) { console.error("Erro ao carregar filiais:", err); // Não define erro global pois as filiais são opcionais } finally { setLoadingStores(false); } }; loadStores(); }, []); /** * Mapeia os dados hierárquicos da API para a estrutura do componente * Segue o mesmo padrão do Angular: mapItems */ /** * Mapeia os dados hierárquicos da API para a estrutura do componente * Segue EXATAMENTE o padrão do Angular: mapItems * No Angular: path: item.url (cada item tem sua própria URL, usada diretamente) * * IMPORTANTE: No Angular, quando clica em uma seção dentro de um departamento, * a URL da seção no banco já deve ser completa (ex: "ferragens/cadeados") * ou precisa ser construída concatenando departamento/seção. * * Seguindo o exemplo do usuário "ferragens/cadeados", vamos construir o path * completo quando há hierarquia, mas também manter a URL original do item. */ const mapItems = ( items: any[], textFields: string[], childFields: string[], level: number = 0, parentPath: string = "" // Path do item pai para construir URL completa quando necessário ): DepartmentItem[] => { const childField = childFields[level]; const textField = textFields[level]; return items .filter((item) => item && item[textField] != null) .map((item) => { // No Angular: path: item.url (usa diretamente a URL do item) const itemUrl = item.url || ""; // Construir path completo seguindo hierarquia // Se há parentPath e itemUrl não começa com parentPath, concatenar // Caso contrário, usar apenas itemUrl (já vem completo da API) let fullPath = itemUrl; if (parentPath && itemUrl && !itemUrl.startsWith(parentPath)) { // Se a URL não começa com o path do pai, construir concatenando fullPath = `${parentPath}/${itemUrl}`; } else if (!itemUrl && parentPath) { // Se não tem URL mas tem parentPath, usar apenas parentPath fullPath = parentPath; } const result: DepartmentItem = { name: item[textField] || "", url: itemUrl, // URL original do item path: fullPath, // Path completo para usar no filtro (seguindo padrão do Angular) subcategories: [], }; if ( childField && item[childField] && Array.isArray(item[childField]) && item[childField].length > 0 ) { // Passar o path completo para os filhos (para construir hierarquia completa) result.subcategories = mapItems( item[childField], textFields, childFields, level + 1, fullPath // Passar path completo para construir hierarquia ); } return result; }); }; /** * Carrega os departamentos da API */ useEffect(() => { const loadDepartments = async () => { try { setLoadingDepartments(true); setError(null); const data = await productService.getClasseMercadologica(); // Seguir EXATAMENTE o padrão do Angular: mapItems com os mesmos parâmetros const mappedItems = mapItems( data.filter((d) => d.tituloEcommerce != null), // Filtrar como no Angular ["tituloEcommerce", "descricaoSecao", "descricaoCategoria"], ["secoes", "categorias"], 0, // level inicial "" // parentPath inicial vazio ); setDepartments(mappedItems); // Departamentos iniciam todos fechados (não expandidos) } catch (err: any) { console.error("Erro ao carregar departamentos:", err); setError("Erro ao carregar departamentos. Tente novamente."); } finally { setLoadingDepartments(false); } }; loadDepartments(); }, []); /** * Carrega as marcas a partir dos produtos * Segue o mesmo padrão do Angular: extrai marcas dos produtos retornados pela API * No Angular: quando getProductByFilter é chamado, os produtos são processados para extrair marcas * * NOTA: As marcas serão carregadas quando os produtos forem buscados pela primeira vez * Por enquanto, não carregamos automaticamente para evitar chamadas desnecessárias à API * O componente pai pode passar as marcas via props quando necessário */ useEffect(() => { // As marcas serão carregadas dinamicamente quando os produtos forem buscados // Isso evita uma chamada extra à API no carregamento inicial // O componente pai pode passar as marcas via props quando necessário setLoadingBrands(false); }, []); return ( ); }; export default FilterSidebar;