RPC function in Javascript returns NULL, but works in Supabase SQL Query

I am hoping to create a RPC function to update contacts field (jsonb[]) in my users table and matching the user’s email.

[screenshot of users table](https://i.stack.imgur.com/QsEK6.png)

I have the below RPC function created in Supabase. I added extra logs to make sure data is correct.

CREATE OR REPLACE FUNCTION public.add_contact(p_email text, p_verified_contact jsonb) 
RETURNS jsonb[] AS $$ 
DECLARE
  updated_contacts jsonb[];
BEGIN
  RAISE LOG 'Input values: email=%, verified_contact=%', p_email, p_verified_contact;

  UPDATE users 
  SET contacts = CASE 
    WHEN contacts IS NULL THEN ARRAY[p_verified_contact] 
    ELSE contacts || p_verified_contact
  END 
  WHERE email = p_email 
  RETURNING contacts INTO updated_contacts;

  RAISE LOG 'The value of updated_contacts is: %', updated_contacts;

  RETURN updated_contacts;
END
$$ LANGUAGE PLPGSQL;

I ran the below SQL test cases in Supabase and both worked and returned the correct data.

SELECT * FROM add_contact('[email protected]', '{"user_id":2,"user_email":"[email protected]","display_name":"Tester1"}');
UPDATE users
SET contacts = CASE
    WHEN contacts IS NULL THEN ARRAY['{"user_id":4,"user_email":"[email protected]","display_name":"Tester4"}'::jsonb]
    ELSE contacts || '{"user_id":4,"user_email":"[email protected]","display_name":"Tester4"}'::jsonb
END
WHERE email = '[email protected]'
RETURNING contacts;

However, when I try to update the table while using the rpc function in my codes, it only returns NULL and does not update.

I originally had it passed as parameters, but thought I should just write the data in to avoid typo.

It returns NULL instead of updating contacts.

  const testAddContact = async () => {
    try {
      const { data: updatedData, error } = await supabase.rpc("add_contact", {
        p_email: "[email protected]",
        p_verified_contact:
          '{"user_id":5,"user_email":"[email protected]","display_name":"Tester5"}',
      });

      if (error) throw error;

      console.log(updatedData);
      return updatedData;
    } catch (error) {
      return console.error(error);
    }
  };

  testAddContact();

Could anyone shed some lights on why this is? I know the function itself works, and the logs show that the correct data is being passed as parameters in the RPC function in my codes.

Thanks a bunch!

Why is orbitControls not working on mobile device

I am running THREE.js and am attempting to build a 360 video viewer that works on mobile devices, however orbitControls isn’t working at all on my phone, it works perfectly fine when on PC

I’ve tried to research and find someone with this issue but no one seems to answer it. I’ve seen things that say that it should work but nothing I do works.

User login activity is not tracked in the form, which is in another file

How to make that the status of user login was saved on the main page, as the form is separate, in a separate file and a separate script, I tried to connect scripts on all files, but nothing worked, although the function Google Firebase OnAuthStateChanged should track this, what can be wrong?
Main page is index.html,reg_form.html, markup for form! Project build using Vite
I will leave the snippet and the link to the live GitHub page below.
When,after submit and successful login redirect to homepage,nothing happened
Its only worked when i paste form HTML markup in one file with header
Logic is here: When user is logged in – in header sign up button is hidden and user dropdown with email and logout button,when user is logged out,sign up button is in header,function which responsible for that is updateUI

const elements = {
  registrationForm: document.getElementById('registration_form'),
  loginForm: document.getElementById('login_form'),
  registrationName: document.getElementById('registration_name'),
  registrationEmail: document.getElementById('registration_email'),
  registrationPassword: document.getElementById('registration_password'),
  closeModalButton: document.getElementById('closeModalButton'),
  showRegistrationFormButton: document.getElementById(
    'showRegistrationFormButton'
  ),
  showLoginFormButton: document.getElementById('showLoginFormButton'),
  registerButton: document.getElementById('registerButton'),
  loginButton: document.getElementById('loginButton'),
  loginEmail: document.getElementById('login_email'),
  loginPassword: document.getElementById('login_password'),
  userDropdown: document.querySelector('.select-menu'),
  signUpButton: document.querySelector('.header-link-log-up'),
  mobileActiveAcc: document.querySelector('.mobile-active-acc'),
  userInfoContainer: document.getElementById('user_info'),
  userNameElement: document.querySelector('.user-name'),
  googleSignInButton: document.getElementById('googleSignInButton'),
  logoutButton: document.getElementById('logoutButton'),
  usernameDisplay: document.querySelector('.user-name'),
  toggleButtons: document.querySelectorAll('.toggle_buttons button'),
};
firebase.initializeApp(firebaseConfig);
const auth = firebase.auth();
const database = firebase.database();
function redirectToIndex() {
  location.href = 'index.html';
}
function toggleFormVisibility(showForm, hideForm, clickedButton) {
  elements[showForm].style.display = 'flex';
  elements[hideForm].style.display = 'none';
  elements.toggleButtons.forEach(button => {
    button.classList.remove('clicked');
  });

  elements[clickedButton].classList.add('clicked');
}

function showRegistrationForm() {
  toggleFormVisibility(
    'registrationForm',
    'loginForm',
    'showRegistrationFormButton'
  );
}

function showLoginForm() {
  toggleFormVisibility('loginForm', 'registrationForm', 'showLoginFormButton');
}

function saveUserDataToLocalStorage(userData) {
  localStorage.setItem('user_data', JSON.stringify(userData));
}

function getUserDataFromLocalStorage() {
  const userDataString = localStorage.getItem('user_data');
  return userDataString ? JSON.parse(userDataString) : null;
}

function closeModal() {
  elements.registrationForm.style.display = 'none';
  // redirectToIndex();
}

function register() {
  let name = elements.registrationName.value;
  let email = elements.registrationEmail.value;
  let password = elements.registrationPassword.value;

  if (
    !validate_field(name) ||
    !validate_email(email) ||
    !validate_password(password)
  ) {
    alert('Registration failed. Please check your inputs.');
    return;
  }

  auth
    .createUserWithEmailAndPassword(email, password)
    .then(userCredential => {
      let user = userCredential.user;
      let database_ref = database.ref();
      let user_data = {
        name: name,
        email: email,
        last_login: Date.now(),
      };
      database_ref.child('users/' + user.uid).set(user_data);
      saveUserDataToLocalStorage(user_data);
      alert('Registration successful!');
      console.log('Name:', name);
      console.log('Email:', email);
      clearRegistrationForm();
      redirectToIndex();
    })
    .catch(error => {
      alert(`Registration failed: ${error.message}`);
    });
}

function login() {
  let email = elements.loginEmail.value;
  let password = elements.loginPassword.value;

  if (!validate_email(email) || !validate_password(password)) {
    alert('Login failed. Please check your inputs.');
    return;
  }

  auth
    .signInWithEmailAndPassword(email, password)
    .then(userCredential => {
      let user = userCredential.user;
      let database_ref = database.ref();
      let user_data = {
        last_login: Date.now(),
      };
      database_ref.child('users/' + user.uid).update(user_data);
      saveUserDataToLocalStorage(user_data);
      alert('Login successful!');
      console.log('Email:', email);
      displayUserInfo(user);
      clearLoginForm();
      redirectToIndex();
    })
    .catch(error => {
      alert(`Login failed: ${error.message}`);
    });
}

function clearRegistrationForm() {
  elements.registrationName.value = '';
  elements.registrationEmail.value = '';
  elements.registrationPassword.value = '';
}

function clearLoginForm() {
  elements.loginEmail.value = '';
  elements.loginPassword.value = '';
}

function displayUserInfo(user) {
  if (elements.userInfoContainer && elements.userNameElement) {
    elements.userInfoContainer.textContent = `Welcome, ${
      user.displayName || user.email
    }!`;
    elements.userNameElement.textContent = user.displayName || user.email;
  } else {
    console.error(
      "Element with id 'user_info' or class 'user-name' not found."
    );
  }
}

function updateUI(user) {
  if (user) {
    elements.usernameDisplay.textContent = user.displayName || user.email;
    elements.userDropdown.classList.remove('is-hidden');
    elements.signUpButton.style.display = 'none';
    elements.mobileActiveAcc.classList.remove('is-hidden');
  } else {
    // localStorage.clear();
    // window.location.href = "../index.html";
  }
}

firebase.auth().onAuthStateChanged(updateUI);

https://bendelvolodymyr.github.io/Smart-Foxes-Bookshelf/

How to use fetch in a function?

<!DOCTYPE html>
<html>
    <style>
        #button {
            width: 250px;
            height: 50px;
            border: 1px solid black;
            text-align: center;
            vertical-align: middle;
            font-size: 32px;
        }
    </style>
    <body>
        <button id="button" type="button" onclick="OnClick()">Show Data</button>
    </body>
    <script>
        function OnClick() {
            fetch('./Data.json')
                .then(res => res.json)
                .then(data => {
                    console.log(data)
                })
        }
    </script>
