Testing ShadCN Select with Jest and React testing Library

I have a ShadCN Select component that is abstracted into a CustomSelect component for reusability. I am trying to test the click functionality of the options and assert the text content on the Select button but I’m faced with this error

Unable to find an element with the text: /latest products/i. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.

    Ignored nodes: comments, script, style
    <body>
      <div>
        <div
          class="min-w-40"
        >
          <button
            aria-autocomplete="none"
            aria-controls="radix-:r7:"
            aria-expanded="false"
            class="flex h-10 items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 w-full"
            data-state="closed"
            dir="ltr"
            role="combobox"
            type="button"
          >
            <span
              style="pointer-events: none;"
            >
              All Products
            </span>
            <svg
              aria-hidden="true"
              class="lucide lucide-chevron-down h-4 w-4 opacity-50"
              fill="none"
              height="24"
              stroke="currentColor"
              stroke-linecap="round"
              stroke-linejoin="round"
              stroke-width="2"
              viewBox="0 0 24 24"
              width="24"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d="m6 9 6 6 6-6"
              />
            </svg>
          </button>
        </div>
      </div>
    </body>

I have been trying to fix this issue in the last hour but I haven’t been lucky enough yet. Below is what my test file looks like:

import { screen, render } from "@testing-library/react";
import ProductSortMenu from "../product-sort-menu";
import userEvent from "@testing-library/user-event";
import { useRouter } from "next/navigation";

jest.mock("next/navigation", () => {
  const replace = jest.fn();
  return {
    useSearchParams: () => new URLSearchParams(""),
    usePathname: jest.fn().mockReturnValue("/products"),
    useRouter: jest.fn().mockReturnValue({ replace }),
  };
});

describe("ProductSortMenu", () => {
  it("render the select menu component", () => {
    render(<ProductSortMenu />);
    const selectElement = screen.getByRole("combobox");
    expect(selectElement).toBeInTheDocument();
  });

  it("updates the url when the selected option changes", async () => {
    const user = userEvent.setup();
    const { replace } = useRouter();

    render(<ProductSortMenu />);

    const selectButtonElement = screen.getByRole("combobox");

    expect(selectButtonElement).toHaveTextContent(/all products/i);
    expect(replace).toHaveBeenCalledTimes(0);

    await user.click(selectButtonElement);

    const latestProductsOption = await screen.findByText(
      /latest products/i,
      {},
      { timeout: 3000 }
    );
    await user.click(latestProductsOption);

    expect(selectButtonElement).toHaveTextContent(/latest products/i);
    expect(replace).toHaveBeenCalledTimes(1);
    expect(replace).toHaveBeenCalledWith("/products?sort_by=created_at_desc");
  });
});

Please note: The error is thrown when I tried to access the latestProductOption variable. Also, this is what my actual component looks like:

"use client";

import React from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";

import CustomSelect from "../shared/custom-select";

type SelectValues =
  | "all products"
  | "oldest products"
  | "latest products"
  | "lowest price"
  | "highest price";

export default function ProductSortMenu() {
  const searchParams = useSearchParams();
  const pathname = usePathname();
  const { replace } = useRouter();

  const onValueChangeHandler = (value: SelectValues): void => {
    const params = new URLSearchParams(searchParams);

    if (value === "lowest price") {
      params.set("sort_by", "price_asc");
    } else if (value === "highest price") {
      params.set("sort_by", "price_desc");
    } else if (value === "oldest products") {
      params.set("sort_by", "created_at_asc");
    } else if (value === "latest products") {
      params.set("sort_by", "created_at_desc");
    } else {
      params.delete("sort_by");
    }

    replace(`${pathname}?${params.toString()}`);
  };

  return (
    <div className="min-w-40">
      <CustomSelect
        options={[
          "All Products",
          "Latest Products",
          "Oldest Products",
          "Lowest Price",
          "Highest Price",
        ]}
        defaultValue="all products"
        placeholder="Sort By"
        label="Sort products"
        onValueChange={onValueChangeHandler}
      />
    </div>
  );
}

Any pointer to why I am experiencing this error would be greatly appreciated.

Toggle visibillity of a list in Svelte

