$(function() { const adsList = $("#ads-list"); const loadMoreWrap = $(".load-more-wrap"); const loadMoreButton = $("#load-more-ads"); const loading = $("#ads-loading"); const clearFiltersButton = $("#clear-ad-filters"); let isLoadingAds = false; let lastFilterSignature = JSON.stringify(collectAdFilters()); loading.addClass("hidden"); updateClearFiltersButton(); $("#apply-ad-filters").on("click", function() { loadAds(0, false); }); clearFiltersButton.on("click", function() { clearAdFilters(); loadAds(0, false); }); $("#adsSort").on("change", function() { updateClearFiltersButton(); loadAds(0, false); }); $("#adsSearch").on("keydown", function(event) { if (event.key === "Enter") { event.preventDefault(); loadAds(0, false); } }); $("#adsSearch, #adsPrice").on("input change", function() { updateClearFiltersButton(); }); $("#adsState").on("change", function() { updateClearFiltersButton(); loadAdCities($(this).val(), ""); }); $("#adsCity").on("change", function() { updateClearFiltersButton(); }); $('input[name="manufacturer"], input[name="scale"], input[name="condition"]').on("change", function() { updateClearFiltersButton(); }); $(".chip").on("click", function() { const chip = $(this); const chipValue = chip.data("chip"); if (chipValue === "all") { $(".chip").removeClass("active"); chip.addClass("active"); } else { $('.chip[data-chip="all"]').removeClass("active"); chip.toggleClass("active"); if ($(".chip.active").length === 0) { $('.chip[data-chip="all"]').addClass("active"); } } loadAds(0, false); }); loadMoreButton.on("click", function() { const offset = parseInt(loadMoreButton.attr("data-offset"), 10) || 0; loadAds(offset, true); }); $(document).on("click", ".public-admin-ad-menu-toggle", function(event) { event.preventDefault(); event.stopPropagation(); const menu = $(this).closest(".public-admin-ad-menu"); const isOpen = menu.hasClass("is-open"); closePublicAdminAdMenus(); if (!isOpen) { menu.addClass("is-open"); positionPublicAdminAdMenu(menu); $(this).attr("aria-expanded", "true"); } }); $(document).on("click", function() { closePublicAdminAdMenus(); }); $(document).on("click", ".public-admin-ad-action", async function(event) { event.preventDefault(); event.stopPropagation(); const button = $(this); const publicId = button.data("ad-id"); const action = button.data("ad-action"); closePublicAdminAdMenus(); if (button.prop("disabled") || button.data("loading")) { return; } if (!publicId || action !== "block") { popZ("Ação administrativa inválida para este anúncio.", "warning"); return; } const confirmed = await confirmPublicAdminAdAction({ title: "Bloquear anúncio?", text: "Use esta ação para anúncios irregulares. O anúncio será removido do catálogo público.", confirmButtonText: "Sim, bloquear" }); if (!confirmed) { return; } button.data("loading", true).prop("disabled", true).attr("aria-busy", "true"); $.ajax({ type: "POST", url: "/api/admin/ads", data: { action: "update_status", public_id: publicId, ad_action: action }, dataType: "json", success: function(response) { handlePublicAdminAdResponse(response); if (response.result === "admin_ad_status_updated") { loadAds(0, false, true); } }, error: function(jqXhr) { handlePublicAdminAdResponse(jqXhr.responseJSON || {}); }, complete: function() { button.data("loading", false).prop("disabled", false).removeAttr("aria-busy"); } }); }); function loadAds(offset, append, force) { const filters = collectAdFilters(); const filterSignature = JSON.stringify(filters); force = Boolean(force); if (isLoadingAds) { return; } if (!append && !force && filterSignature === lastFilterSignature) { return; } isLoadingAds = true; loading.removeClass("hidden"); loadMoreButton.prop("disabled", true).text("Carregando..."); $.ajax({ type: "POST", url: "/api/anuncios", data: { offset: offset, limit: 12, search: filters.search, manufacturers: filters.manufacturers, scales: filters.scales, conditions: filters.conditions, price: filters.price, state: filters.state, city: filters.city, chips: filters.chips, sort: filters.sort, seller_username: filters.sellerUsername }, dataType: "json", success: function(response) { if (response.result !== "ads_loaded") { popZ("Não foi possível carregar os anúncios.", "error"); return; } if (append) { adsList.append(response.html); } else { adsList.html(response.html); } loadMoreButton.attr("data-offset", response.nextOffset); if (response.hasMore) { loadMoreWrap.removeClass("hidden"); } else { loadMoreWrap.addClass("hidden"); } lastFilterSignature = filterSignature; updateClearFiltersButton(); }, error: function(jqXhr) { if (jqXhr.status === 503) { popZ("Estamos aplicando uma atualização, tente novamente mais tarde.", "warning"); return; } popZ("Não foi possível carregar os anúncios.", "error"); }, complete: function() { isLoadingAds = false; loading.addClass("hidden"); loadMoreButton.prop("disabled", false).text("Carregar mais"); } }); } if (applyStoredHomeFilters()) { updateClearFiltersButton(); loadAds(0, false); } }); function clearAdFilters() { $("#adsSearch").val(""); $("#adsPrice").val(""); $("#adsState").val(""); $("#adsCity") .html('') .prop("disabled", true) .val(""); $("#adsSort").val("recent"); $('input[name="manufacturer"], input[name="scale"], input[name="condition"]').prop("checked", false); $(".chip").removeClass("active"); $('.chip[data-chip="all"]').addClass("active"); updateClearFiltersButton(); } function hasActiveAdFilters() { return ( ($("#adsSearch").val() || "") !== "" || ($("#adsPrice").val() || "") !== "" || ($("#adsState").val() || "") !== "" || ($("#adsCity").val() || "") !== "" || ($("#adsSort").val() || "recent") !== "recent" || $('input[name="manufacturer"]:checked').length > 0 || $('input[name="scale"]:checked').length > 0 || $('input[name="condition"]:checked').length > 0 || $('.chip.active[data-chip!="all"]').length > 0 ); } function updateClearFiltersButton() { $("#clear-ad-filters").toggleClass("hidden", !hasActiveAdFilters()); } function applyStoredHomeFilters() { const storedFilters = sessionStorage.getItem("garagem164AdFilters"); if (!storedFilters) { return false; } sessionStorage.removeItem("garagem164AdFilters"); let filters = {}; try { filters = JSON.parse(storedFilters); } catch (error) { return false; } $("#adsSearch").val(filters.search || ""); $("#adsPrice").val(filters.price || ""); $("#adsState").val(filters.state || ""); $("#adsSort").val(filters.sort || "recent"); $('input[name="manufacturer"]').prop("checked", false); String(filters.manufacturers || "").split("|").forEach(function(manufacturer) { if (manufacturer) { $('input[name="manufacturer"][value="' + manufacturer + '"]').prop("checked", true); } }); $('input[name="scale"]').prop("checked", false); String(filters.scales || "").split("|").forEach(function(scale) { if (scale) { $('input[name="scale"][value="' + scale + '"]').prop("checked", true); } }); $('input[name="condition"]').prop("checked", false); String(filters.conditions || "").split("|").forEach(function(condition) { if (condition) { $('input[name="condition"][value="' + condition + '"]').prop("checked", true); } }); $(".chip").removeClass("active"); const chips = String(filters.chips || "").split("|").filter(Boolean); if (chips.length) { chips.forEach(function(chip) { $('.chip[data-chip="' + chip + '"]').addClass("active"); }); } else { $('.chip[data-chip="all"]').addClass("active"); } if (filters.state) { loadAdCities(filters.state, filters.city || ""); } return true; } function loadAdCities(state, selectedCity) { const citySelect = $("#adsCity"); citySelect .prop("disabled", true) .html(''); if (!state) { citySelect .html('') .prop("disabled", true); return; } $.ajax({ type: "GET", url: "/api/cidades/" + encodeURIComponent(state), dataType: "json", success: function(response) { citySelect.html(''); if (!response.cities || !response.cities.length) { citySelect.html(''); return; } response.cities.forEach(function(city) { citySelect.append( $("'); popZ("Não foi possível carregar as cidades deste estado.", "error"); }, complete: function() { citySelect.prop("disabled", false); updateClearFiltersButton(); } }); } function collectAdFilters() { const manufacturers = []; const scales = []; const conditions = []; const chips = []; $('input[name="manufacturer"]:checked').each(function() { manufacturers.push($(this).val()); }); $('input[name="scale"]:checked').each(function() { scales.push($(this).val()); }); $('input[name="condition"]:checked').each(function() { conditions.push($(this).val()); }); $(".chip.active").each(function() { const chip = $(this).data("chip"); if (chip && chip !== "all") { chips.push(chip); } }); return { search: $("#adsSearch").val() || "", manufacturers: manufacturers.join("|"), scales: scales.join("|"), conditions: conditions.join("|"), price: $("#adsPrice").val() || "", state: $("#adsState").val() || "", city: $("#adsCity").val() || "", chips: chips.join("|"), sort: $("#adsSort").val() || "recent", sellerUsername: $("#adsSellerUsername").val() || "" }; } function closePublicAdminAdMenus() { $(".public-admin-ad-menu") .removeClass("is-open opens-up") .find(".public-admin-ad-menu-toggle") .attr("aria-expanded", "false"); $(".card.public-admin-menu-open").removeClass("public-admin-menu-open"); } function positionPublicAdminAdMenu(menu) { const list = menu.find(".dashboard-ad-menu-list"); if (!list.length) { return; } menu.removeClass("opens-up"); const menuRect = menu[0].getBoundingClientRect(); const listHeight = list.outerHeight() || 0; const bottomSpace = window.innerHeight - menuRect.bottom; if (bottomSpace < listHeight + 16) { menu.addClass("opens-up"); } menu.closest(".card").addClass("public-admin-menu-open"); } async function confirmPublicAdminAdAction(options) { if (typeof Swal === "undefined") { return window.confirm(options.title || "Deseja continuar?"); } const result = await Swal.fire({ icon: "warning", title: options.title, text: options.text, showCancelButton: true, confirmButtonText: options.confirmButtonText || "Confirmar", cancelButtonText: "Cancelar", reverseButtons: true, confirmButtonColor: "#e63946", cancelButtonColor: "#6b7785" }); return result.isConfirmed; } function handlePublicAdminAdResponse(response) { switch (response.result) { case "admin_ad_status_updated": popZ("Anúncio bloqueado com sucesso.", "success"); break; case "admin_ad_not_found": popZ("Anúncio não encontrado.", "warning"); break; case "invalid_admin_ad_status_transition": popZ("Este anúncio não permite essa alteração.", "warning"); break; case "invalid_admin_ad_action": popZ("Ação inválida para este anúncio.", "warning"); break; case "admin_ad_status_update_failed": popZ("Não foi possível bloquear o anúncio. Tente novamente.", "error"); break; default: popZ("Não foi possível executar a ação no anúncio.", "error"); break; } }