</html>

I try to make a function that data will show out when I click the button, but the console respond” f json() { [native code] }. How to fix this error

People also ask with Js, css and html

I’m doing an people also ask section, just like the google one. But i’m having troubles with my js.

In my js code i’m using sibbilings and parents functions to create a new div. To temporaly replace one div. But the father div is losing the formatation “justify-content”. And i don’t know how to fix it.

`

HTML

<!DOCTYPE html>
<html lang="pt-br">
  <head>
    <meta charset="UTF-8" />

    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="assets/CSS/style.css" />
    <link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Roboto&display=swap"
      rel="stylesheet"
    />
<script src="script.js"></script>
    <title>Document</title>
  </head>
  <body>
    <!-- João Macedo (Contribuição vai das dúvidas até o footer)-->
    <!--Adicionei as imagens das redes sociais (provavelmente que fez o header tbm tem, mas com nome diferente lembra de mudar)-->

    <section class="secao-duvidas">
      <div class="duvidas">
        <div class="tire-duvidas">
          <h2>Tire suas dúvidas</h2>
        </div>

        <div class="duvidas-box">
          <div class="duvidas-etec">
            <p class="numero">1</p>

            <p class="texto-caixa-duvida">DUVIDA</p>
            <img src="assets/img/Group 2.png" class="seta-baixo" onclick="toggleResposta(this)">
            <div class="resposta">
              <p class="resp">Sou um texto muito convincente sobre esse assunto aqui que voce perguntou</p>
            </div>
          </div>

          <div class="duvidas-etec">
            <p class="numero">2</p>

            <p class="texto-caixa-duvida">DUVIDA</p>
            <img src="assets/img/Group 2.png" class="seta-baixo" />
          </div>

          <div class="duvidas-etec">
            <p class="numero">3</p>
            <p class="texto-caixa-duvida">DUVIDA</p>
            <img src="assets/img/Group 2.png" class="seta-baixo" />
          </div>

          <div class="duvidas-etec">
            <p class="numero">4</p>
            <p class="texto-caixa-duvida">DUVIDA</p>
            <img src="assets/img/Group 2.png" class="seta-baixo" />
          </div>

          <div class="duvidas-etec">
            <p class="numero">5</p>
            <p class="texto-caixa-duvida">DUVIDA</p>
            <img src="assets/img/Group 2.png" class="seta-baixo" />
          </div>

          <div class="duvidas-etec">
            <p class="numero">6</p>
            <p class="texto-caixa-duvida">DUVIDA</p>
            <img src="assets/img/Group 2.png" class="seta-baixo" />
          </div>

          <div class="duvidas-etec">
            <p class="numero">7</p>
            <p class="texto-caixa-duvida">DUVIDA</p>
            <img src="assets/img/Group 2.png" class="seta-baixo" />
          </div>
        </div>
      </div>
    </section>

    <footer>
      <div class="container-footer">
        <div class="redes-sociais">
          <img src="assets/img/linkedin-logo 1.png" class="linkedin" />
          <img src="assets/img/tiktok 1.png" class="tiktok" />
          <img src="assets/img/youtube (1) 1.png" class="youtube" />
          <img src="assets/img/twitter 1.png" class="twitter" />
          <img src="assets/img/instagram 1.png" class="instagram" />
          <img src="assets/img/facebook (1) 1.png" class="facebook" />
        </div>

        <img
          src="assets/img/cropped-BRASÃO-ESCOLA-OFICIAL-2-1 1.png"
          class="logo-etec"
        />
      </div>
    </footer>
  </body>
</html>`