I am trying to do the most basic thing in Svelte but I keep failing. I want the button element to trigger the visibillity of the ul-element with classname “this-one”. I am stuck in the “vanilla js”-approach and can not wrap my head around how to achieve this the svelte-way. Any pointers?

        <ul>
            <li>
                <ul>
                    {#each item.children as child (child.id)}
                        <li>
                            <div>
                                <a href={child.route.path} on:click={() => dispatch('click')}>
                                    <span>{child.name}</span>
                                </a>
                                {#if child.children?.length > 0}
                                    <button>
                                        <i class="chevron-down"></i>
                                    </button>
                                {/if}
                            </div>
                            {#if child.children?.length > 0}
                                <ul class="this-one">
                                    {#each child.children as grandChild}
                                        <li>
                                            <a href={grandChild.route.path} on:click={() => dispatch('click')}>
                                                {grandChild.name}
                                            </a>
                                        </li>
                                    {/each}
                                </ul>
                            {/if}
                        </li>
                    {/each}
                </ul>
            </li>
        </ul>

How to Disable Auto-Scrolling to the Bottom in Microsoft Bot Framework Web Chat?

I’m using Microsoft Bot Framework Web Chat CDN for my chatbot. Whenever a new bot message appears, the chat automatically scrolls to the bottom, which hides earlier messages. I want to disable this auto-scrolling so that users can view new messages starting from the top of the chat window, rather than being taken to the bottom each time

What I tried:

I attempted to customize the Web Chat behavior using the scrollToEnd middleware and JavaScript to control scrolling, but these approaches either did not prevent auto-scrolling or affected the overall chat experience in ways I did not intend.

What I was expecting:

I expected to disable auto-scrolling so that new messages would appear at the top of the chat window, allowing users to see the content from the beginning of the conversation without being automatically scrolled to the bottom.

problem with cypress in fs-extra does not allow me to run tests

the present problem is that when I try to place a wait and a timeout to one of my tests it shows me this error

I hope that the test will run normally because with this error no test will work.

I suspect that this error is specifically related to the fs-extra library.

No commands were issued in this test.
TypeError
The following error originated from your test code, not from Cypress.

> Cannot read properties of undefined (reading 'native')

When Cypress detects uncaught errors originating from your test code it will automatically fail the current test.

Cypress could not associate this error to any specific test.

We dynamically generated a new test to display this failure.
node_modules/get-uri/node_modules/fs-extra/lib/fs/index.js:133:1
  131 |
  132 | // fs.realpath.native sometimes not available if fs is monkey-patched
> 133 | if (typeof fs.realpath.native === 'function') {
      | ^
  134 |   exports.realpath.native = u(fs.realpath.native)
  135 | } else {

136 | process.emitWarning(

How to change request header of websocket connection from chrome extension service worker

On invoking the below function, the console message “Token rule update with new token” is logged, but when actually invoking the web-socket connection request, the request header is not updated.

Has anyone faced this issue?
I got few articles that this is chrome bug, is this fixed yet?


function updateTokenRule(token) {
  const rule = {
    id: RULE_ID_TOKEN,
    priority: 1,
    action: {
      type: "modifyHeaders",
      requestHeaders: [
        {
          header: "token-header",
          operation: "set",
          value: token,
        },
      ],
    },
    condition: {
      urlFilter: "wss://localhost:19457/",
      // resourceTypes: ["websocket"],
    },
  };

  chrome.declarativeNetRequest.updateDynamicRules(
    {
      removeRuleIds: [RULE_ID_TOKEN],
      addRules: [rule],
    },
    () => {
      if (chrome.runtime.lastError) {
        console.error(chrome.runtime.lastError);
      } else {
        console.log("Token rule updated with new token:", token);
      }
    }
  );
}

Looking for a State timeline chart from javascript code

I am interested in finding some kind of JS code for making a State timeline chart in javascript.
I know that Grafan has a really nice solution for this, so its some thing smililar.
I have like an array of data that has different values depending on the state of the machine.

Does anyone know if there is something out there that i can use.

Thanks alot.

How to remove multiple values in array. tried for loop already

I was playing around with bind function mainly and i decided to test out in removing all items at once in the ItemsinCart array when click the checkout button. it works by using pop function but when i tried for loop it does not work. this is the portion of the function where i also changed to for loop.

please advise how can i achieve it.

    <h1>The Books</h1>
    <div id="bookstore"></div>
    <h1>Your Cart</h1>
    <div id="cart"></div>

  <script>
  const bookstore = {
    books: ["Java", "Python", "JavaScript"],
      removeBook(title) {
      let newList = this.books.filter((book) => book != title);
      this.books = newList;
      this.displayBookstore();
  },

  displayBookstore() {
    const renderTarget = document.getElementById("bookstore");
    const bookList = this.books.map((book) => `<p>${book}</p>`);
    renderTarget.innerHTML = bookList.join("");
    shoppingCart.displayCart(this.removeBook.bind(this));
   },
  };

  const shoppingCart = {
    itemsInCart: ["Python", "Java"],
    handleClick(remove) {           //this is the section where i tried for loop.
    remove(this.itemsInCart.pop());
 },

  displayCart(clickHandler) {
    const renderTarget = document.getElementById("cart");
    const itemsInCart = this.itemsInCart.map((item) => `<p>${item}</p>`);
    const checkoutButton = "<button id='checkout'>Check Out</button>";
  renderTarget.innerHTML = itemsInCart.join("") + checkoutButton;
  document
 .getElementById("checkout")
 .addEventListener("click", () => this.handleClick(clickHandler));
    },
  };
  bookstore.displayBookstore();
</script>

Issue with Local Audio Playback in Custom Video Call Demo

I’m working on a custom video call demo using Daily’s call object mode with SvelteKit. The audio from the shared video is correctly sent to other participants, but it doesn’t play locally. If I join the same room using the Daily.co website, the audio plays correctly.

Here are the relevant code excerpts:

VideStreamerTile.svelte:

let videoInput;
let localVideoStream;
let localAudioStream;

function playLocalVideoFile(evt) {
    let videoEl = document.getElementById('local-vid');
    let file = evt.target.files[0];
    if (!videoEl.canPlayType(file.type)) {
        toast('cannot play that file');
        return;
    }
    videoEl.src = URL.createObjectURL(file);
    videoEl.play().then(() => {
        localVideoStream = videoEl.captureStream();
        if (localVideoStream) {
            localVideoStream = new MediaStream(localVideoStream.getVideoTracks());
        }
        const audioTracks = videoEl.captureStream().getAudioTracks();
        if (audioTracks.length > 0) {
            localAudioStream = new MediaStream(audioTracks);
        }
    });
}

async function shareVideo() {
    if (localVideoStream) {
        const combinedStream = new MediaStream([
            ...localVideoStream.getVideoTracks(),
            ...(localAudioStream ? localAudioStream.getAudioTracks() : [])
        ]);
        await callObject.startScreenShare({
            mediaStream: combinedStream,
            videoSource: 'mediaStream',
            audioSource: localAudioStream ? 'mediaStream' : false
        });
    } else {
        toast('No video stream available to share.');
    }
}

VideoTile.svelte:

let videoTrackSet = false;
let videoSrc;
$: videoTrack = participant?.tracks?.video;
$: screenTrack = screen?.tracks?.screenVideo;
$: {
    if (!screen && videoTrack?.state === 'playable' && !videoTrackSet) {
        videoSrc = new MediaStream([videoTrack.persistentTrack]);
        videoTrackSet = true;
    } else if (screen && screenTrack?.state === 'playable' && !videoTrackSet) {
        videoSrc = new MediaStream([screenTrack.track]);
        videoTrackSet = true;
    }
}

let audioTrackSet = false;
let audioSrc;
$: audioTrack = participant?.tracks?.audio;
$: {
    if (audioTrack?.state === 'playable' && !audioTrackSet) {
        audioSrc = new MediaStream([audioTrack.persistentTrack]);
        audioTrackSet = true;
    }
}

function srcObject(node, stream) {
    node.srcObject = stream;
    return {
        update(newStream) {
            if (node.srcObject != newStream) {
                node.srcObject = newStream;
            }
        }
    };
}

Any insights or suggestions on how to resolve this issue would be greatly appreciated!

Thanks in advance!

Building and loading a React Component from a bundle is file

I have not seen any implementation of this but is it possible to build a React component into a bundle js file through Vite then load the component from the bundle js file into another application?
Such that the other application can pass props to the loaded component and also re-render when the content in the other application loads.

Problem with recursion interfering with a for loop iterating over a nested array in JavaScript

I have a function in my JavaScript code which iterates over an array called background_check which has elements which are also arrays. I want to see if any element in this nested array is a 0 (each element is either a 0 or a 1). I do this with a for loop iterating over the array and calling the function recursively if any element is an array.

Here is my code:

function loop_checker(background_check) {
    let loop_condition = false
    let count = background_check.length
    for (i=0;i<count;i++) {
        if (typeof background_check[i] === 'object') {
            let x = loop_checker(background_check[i])
            if (x === true) {
                loop_condition = true
            }
        }
        else if (background_check[i] === true) {
            loop_condition = true
        }
    }
    return loop_condition

The array in question is background_check = [1, 1, 1, 1, [1, 1, [1, 0]]]
The function iterates over the first four elements fine. It then gets to the i=4 case and iterates over the smaller array. When the new for loop gets to i=2 it iterates over the smallest list. Once it is all done it goes back down the call stack, but once it gets back to the first call of the function, i=4 has changed to i=3 and so it redoes the 5th element. This repeats infinitely; I cannot figure out why this has happened. I think it may be an issue with the scope of the i variable and recursion messing with that, but I am very new to JS.

P.S. sorry if this is poorly or incorrectly formatted, this is my first question. Thanks in advance!

Filtro de Tabela [closed]

Fala galera, tenho o código abaixo que possui funções para filtrar os dados, mas ao clicar no filtro há uma demora para abrir, as vezes abre rápido outras vezes não abre o poderia melhorar?

Interface da Aplicação: (https://i.sstatic.net/9VAOMWKN.png)

Realizar uma filtragem rápida , mas o filtro tem problemas ao ser clicado.

const { ipcRenderer } = require('electron');

document.addEventListener("DOMContentLoaded", () => {
    const fileInput = document.getElementById("fileInput");
    const processButton = document.getElementById("processButton");

    // Elementos do cabeçalho para exibir as informações do associado
    const accountName = document.getElementById("accountName");
    const accountNumber = document.getElementById("accountNumber");
    const accountAddress = document.getElementById("accountAddress");
    const bairroCep = document.getElementById("bairroCep");
    const cityState = document.getElementById("cityState");
    const saldoDisponivel = document.getElementById("saldoDisponivel");
    const limiteChequeEspecial = document.getElementById("limiteChequeEspecial");
    const limiteUtilizado = document.getElementById("limiteUtilizado");

    const progressContainer = document.querySelector('.progress-container');
    const progressText = document.getElementById('progressText');

    // Elementos dos filtros
    const dateFilter = document.getElementById('filterDateDropdown');
    const docFilter = document.getElementById('filterDocDropdown');
    const histFilter = document.getElementById('filterHistDropdown');
    const debFilter = document.getElementById('filterDebDropdown');
    const credFilter = document.getElementById('filterCredDropdown');
    const saldoFilter = document.getElementById('filterSaldoDropdown');

    let allTransactions = []; // Variável para armazenar todas as transações carregadas
    let filteredTransactions = []; // Transações filtradas
    let selectedFilters = {}; // Armazena os valores selecionados de cada filtro

    // Função para mostrar o anel de progresso
    function showProgress() {
        progressContainer.style.opacity = '1';
    }

    // Função para esconder o anel de progresso
    function hideProgress() {
        setTimeout(() => {
            progressContainer.style.opacity = '0';
        }, 1000);
    }

    // Função para atualizar o texto do progresso
    function updateProgress(percentage) {
        progressText.textContent = `${percentage}%`;
    }

    // Função para limpar as transações antes de exibir novas
    function clearTransactions() {
        const transactionsTableBody = document.getElementById('transactionsTableBody');
        transactionsTableBody.innerHTML = ''; // Limpa as linhas antigas
    }

    // Função para exibir as transações
    function displayTransactions(transactions) {
        clearTransactions(); // Limpa transações anteriores
        const transactionsTableBody = document.getElementById('transactionsTableBody');

        transactions.forEach((transaction) => {
            const row = document.createElement('tr');

            // Crie as células da tabela com base nos dados da transação
            const dateCell = document.createElement('td');
            dateCell.textContent = transaction.date;

            const docCell = document.createElement('td');
            docCell.textContent = transaction.document;

            const histCell = document.createElement('td');
            histCell.textContent = transaction.historico;

            const debCell = document.createElement('td');
            debCell.textContent = transaction.debito;

            const credCell = document.createElement('td');
            credCell.textContent = transaction.credito;

            const saldoCell = document.createElement('td');
            saldoCell.textContent = transaction.saldo;

            // Adiciona as células à linha
            row.appendChild(dateCell);
            row.appendChild(docCell);
            row.appendChild(histCell);
            row.appendChild(debCell);
            row.appendChild(credCell);
            row.appendChild(saldoCell);

            // Adiciona a linha ao corpo da tabela
            transactionsTableBody.appendChild(row);
        });
    }

    // Função para popular os filtros com os valores únicos das transações (usando checkboxes)
    function populateFilters(transactions) {
        const dates = new Set();
        const docs = new Set();
        const hists = new Set();
        const debs = new Set();
        const creds = new Set();
        const saldos = new Set();

        transactions.forEach(transaction => {
            if (transaction.date) dates.add(transaction.date);
            if (transaction.document) docs.add(transaction.document);
            if (transaction.historico) hists.add(transaction.historico);
            if (transaction.debito) debs.add(transaction.debito);
            if (transaction.credito) creds.add(transaction.credito);
            if (transaction.saldo) saldos.add(transaction.saldo);
        });

        function createFilterOptions(set, filterDropdown, filterName) {
            filterDropdown.innerHTML = ''; // Limpa o dropdown atual

            const filterContainer = document.createElement('div');
            filterContainer.classList.add('filter-container');

            const search = document.createElement('input');
            search.type = 'text';
            search.placeholder = 'Pesquisar...';
            search.classList.add('dropdown-search');
            search.onkeyup = function () {
                filterDropdown.querySelectorAll('label').forEach(label => {
                    const text = label.textContent.toLowerCase();
                    const searchValue = search.value.toLowerCase();
                    label.style.display = text.includes(searchValue) ? '' : 'none';
                });
            };
            filterContainer.appendChild(search);

            const optionList = document.createElement('div');
            optionList.classList.add('option-list');  // Contêiner para opções com rolagem interna
            set.forEach(item => {
                const label = document.createElement('label');
                const checkbox = document.createElement('input');
                checkbox.type = 'checkbox';
                checkbox.value = item;

                // Marca a caixa de seleção se o item estiver no filtro selecionado
                if (selectedFilters[filterName] && selectedFilters[filterName].includes(item)) {
                    checkbox.checked = true;
                }

                label.appendChild(checkbox);
                label.appendChild(document.createTextNode(item));
                optionList.appendChild(label);
            });
            filterContainer.appendChild(optionList);

            const clearButton = document.createElement('button');
            clearButton.textContent = 'Limpar Filtros';
            clearButton.classList.add('clear-filter-button');
            clearButton.onclick = function() {
                clearAllFilters(); // Limpa todos os filtros
            };
            filterContainer.appendChild(clearButton);

            filterDropdown.appendChild(filterContainer);

            // Adiciona os botões OK e Cancelar
            const actionsContainer = document.createElement('div');
            actionsContainer.classList.add('filter-actions');

            const applyButton = document.createElement('button');
            applyButton.textContent = 'OK';
            applyButton.classList.add('apply-filter-button');
            applyButton.onclick = function() {
                applyFilters(filterDropdown, filterName); // Aplica os filtros do dropdown específico
                closeAllDropdowns(); // Fecha o dropdown após aplicar os filtros
            };

            const cancelButton = document.createElement('button');
            cancelButton.textContent = 'Cancelar';
            cancelButton.classList.add('cancel-filter-button');
            cancelButton.onclick = function() {
                closeAllDropdowns(); // Fecha sem aplicar
            };

            actionsContainer.appendChild(applyButton);
            actionsContainer.appendChild(cancelButton);
            filterContainer.appendChild(actionsContainer);
        }

        createFilterOptions(dates, dateFilter, 'date');
        createFilterOptions(docs, docFilter, 'doc');
        createFilterOptions(hists, histFilter, 'hist');
        createFilterOptions(debs, debFilter, 'deb');
        createFilterOptions(creds, credFilter, 'cred');
        createFilterOptions(saldos, saldoFilter, 'saldo');
    }

    // Função para aplicar os filtros de checkboxes e garantir a interdependência entre colunas
    function applyFilters(filterDropdown, filterName) {
        const getCheckedValues = () => {
            const checkboxes = filterDropdown.querySelectorAll('input[type="checkbox"]');
            const checkedValues = [];
            checkboxes.forEach(checkbox => {
                if (checkbox.checked) {
                    checkedValues.push(checkbox.value);
                }
            });
            return checkedValues.length > 0 ? checkedValues : null;
        };

        selectedFilters[filterName] = getCheckedValues(); // Armazena os valores selecionados no filtro

        filteredTransactions = allTransactions.filter(transaction => {
            const dateMatch = !selectedFilters.date || selectedFilters.date.includes(transaction.date);
            const docMatch = !selectedFilters.doc || selectedFilters.doc.includes(transaction.document);
            const histMatch = !selectedFilters.hist || selectedFilters.hist.includes(transaction.historico);
            const debMatch = !selectedFilters.deb || selectedFilters.deb.includes(transaction.debito);
            const credMatch = !selectedFilters.cred || selectedFilters.cred.includes(transaction.credito);
            const saldoMatch = !selectedFilters.saldo || selectedFilters.saldo.includes(transaction.saldo);

            return dateMatch && docMatch && histMatch && debMatch && credMatch && saldoMatch;
        });

        displayTransactions(filteredTransactions);
    }

    // Função para limpar todos os filtros
    function clearAllFilters() {
        selectedFilters = {}; // Limpa todos os filtros
        filteredTransactions = [...allTransactions]; // Restaura todas as transações
        displayTransactions(filteredTransactions); // Exibe todas as transações
        populateFilters(filteredTransactions); // Restaura os filtros para os valores completos
    }

    // Processar o arquivo ao clicar no botão "Processar"
    processButton.addEventListener("click", () => {
        const file = fileInput.files[0];
        if (!file) {
            alert("Selecione um arquivo .prn.");
            return;
        }

        showProgress(); // Exibe o anel de progresso

        let progress = 0;
        const interval = setInterval(() => {
            if (progress < 100) {
                progress += 10;
                updateProgress(progress);
            } else {
                clearInterval(interval);
                hideProgress(); // Esconde o anel de progresso
            }
        }, 300);

        const reader = new FileReader();
        reader.onload = function (event) {
            const content = event.target.result;

            // Envia o conteúdo para o processo principal (main.js) para ser processado
            ipcRenderer.send('process-file', content);
        };
        reader.readAsText(file);
    });

    // Recebe a resposta do Python via o processo principal
    ipcRenderer.on('file-processed', (event, data) => {
        const headerInfo = data.header_info;
        const transactions = data.transactions;

        // Exibe o cabeçalho
        accountName.textContent = headerInfo.name;
        accountNumber.textContent = headerInfo.account_number;
        accountAddress.textContent = headerInfo.address;
        bairroCep.textContent = headerInfo.bairro_cep;
        cityState.textContent = headerInfo.city_state;
        saldoDisponivel.textContent = headerInfo.saldo_disponivel;
        limiteChequeEspecial.textContent = headerInfo.limite_cheque_especial;
        limiteUtilizado.textContent = headerInfo.limite_utilizado;

        // Exibe as transações e popula os filtros
        allTransactions = transactions;
        filteredTransactions = transactions; // Inicialmente todas as transações são exibidas
        displayTransactions(transactions);
        populateFilters(transactions);
    });
});

// Função para mostrar/ocultar o dropdown de filtro
function toggleDropdown(dropdownId) {
    const dropdown = document.getElementById(dropdownId);
    
    // Fecha todos os dropdowns abertos antes de abrir o selecionado
    closeAllDropdowns();

    // Verifica se o dropdown já está visível
    if (!dropdown.classList.contains("show")) {
        dropdown.classList.add("show");
    }
}

// Função para fechar todos os dropdowns abertos
function closeAllDropdowns() {
    const dropdowns = document.getElementsByClassName("dropdown-content");
    for (let i = 0; i < dropdowns.length; i++) {
        dropdowns[i].classList.remove("show");
    }
}

// Evitar que o dropdown feche ao clicar dentro dele
document.querySelectorAll('.dropdown-content').forEach(dropdown => {
    dropdown.addEventListener('click', function(event) {
        event.stopPropagation(); // Impede o fechamento do dropdown ao clicar em elementos internos
    });
});

// Fecha o dropdown se o usuário clicar fora dele
window.onclick = function(event) {
    if (!event.target.matches('.filter-btn') && !event.target.matches('.filter-btn img')) {
        closeAllDropdowns();
    }
}

show counter for nested loop using alphine js

I am trying to add the counter based on the provider. It should be global not service wise. I mean if there are 2 services and both of them have 2 providers, then it should show the counter like provider 1 and 2 for first service, then provider 3 and 4 for next service.

<template x-if="services !== undefined" x-for="(service,key) in services.services">
                    <template x-if="service !== undefined">
                        <tbody>
                            <tr>
                                <td>
                                    <h3 x-text="service.service_name"></h3>
                                    <div id="service-info" class="mt-3">
                                        <span class="text-left"
                                            x-html="'Booking No. '+service.booking_number "></span><br>
                                        <span class="text-left" x-html="'Start Time: '+ service.start_time "></span><br>
                                        <span class="text-left" x-html="'End Time: '+ service.end_time "></span><br>
                                        <span class="text-left"
                                            x-html="'Providers: '+ service.providers.length "></span><br>


                                    </div>

                                </td>
                                <td>

                                </td>
                                <td>

                                </td>
                            </tr>
                            <template x-for="(provider,index) in service.providers" :key="index">
                                <tr x-show="provider !== undefined && provider.check_in_status !== null">
                                    <td>

                                        <h3 x-show="details.hide_providers !== undefined && !details.hide_providers"
                                            x-text="provider.user.name"></h3>
                                        <h3 x-show="details.hide_providers !== undefined && details.hide_providers"
                                            x-text="'service-' + service.id + '-' + provider.id + 'Provider ' + providerCounter++"
                                            :id="'service-' + service.id + '-' + provider.id"></h3>

                                        <!-- Increment customCounter after each provider is rendered -->

                                        <div id="provider-info" class="mt-3">
                                            <span class="text-left"
                                                x-html="'Start Time: '+ new Date(provider.check_in_procedure_values.actual_start_timestamp).toLocaleDateString() "></span><br>
                                            <span class="text-left"
                                                x-html="'End Time: '+  new Date(provider.check_out_procedure_values.actual_end_timestamp).toLocaleDateString() "></span><br>
                                        </div>
                                    </td>
                                    <td>

                                        <div x-show=" 'total_durations_billing' in provider.billing_details ">
                                            <span
                                                x-show="!provider.service_payment_details.fixed_rate && provider.service_payment_details.day_rate "
                                                class="text-left"
                                                x-html="provider.billing_details.total_durations_billing.days == null ? '' : provider.billing_details.total_durations_billing.days+' Days '"></span>
                                            <span x-show="!provider.service_payment_details.fixed_rate"
                                                class="text-left"
                                                x-html="provider.billing_details.total_durations_billing.hours == null ? '' : provider.billing_details.total_durations_billing.hours+' Hours '"></span>
                                            <span
                                                x-show="!provider.service_payment_details.fixed_rate && !provider.service_payment_details.day_rate"
                                                class="text-left"
                                                x-html="provider.billing_details.total_durations_billing.mins == null ? '' : provider.billing_details.total_durations_billing.mins+' Mins '"></span>
                                        </div>

                                        <div x-show=" 'total_durations_billing' in provider.billing_details == false ">
                                            <span
                                                x-show="!provider.service_payment_details.fixed_rate && provider.service_payment_details.day_rate "
                                                class="text-left"
                                                x-html="service.days == null ? '' : service.days+' Days '"></span>
                                            <span x-show="!provider.service_payment_details.fixed_rate"
                                                class="text-left"
                                                x-html="service.hours == null ? '' : service.hours+' Hours '"></span>
                                            <span
                                                x-show="!provider.service_payment_details.fixed_rate && !provider.service_payment_details.day_rate"
                                                class="text-left"
                                                x-html="service.mins == null ? '' : service.mins+' Mins '"></span>
                                        </div>

                                    </td>
                                    <td>

                                        <template x-if="provider.billing_details !== undefined"
                                            x-for="(charge,charge_index) in provider.billing_details"
                                            :key="charge_index">
                                            <template x-if="charge_index == 'service_charges'">

                                                <div id="service-list" class="d-flex"
                                                    style="justify-content: space-between;">
                                                    <div class="text-left mb-2">
                                                        <p x-html="'Service Charges'"></p>
                                                    </div>
                                                    <div class="text-right mb-3">
                                                        <p x-html="charge"></p>
                                                    </div>
                                                </div>


                                            </template>
                                        </template>


                                        <template x-if="provider.billing_details !== undefined"
                                            x-for="(charge,charge_index) in provider.billing_details"
                                            :key="charge_index">
                                            <template
                                                x-if="charge_index == 'additional_charges' || charge_index == 'specialization_charges' || charge_index == 'expedited_charges'">
                                                <template x-for="(details,detail_index) in charge">
                                                    <div id="service-list" class="d-flex"
                                                        style="justify-content: space-between;">
                                                        <div class="text-left mb-2">
                                                            <p x-text="details.label"></p>
                                                        </div>
                                                        <div class="text-right mb-3">
                                                            <p x-text="details.charges"></p>
                                                        </div>
                                                    </div>
                                                </template>

                                            </template>
                                        </template>

                                        <template x-if="provider.service_payment_details.expedited_rate !== undefined">
                                            <div id="service-list" class="d-flex"
                                                style="justify-content: space-between;">
                                                <div class="text-left mb-2">
                                                    <p>Expedition Charges</p>
                                                </div>
                                                <div class="text-right mb-3">
                                                    <p x-html="provider.service_payment_details.expedited_rate"></p>
                                                </div>
                                            </div>
                                        </template>

                                        <template x-if="provider.billing_details !== undefined"
                                            x-for="(charge,charge_index) in provider.billing_details"
                                            :key="charge_index">
                                            <template x-if="charge_index == 'discounts_list'">
                                                <template x-for="(discounts,discount_index) in charge"
                                                    :key="discount_index">
                                                    <template x-if="discount_index !== undefined">
                                                        <div id="service-list" class="d-flex"
                                                            style="justify-content: space-between;">
                                                            <div class="text-left mb-2">
                                                                <p
                                                                    x-html="'<strong>Discount :</strong> ' + discounts.label">
                                                                </p>
                                                            </div>
                                                            <div class="text-right mb-3">
                                                                <p x-html="discounts.value"></p>
                                                            </div>
                                                        </div>
                                                    </template>
                                                </template>
                                            </template>
                                        </template>

                                        <div id="service-list" class="d-flex" style="justify-content: space-between;">
                                            <div class="text-left mb-2">
                                                <p><strong>Total Provider Charges</strong></p>
                                            </div>
                                            <div class="text-right mb-3">
                                                <p
                                                    x-html="services.currency + provider.billing_details.total_provider_charges">
                                                </p>
                                            </div>
                                        </div>

                                    </td>
                                </tr>

                                <!-- Increment the counter after rendering each service -->
                            </template>

I tried adding the counter but it was always returning the max count for every provider, meaning that if I had 10 providers it was showing provider 10 for each of them. Any ideas how I solve this?

How to convert web extension to safari extension using window’s

i am facing a issue i am a beginner and for me everything is right new but i love trying i am facing this issue which is this i have a window’s laptop and i am working on a extension what we want to do is to convert the extension to safari extension

the issue here is the as per the docs https://developer.apple.com/documentation/safariservices/converting-a-web-extension-for-safari

we need xcode basically i need macOS environment to run the code which is a issue for me
i want to know how can i do that create a code into my window laptop or what services i can use . Is there is a service for converting extension for other browser we have api we are able to convert the extension for other browser’s

what i need to know what i can do to convert our extension to safari extension any information might be help full for me

note : if there is any paid , free services which can help me please let me know

i tried to use creating a local macOS local environment using a virtual box but there are some compatibility issue and i am trying to find much better alternative’s

token from Google Auth not matching otplib authenticator.verify function

I’m trying to get 2fa to work on a backend project.

I have a function that created and store the secret:

import { Injectable } from '@nestjs/common';
import { authenticator } from 'otplib';
import { ClientService } from 'src/client/client.service';

@Injectable()
export class TwoFactorAuthService {
constructor(private readonly prisma: ClientService)

public async generateTwoFactorAuthSecret(loggedUserEmail: string) {
 const user = await this.prisma.users.findFirst({
   where: { email: loggedUserEmail },
 });
 const secret = authenticator.generateSecret();

 const otpAuthUrl = authenticator.keyuri(user.email, 'MyApp', secret);

 await this.prisma.users.update({
  where: {id: user.id},
  data: {2faSecret: secret}
 });

 return {otpAuthUrl}
}
}

Then with that otpAuthUrl I have the qrcode to read using google authenticator, but the thing is when I use the code provided by the auth APP it doesn’t match.

Here’s the function to verify the code:

async verifyTwoFaCode(code: string, user: Users) {
 //code = 6 number token from google auth APP
 return authenticator.verify({
   token: code,
   secret: user.2faSecret,
 });
}

The ‘authenticator.verify’ function returns false.

There’s a function that actually gives me the correct code:

const correctCode = authenticator.generate(user.2faSecret); //6 number code
 return authenticator.verify({
   token: correctCode,
   secret: user.2faSecret,
 });

If I do this it returns true, which tells me that the code provided by google auth APP is incorrect. How can I get this working properly??