`CSS




ul{
    list-style: none;
}

.secao-duvidas{
    width: 100%;
    display: flex;
justify-content: center;
}



.tire-duvidas{
    color: white;
    width: 50vw;
    height: 7vw;
    background-color: #B20000;
}




.duvidas-box{
    background-color: #FFFFFF;
border: #FFFFFF 1px solid;
border-radius: 5px;
height: 30vw;
filter: drop-shadow(grey 0.6px 0.6px 2px);
height: auto;
min-height: 30vw;
}

.tire-duvidas h2{
    font-size:49px;
    padding-top: 1.5vw;
    text-align: center;
}

.duvidas-etec{
background-color: #D9D9D9;
width: 44vw;
height:2.5vw;
margin-top: 1.5vw;
margin-left: 2.7vw;
display: flex;
align-items: center;
filter:drop-shadow(grey 0.6px 0.6px 1.5px);
justify-content: space-between;
}



.numero{   
padding-left: 0.5vw;
color: white;
font-size:20px;
font-family: 'Roboto',sans-serif;

}


.seta-baixo{
padding-top: 0.8vw;
padding-right: 1vw;
height: 0.7vw;
}

.duvidas-etec {
    overflow: hidden;
    transition: height 0.3s ease-out;
}

 .duvidas-etec.expandido {
    height: 150px; 
    transition: height 0.5s ease-in;
    justify-content: space-between;
}

.duvidas-etec .duvidas-etec.expandido{
    justify-content: space-between;
}
.resp{
    margin-top: 4em;
    margin-right: 15em;
width: 90%;
}
.resposta {

    max-height: 0;
    overflow: hidden;
    transition: max-height 0.3s ease-out;
}



.duvidas-etec.expandido .resposta {
    height: auto;
    max-height: 1000px; 
    transition:max-height  0.5s ease-in;
}
    .resposta {
        max-height: 0;
        overflow: hidden;
        transition: max-height 0.3s ease-out;
        margin-top: 0; 
    }

/* FINAL DA SESSÃO DE DUVIDAS */

/* COMEÇO DO FOOTER */

footer{
    width: 100%;
    margin-top: 5vw;
    border: gray 1px solid;
}

.container-footer{
    filter: drop-shadow(0px -17px 3px #000000);

display: flex;
justify-content: space-between;
}

.logo-etec{
    width: 18vw;
}

.redes-sociais{
    margin-left: 1vw;
}
.redes-sociais img{
margin-top: 2vw;
padding: 3px;
width: 2.5vw;
}

`JS

function toggleResposta(element) {
  var resposta = element.nextElementSibling;
  var duvidasEtec = element.closest('.duvidas-etec');

  if (resposta.style.display === 'block') {
    resposta.style.display = 'none';
    duvidasEtec.classList.remove('expandido');
  } else {
    resposta.style.display = 'block';
    duvidasEtec.classList.add('expandido');
  }
}
``

Can Someone help me with CryptoJS in PHP?, Im getting different output when converting the js to php code [duplicate]

Im trying to convert the javascript function below to php, but i still get different output the output of the function below is 9e1512ab6b96e83c5021bd06034bce13. Can anyone show me where I’m doing wrong with my code?

function doHash(){
    var c = "a228d4d86874eea2717200a15377933b312f46afeb994849ded695130d763391";
    var y = "I0E0msM0JY8W9dgL";
    var s = CryptoJS.PBKDF2(c, y, {
            keySize: 4,
            iterations: "200"
        });
    return s.toString();
}

//output: 9e1512ab6b96e83c5021bd06034bce13

i have try this code in php, but still different output

<?php

function doHash(){
    $c = "a228d4d86874eea2717200a15377933b312f46afeb994849ded695130d763391";
    $y = 'I0E0msM0JY8W9dgL';
    $s = hash_pbkdf2('sha256', $c, $y, 200, 16,true);

    return  bin2hex($s);
}
echo doHash();
//returns incorrect: f4895ad6fa875a4a25c9fd97875ba13a
?>

Ajv can’t resolve reference #/components/schemas/other from id test

I am getting the error when using the Ajv to load a schema that uses $ref imported from a yaml file.

This is my ymal file:

openapi: 3.0.3
info:
  title: Demo
  version: 1.0.0
paths:
  /carousel:
    get:
      responses:
        "200":
          description: Successful operation
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/test"
components:
  schemas:
    other:
      type: object
      properties:
        component:
          type: object
    test:
      type: object
      properties:
        slides:
          type: array
          items:
            type: object
            properties:
              contents:
                type: array
                items:
                  anyOf:
                    - $ref: "#/components/schemas/other"

This is my code (JS + Vite)

import Ajv from 'ajv'
import YamlContent from '../Config/API.yaml'; //vite.config.js with package @modyfi/vite-plugin-yaml
const validate =
  new Ajv({
    schemas: YamlContent.components.schemas
  }).
  getSchema(YamlContent.components.schemas.test);

I also tried:

const validate =
  new Ajv().
  addSchema(YamlContent.components.schemas.other).
  compile(YamlContent.components.schemas.test);

But it always gives the same error.
What am i missing here? Thanks.

how to store key value pairs when HTML form is submitted JS

I am trying to create dynamic search (I erased some of the code that was irrelevant ).
Creating new fields in form is working but I would like to have different output
right now I have 2 arrays with numerated array, but since I want to use this for creating SQL query I would like to store search-values array with key, value pairs where key would be selectedValue variable and value is input field value

<select id="add-new-field">
    <option value="media-title">Title</option>
    <option value="media-description">description</option>
    <option value="media-source">source</option>
    <option value="media-keywords">keywords</option>
    <option value="media-type">type</option>
    <option value="media-language">language</option>
    <option value="media-eurovoc">eurovoc</option>
    <option value="media-category">category</option>
</select>

<button type="button" onclick="addNewField()">Add field</button>

<form method="post" class="advanced-search-form" id="advanced-search-form">
    <label for="media-context">Sadržaj</label>
    <input type="text" name="media-context" id="media-context" />

    <br />
    <div id="additional-fields"></div>
    <br />
    <input type="submit" name="search" value="Pretraži" />
</form>

<?php
if (isset($_POST['search'])) {
    echo '<pre>' . var_dump($_POST) . '</pre>';
    // search logic
    $searchMediaContext = $_POST['media-context'];
    $searchMediaTitle = $_POST['media-title'];
    $searchMediaDescription = $_POST['media-description'];
    $searchMediaSource = $_POST['media-source'];
    $searchMediaKeywords = $_POST['media-keywords'];
    $searchMediaType = $_POST['media-type'];
    $searchMediaLanguage = $_POST['media-language'];
    $searchMediaEurovoc = $_POST['media-eurovoc'];
    $searchMediaCategory = $_POST['media-category'];


    // irrelevant 
    $advancedSearchQuery = "";


    foreach ($_POST['logical-operators'] as $key => $value) {
        echo 1;
    }


    if (!empty($results)) {
        echo 'RESULTS:';
        foreach ($results as $result) {
            $postTitle = $result->title;

            $postURL = $documentBaseURL . $result->post_name;

            echo '<br /><hr />';
            echo '<a href="' . $postURL . '">' . $postTitle . '</a>';
            
            echo '<br /><hr />';
        }
    } else {
        echo "No data";
    }
}
?>

<script type='text/javascript'>
    var additionalFieldsArray = [];

    function addNewField() {
        var form = document.getElementById('advanced-search-form');
        var select = document.getElementById('add-new-field');
        var selectedValue = select.value;
        var selectedInnerText = select.innerText;
        var additionalFieldsContainer = document.getElementById('additional-fields');

        if (selectedValue) {
            var newField = document.createElement('div');
            newField.className = 'additional-field';

            // Dropdown for Logical operatos
            var logicalOperatorDropdown = document.createElement('select');
            logicalOperatorDropdown.name = 'logical-operators[]';

            var optionAnd = document.createElement('option');
            optionAnd.value = 'AND';
            optionAnd.text = 'I';

            var optionOr = document.createElement('option');
            optionOr.value = 'OR';
            optionOr.text = 'ILI';

            logicalOperatorDropdown.add(optionAnd);
            logicalOperatorDropdown.add(optionOr);

            newField.appendChild(logicalOperatorDropdown);

            var label = document.createElement('span');
            // Create additional field
            if (selectedValue === 'media-category' || selectedValue === 'media-language' || selectedValue === 'media-type' || selectedValue === 'media-eurovoc') {
                // Create dropdown
                var dropdown = document.createElement('select');
                dropdown.name = 'search-values[]';

                // create option for category
                if (selectedValue === 'media-category') {
                    label.innerText = " Kategorija ";
                    <?php foreach ($acfMediaCategory['choices'] as $value => $label): ?>
                        var option = document.createElement('option');
                        option.value = "<?php echo $value ?>";
                        option.text = "<?php echo $label ?>";

                        dropdown.add(option);
                    <?php endforeach; ?>
                }

                // create options for language
                else if (selectedValue === 'media-language') {
                    label.innerText = " Jezik ";
                    <?php foreach ($acfMediaLanguage['choices'] as $value => $label): ?>
                        var option = document.createElement('option');
                        option.value = "<?php echo $value ?>";
                        option.text = "<?php echo $label ?>";

                        dropdown.add(option);
                    <?php endforeach; ?>
                }

                newField.appendChild(label);
                newField.appendChild(dropdown);
            } else {

                // Variables
                var displayText = '';
                if (selectedValue === 'media-title') {
                    displayText = ' title ';
                }
                else if (selectedValue === 'media-description') {
                    displayText = ' description ';
                }
                else if (selectedValue === 'media-source') {
                    displayText = ' source ';
                }
                else if (selectedValue === 'media-keywords') {
                    displayText = ' keywords ';
                }

                var input = document.createElement('input');
                input.type = 'text';
                input.name = 'search-values[]';
                input.placeholder = displayText;
                label.innerText = displayText;

                newField.appendChild(label);
                newField.appendChild(input);
            }

            additionalFieldsContainer.appendChild(newField);

            // Push new field to array
            additionalFieldsArray.push(newField);

            // Remove from dropdown
            select.remove(select.selectedIndex);
        }
    }

    window.additionalFieldsArray = additionalFieldsArray;
</script>

Cant render all the files in my project with vanilla javaScript and vite

I create a project using vite and vanilla javasciprt, a simple landing with a contact page.
I have my index.html and 2 more .html files, contact and home.

The problem is that when i compiled my project, in the folder dist, the contact.html and home.html are not incluided neither some other .js files that i have with some logic.

I was looking for the vite documentation but get nothing to work

Mapping array of objects and adding property based on calculation of values

I have array of objects

const tableRows = [
  {
    id: "dba3d111",
    name : "Budget 1",
    dropdownOptions: [
      {
        "id": "UUID1",
        "type": "Expenditure: Administration",
        "name": "Office Supplies 1",
        "chosen": true
      },
      {
        "id": "UUID2",
        "type": "Expenditure: Administration",
        "name": "Office Supplies 2",
        "chosen": false
      },
       {
        "id": "UUID3",
        "type": "Expenditure: Administration",
        "name": "Office Supplies 3",
        "chosen": false
      },
      {
        "id": "UUID4",
        "type": "Liability Group",
        "name": "Vehicles 1",
        "chosen": false
      },
      {
        "id": "UUID5",
        "type": "Liability Group",
        "name": "Vehicles 2",
        "chosen": false
      },
      {
        "id": "UUID6",
        "type": "Liability Group",
        "name": "Vehicles 3",
        "chosen": false
      },
      {
        "id": "UUID7",
        "type": "Liability Group",
        "name": "Vehicles 4",
        "chosen": false
      }
    ]
  },
  {
    id: "dba3d222",
    name : "Budget 2",
    dropdownOptions: [
      {
        "id": "UUID1",
        "type": "Expenditure: Administration",
        "name": "Office Supplies 1",
        "chosen": false
      },
      {
        "id": "UUID2",
        "type": "Expenditure: Administration",
        "name": "Office Supplies 2",
        "chosen": true
      },
       {
        "id": "UUID3",
        "type": "Expenditure: Administration",
        "name": "Office Supplies 3",
        "chosen": false
      },
      {
        "id": "UUID4",
        "type": "Liability Group",
        "name": "Vehicles 1",
        "chosen": false
      },
      {
        "id": "UUID5",
        "type": "Liability Group",
        "name": "Vehicles 2",
        "chosen": false
      },
      {
        "id": "UUID6",
        "type": "Liability Group",
        "name": "Vehicles 3",
        "chosen": false
      },
      {
        "id": "UUID7",
        "type": "Liability Group",
        "name": "Vehicles 4",
        "chosen": false
      }
    ]
  },
  {
    id: "dba3d333",
    name : "Budget 3",
    dropdownOptions: [
      {
        "id": "UUID1",
        "type": "Expenditure: Administration",
        "name": "Office Supplies 1",
        "chosen": false
      },
      {
        "id": "UUID2",
        "type": "Expenditure: Administration",
        "name": "Office Supplies 2",
        "chosen": false
      },
       {
        "id": "UUID3",
        "type": "Expenditure: Administration",
        "name": "Office Supplies 3",
        "chosen": true
      },
      {
        "id": "UUID4",
        "type": "Liability Group",
        "name": "Vehicles 1",
        "chosen": false
      },
      {
        "id": "UUID5",
        "type": "Liability Group",
        "name": "Vehicles 2",
        "chosen": false
      },
      {
        "id": "UUID6",
        "type": "Liability Group",
        "name": "Vehicles 3",
        "chosen": false
      },
      {
        "id": "UUID7",
        "type": "Liability Group",
        "name": "Vehicles 4",
        "chosen": false
      }
    ]
  }
];

I need to achieve the followings:
Count the amount of chosen options and if every option of each type has been chosen at least once I need to add property to option called disableType: true. For the rest of option types disableType: false needs to be added.

So the final output looks like:

const tableRows = [
      {
        id: "dba3d111",
        name : "Budget 1",
        dropdownOptions: [
          {
            "id": "UUID1",
            "type": "Expenditure: Administration",
            "name": "Office Supplies 1",
            "chosen": true,
            "disableType": true,
          },
          {
            "id": "UUID2",
            "type": "Expenditure: Administration",
            "name": "Office Supplies 2",
            "chosen": false,
            "disableType": true
          },
           {
            "id": "UUID3",
            "type": "Expenditure: Administration",
            "name": "Office Supplies 3",
            "chosen": false,
            "disableType": true
          },
          {
            "id": "UUID4",
            "type": "Liability Group",
            "name": "Vehicles 1",
            "chosen": false,
            "disableType": false
          },
          {
            "id": "UUID5",
            "type": "Liability Group",
            "name": "Vehicles 2",
            "chosen": false,
            "disableType": false
          },
          {
            "id": "UUID6",
            "type": "Liability Group",
            "name": "Vehicles 3",
            "chosen": false,
            "disableType": false
          },
          {
            "id": "UUID7",
            "type": "Liability Group",
            "name": "Vehicles 4",
            "chosen": false,
            "disableType": false
          }
        ]
      },
      {
        id: "dba3d222",
        name : "Budget 2",
        dropdownOptions: [
          {
            "id": "UUID1",
            "type": "Expenditure: Administration",
            "name": "Office Supplies 1",
            "chosen": false,
            "disableType": true,
          },
          {
            "id": "UUID2",
            "type": "Expenditure: Administration",
            "name": "Office Supplies 2",
            "chosen": true,
            "disableType": true,
          },
           {
            "id": "UUID3",
            "type": "Expenditure: Administration",
            "name": "Office Supplies 3",
            "chosen": false,
            "disableType": true,
          },
          {
            "id": "UUID4",
            "type": "Liability Group",
            "name": "Vehicles 1",
            "chosen": false,
            "disableType": false
          },
          {
            "id": "UUID5",
            "type": "Liability Group",
            "name": "Vehicles 2",
            "chosen": false,
            "disableType": false
          },
          {
            "id": "UUID6",
            "type": "Liability Group",
            "name": "Vehicles 3",
            "chosen": false,
            "disableType": false
          },
          {
            "id": "UUID7",
            "type": "Liability Group",
            "name": "Vehicles 4",
            "chosen": false,
            "disableType": false
          }
        ]
      },
      {
        id: "dba3d333",
        name : "Budget 3",
        dropdownOptions: [
          {
            "id": "UUID1",
            "type": "Expenditure: Administration",
            "name": "Office Supplies 1",
            "chosen": false,
            "disableType": true,
          },
          {
            "id": "UUID2",
            "type": "Expenditure: Administration",
            "name": "Office Supplies 2",
            "chosen": false,
            "disableType": true,
          },
           {
            "id": "UUID3",
            "type": "Expenditure: Administration",
            "name": "Office Supplies 3",
            "chosen": true,
            "disableType": true,
          },
          {
            "id": "UUID4",
            "type": "Liability Group",
            "name": "Vehicles 1",
            "chosen": false,
            "disableType": false
          },
          {
            "id": "UUID5",
            "type": "Liability Group",
            "name": "Vehicles 2",
            "chosen": false,
            "disableType": false
          },
          {
            "id": "UUID6",
            "type": "Liability Group",
            "name": "Vehicles 3",
            "chosen": false,
            "disableType": false
          },
          {
            "id": "UUID7",
            "type": "Liability Group",
            "name": "Vehicles 4",
            "chosen": false,
            "disableType": false
          }
        ]
      }
    ]

Please note that each option can only be selected once in each type and all more than one option of each type can be chosen in the same row, eg

[
      {
        "id": "UUID1",
        "type": "Expenditure: Administration",
        "name": "Office Supplies 1",
        "chosen": true
      },
      {
        "id": "UUID2",
        "type": "Expenditure: Administration",
        "name": "Office Supplies 2",
        "chosen": true
      },
       {
        "id": "UUID3",
        "type": "Expenditure: Administration",
        "name": "Office Supplies 3",
        "chosen": true
      },
]

So far I can calculate amount of options in each group with

const optionsCounter = {};
const selectedOptionsCounter = {};

tableRows[0].dropdownOptions.forEach((opt) => {
      optionsCounter[opt.type] = (optionsCounter[opt.type] || 0) + 1;
    });

tableRows.forEach((row) => {
      row.dropdownOptions.forEach((opt) => {
        if(opt.chosen === true) {
          selectedOptionsCounter[opt.type] = (selectedOptionsCounter[opt.type] || 0) + 1;
        }
        
      })
    })

Now I need to compare the values of optionsCounter and selectedOptionsCounter objects and assign disableType: true to the corresponding types if the object values are the and disableType: false for the other types.

At the moment the calculations are:

optionsCounter = {
 "Expenditure: Administration": 3,
  "Liability Group": 4
}

and

selectedOptionsCounter = {
 "Expenditure: Administration": 3
}

what makes sense, but I have no idea what to do next to solve my problem

How to programmatically add subgrid to Gridstack?

I’m working with Gridstack.js and I’ve hit a snag. I’ve been through the documentation and even checked their nested grid demo, but I couldn’t find specific guidance on how to programmatically add a subgrid to an existing main Gridstack instance.

I’m looking for a way to dynamically add a subgrid as a direct child of my main Gridstack instance. Ideally, the subgrid should be able to set its own gridstack options independently of the main grid.

Of course, none of these methods exist, but In my mind it should be something like this:

const subGridOptions = {...};
const subGrid = mainGridstackInstance.createGrid(subGridOptions);
mainGridstackInstance.createGrid.addGrid(subGrid);
// at some point later...
subGrid.addWidget(...);

Has anyone encountered a similar challenge or can provide some insights on how to accomplish this?

NextAuth.js and Capacitor – Session Token Cookie not deleting on signOut

I am using NextAuth.js in my Next.js app to authenticate users via Google Provider and magic link. This works perfectly in a browser but not so well in a native wrapper using Capacitor JS – https://capacitorjs.com/.

To provide some context, here is my setup for authentication. I’m using MongoDB to store user accounts:

// [...nextauth.js]

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import EmailProvider from "next-auth/providers/email";
import { MongoDBAdapter } from "@next-auth/mongodb-adapter";

import clientPromise from "@/lib/mongodb";
import { AuthVerification } from "@/emails/sign-in";

const THIRTY_DAYS = 30 * 24 * 60 * 60;
const THIRTY_MINUTES = 30 * 60;

const adapterOptions = {
  databaseName: "accounts",
};

export const authOptions = (req) => ({
  pages: {
    verifyRequest: "/auth/verify-request",
  },
  secret: process.env.NEXTAUTH_SECRET,
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
    EmailProvider({
      sendVerificationRequest({ identifier, url, provider }) {
        AuthVerification({ identifier, url, provider });
      },
    }),
  ],
  callbacks: {
    async session({ session, user }) {
      if (session?.user) {
        session.user.id = user.id;
        session.user.filters = user.filters;
        session.user.admin = user.admin;
        session.user.stripeCustomerId = user.stripeCustomerId;
        session.user.plus = user.plus;
      }
      return session;
    },
  },
  adapter: MongoDBAdapter(clientPromise, adapterOptions),
});

export default async function auth(req, res) {
  return await NextAuth(req, res, authOptions(req), {
    debug: true,
  });
}

I am triggering the sign in functions via:

// Google Provider

signIn('google', {
  callbackUrl: process.env.NEXT_PUBLIC_BASE_URL,
});

// Email Provider

signIn("email", {
  redirect: false,
  callbackUrl: process.env.NEXT_PUBLIC_BASE_URL,
  email,
})

The issue stems from the fact that in Capacitor, the Google Provider opens in a new browser window so the session-token cookie required for authentication wasn’t passed back to the app.

I decided to setup deep linking and tried to make it work through the Capacitor Browser plugin (https://capacitorjs.com/docs/apis/browser) using this custom signIn function:

async function signIn() {
  const { url } = await fetch(
    `${
      process.env.NEXT_PUBLIC_BASE_URL
    }/api/auth/signin/google`,
    {
      method: "POST",
      headers: {
        "Content-Type": "application/x-www-form-urlencoded",
      },
      body: new URLSearchParams({
        csrfToken: await getCsrfToken(),
        json: "true",
        callbackUrl: process.env.NEXT_PUBLIC_BASE_URL,
      }),
      redirect: "follow",
      credentials: "include",
    },
  );

  await Browser.open({ url: url });
}

This generated the correct auth URL and opened in the in-app browser, however once authenticated it just loaded the callbackUrl inside the in-app browser rather than deep-linking back to the app.

After much trial and error, I gave up and ended up focusing solely on the email provider route.

The email generated a URL that successfully deep linked into the app so I caught the link containing the email and token using the Capacitor listener appUrlOpen and hit the api/auth/callback/email route manually:

async function authUser(token, email) {
  try {
    const response = await fetch(
      `${
        process.env.NEXT_PUBLIC_BASE_URL
      }/api/auth/callback/email?token=${token}&email=${email}`,
      {
        method: "POST",
        headers: {
          "Content-Type": "application/x-www-form-urlencoded",
        },
        body: new URLSearchParams({
          csrfToken: await getCsrfToken(),
          json: "true",
        }),
        credentials: "include",
      },
    );

    if (response.ok) {
      router.reload();
    }
  } catch (error) {
    console.error("Error during authentication:", error);
  }
}

useEffect(() => {
  App.addListener("appUrlOpen", (event: URLOpenListenerEvent) => {
    const authUrl = new URL(event.url);
    const params = new URLSearchParams(authUrl.search);
    const tokenRegex = /token=([^&]+)/;
    const match = tokenRegex.exec(event.url);

    if (match && match[1]) {
      const token = match[1];

      authUser(token, params.get("email"));
    }
  });

  return () => {
    App.removeAllListeners();
  };
}, [router]);

This worked great – On success it reloaded the router and the user is authenticated. It also remained authenticated when I exited and reloaded the app so all seemed to be working perfect.

If I inspect the cookies, I can see that the three cookies are all present as they should be:

__Host-next-auth.csrf-token
__Secure-next-auth.callback-url
__Secure-next-auth.session-token

However, the issue comes when I logout. The session-token cookie disappears as it should but then if I try to log back in it just reloads the router but remains unauthenticated.

If I reload the app then the session-token cookie sometimes appears again but I still can’t login. If I manually delete the cookies and try again then it works so I’m thinking that the session-token cookie isn’t getting properly removed on signOut maybe?

For reference, I am obtaining the session info on the client-side using the following way:

import { useSession } from "next-auth/react";

const { data: session } = useSession();

This issue doesn’t happen in Xcode simulator by the way, only when testing on a connected iOS device or when distributed via TestFlight.

Any help or guidance would be greatly appreciated – I’ve been banging my head against the wall for days on this!

Thanks.

Allows multiple questions to be opened on FAQ Accordion

I want to modify my code where it allows me to open multiple questions at once, because in this code it only opens one. when I opened another question, the previous one closes.

import React, { useState } from "react";
import "./faq.css";

function Faq() {
  const [selected, setSelected] = useState(null);

  const toggle = (id) => {
    if (selected === id) {
      return setSelected(null);
    }

    setSelected(id);
  };



  return (
    <div className="container">
      <h2>Frequently Asked Questions</h2>
      <div className="questions">
        {Questions.map((question) => (
          <div key={question.id}>
            <h4>
              {question.title}
              <button onClick={() => toggle(question.id)}>
                {selected === question.id ? "X" : "+"}
              </button>
            </h4>
            <p
              className={selected === question.id ? "question-info" : "hidden"}
            >
              {question.info}
            </p>
          </div>
        ))}
      </div>
    </div>
  );
}

export default Faq;

Here is my code and I can’t solve this problem

Why is DataTable state not saved despite manually updating search and using table.state.save() in an external function on input change?

The issue arises when attempting to manually update the search value for the ‘event_name’ column in a DataTable. In the DataTable initialization (initComplete), an input element is dynamically created for each column with the class ‘search’, allowing users to filter data. Additionally, an external function is triggered on the change event of an external input (#event_name). This function aims to update the DataTable’s search value for the ‘event_name’ column, save the state, and redraw the DataTable.

However, despite the manual efforts to update the search value and save the state, the DataTable state is not reflecting the changes made to the search value for the ‘event_name’ column. Further investigation is needed to understand why the DataTable state is not being saved as expected.

Here is the code for better context:

Initialization in DataTable:

initComplete: function () {
    let api = this.api();
    api.columns(".search").every(function () {
        let column = this;
        let columnName = api.settings().init().columns[column.index()].name;

        let title = column.header().textContent;
        let input = document.createElement('input');
        input.classList.add("form-control");
        input.classList.add("form-control-sm");
        input.classList.add(columnName);
        input.placeholder = title;

        table.context[0].json.input.columns.forEach(function (column) {
            if(column.name == columnName){
                input.value = column.search.value;
                $('#' + column.name).val(column.search.value);
            }
        });

        column.footer().replaceChildren(input);

        input.addEventListener('keyup', () => {
            if (column.search() !== this.value) {
                column.search(input.value).draw();
            }
        });
    });
}

***External Function:***

$('#event_name').change(function () {
    $('.event_name').val(this.value);

    let eventColumn = table.column('event_name:name');
    eventColumn.search(this.value).draw();

    table.state.save();
    table.draw();
});

I attempted to manually update the search value for the 'event_name' column in a DataTable using the jQuery change event on the #event_name input field. I expected that, after updating the search value, calling table.state.save() and redrawing the DataTable with table.draw() would save the state, including the modified search value. However, the state was not saved as expected, and the DataTable did not reflect the changes made to the search value.