SnapAR CameraKit lens not loaded error in React

import React, { useEffect, useRef, useState } from "react"
import { bootstrapCameraKit } from "@snap/camera-kit"

const CameraKitComponent = () => {
  const canvasRef = useRef(null) // Reference to the canvas element
  const [lensId, setLensId] = useState("43293650876") // State to manage the current lens ID
  const [session, setSession] = useState(null) // State to manage the camera session
  const [cameraKit, setCameraKit] = useState(null)
  const [lensConfig, setLensConfig] = useState({
    lensId: "43293650876",
    lensGroupId: "913dffcc-07d8-4f94-901f-297260c6c4a6",
  })

  useEffect(() => {
    const initializeCameraKit = async () => {
      const apiToken =
        "API TOKEN" // Place your actual API token here
      const cameraKit = await bootstrapCameraKit({ apiToken })
      setCameraKit(cameraKit)
      const liveRenderTarget = canvasRef.current

      if (!liveRenderTarget) {
        return
      }

      const newSession = await cameraKit.createSession({ liveRenderTarget })

      const mediaStream = await navigator.mediaDevices.getUserMedia({
        video: true,
      })

      await newSession.setSource(mediaStream)
      await newSession.play()

      setSession(newSession) // Save the session to state
    }

    initializeCameraKit()

    return () => {
      session && session.stop && session.stop()
    }
  }, [])

  const applyLens = async (lensId, lensGroupId) => {
    if (!cameraKit || !session || !lensId || !lensGroupId) {
      return
    }

    try {
      const lens = await cameraKit.lensRepository.loadLens(lensId, lensGroupId)
      console.log("lens", lens)
      await session.applyLens(lens)
    } catch (error) {
      console.error("Error loading or applying lens:", error)
    }
  }

  useEffect(() => {
    if (lensConfig.lensId && lensConfig.lensGroupId) {
      applyLens(lensConfig.lensId, lensConfig.lensGroupId) // Update to pass lens group ID as well
    }
  }, [lensConfig, session, cameraKit])

  return (
    <div>
      <canvas ref={canvasRef}></canvas>
      <div>
        <button
          onClick={() =>
            setLensConfig({
              lensId: "43293650876",
              lensGroupId: "913dffcc-07d8-4f94-901f-297260c6c4a6",
            })
          }
        >
          Load Lens 1
        </button>
        <button
          onClick={() =>
            setLensConfig({
              lensId: "50502080875",
              lensGroupId: "913dffcc-07d8-4f94-901f-297260c6c4a6",
            })
          }
        >
          Load Lens 2
        </button>
        <button
          onClick={() =>
            setLensConfig({
              lensId: "50507980875",
              lensGroupId: "913dffcc-07d8-4f94-901f-297260c6c4a6",
            })
          }
        >
          Load Lens 3
        </button>
      </div>
    </div>
  )
}

export default CameraKitComponent

The above is the code of my component. What I am trying to do it loading different lenses in runtime based on the button clicked. It gives me the following error once in a while. Error loading or applying lens: Error: Cannot apply lens 43293650876. It has not been loaded by the Lens repository. Use CameraKit.lensRepository.loadLens (or loadLensGroups) to load lens metadata before calling CameraKitSession.applyLens. I did try different methods including making a generic CameraKit componenet which is loaded based on given props (id and lens group id) Is there a way to make this work

How parse a Certificate Signing Request (CSR) file with Javascript to get the subject?

I read a Certificate Signing Request in X.509 format. I have tried to parse it with the class X509Certificate from NodeJS ‘crypto’ library.
The input format is a string

-----BEGIN CERTIFICATE REQUEST-----
MII....
-----END CERTIFICATE REQUEST-----

I want to get the subject from this certificate signing request (CN, emailAddress, C,…)

I tried the following code

const { X509Certificate } = await import('crypto');
const x509CSR = new X509Certificate(pem)

But the the following error is displayed:

error:0909006C:PEM routines:get_name:no start line

So I have tried another library called ‘node-forge’.

const { pki } = await require('node-forge');
x509CSR = pki.certificationRequestFromPem(pem);

But the the following error is displayed:

Cannot read public key. OID is not RSA.

How to get the subject data from the certificate signing request in a NodeJS server?

How to write the right backend for proxying fetch requests on the service https://timeweb.cloud /?

I use fetch queries on https://api.openai.com on React to use ChatGPT, I have a working backend code and it is hosted on the server https://vercel.com / and it works correctly. But it turned out that there are restrictions on vercel servers and if the response should come from a relatively large chat, then it falls into an error and nothing comes, that is, short answers from the gpt chat come correctly, and large ones fall into an error.

I decided to post it on another service https://timeweb.cloud /, but the code with vercel does not work on this service, and it did not work out to register it correctly myself. In fact, you need a very simple code on the backend so that when you request a fetch on its domain/api, it redirects the request from its foreign ip address to https://api.openai.com/v1/chat/completions and the answer was passed back. Who can help?

Svelte switch/case vs. if logic blocks

I was wondering whether there are any best practises on when to use a switch/case assignment vs. using if / else if logic blocks in svelte?

E.g. if I have an enum

export enum Status {
  riding = "RIDING",
  arrived = "ARRIVED",
  failed = "FAILED",
}

Is it better to use a switch case in the script tag

<script>
  let statusText = ""

  $: switch (status) {
    case Status.riding:
      statusText = "Riding"
      break
    case Status.arrived:
      statusText = "Arrived"
      break
    case Status.failed:
      statusText = "Failed"
      break
  }
</script>

<span class="status-text">
  {statusText}
</span>

…or to use if / else if logic blocks:

<span class="status-text">
  {#if status === Status.riding}
    "Riding"
  {:else if status === Status.arrived}
    "Arrived"
  {:else if status === Status.failed}
    "Failed"
  {/if}
</span>

Any recommendation on what to use? Thanks.

Need help removing image with X button using javascript

I am trying to create some javascript that will allow users to remove an image they uploaded. I have been trying different suggestions and methods and none have seemed to work. Here is my code.

    <!DOCTYPE html>
    <html>
      <head>
        <title>Creating NFT</title>

        <style>
          body {
          background-color: seashell;
          color: palegoldenrod;
          font-family: Georgia, "Times New Roman", Times, serif;
          overflow-x: hidden;
          height: 100vh;
          width: 100%;
        }

        .TopNav {
          overflow: hidden;
          background-color: seashell;
         }

        .TopNav .Horizons {
          position: relative;
          font-size: 60px;
          font-family: Georgia, "Times New Roman", Times, serif;
          float: left;
          top: 0px;
          font-weight: bold;
          color: palegoldenrod;
        }

         .Horizons:hover {
          color: grey;
        }

        .MintingIntro {
          position: relative;
          right: -100px;
          font-size: 25px;
          text-align: center;
        }

        .NftCard {
          border: 4px solid palegoldenrod;
          height: 600px;
          width: 500px;
          position: relative;
          bottom: 40px;
          background-color: snow;
          object-fit: contain;
        }

        .NftUpload {
          height: 100%;
          width: 100%;
          object-fit: cover;
        }

        .Upload {
          text-align: center;
          position: relative;
          top: 50%;
          left: 32%;
          display: none;
        }

        .Upload:hover {
          color: grey;
         cursor: pointer;
       }

       .upload-file {
         text-align: center;
         position: relative;
         top: 5;
         left: 33%;
         display: block;
         border: 1px solid palegoldenrod;
         width: 140px;
         height: 30px;
         font-family: Georgia, "Times New Roman", Times, serif;
         font-weight: bold;
         font-size: 22px;
         border-radius: 10px;
         background-color: black;
         margin: 5px;
         padding: 6px;
         cursor: pointer;
        }

        .upload-file:hover {
          color: grey;
        }

        .Customization {
          border: 2px solid floralwhite;
           position: relative;
           float: right;
          height: 520px;
         width: 850px;
         top: -540px;
         text-align: center;
         }

       .NameCustom {
          padding-bottom: 80px;
         }

       .NameIn {
         font-size: 19px;
        }

     .NameInput {
        background-color: snow;
        color: black;
        border-radius: 0%;
        font-size: 17px;
        position: relative;
        bottom: -30px;
        left: -190px;
        border: 1px solid black;
      }

     .SupplyIn {
       font-size: 19px;
      }

      .SupplyInput {
        background-color: snow;
        color: black;
        border-radius: 0%;
        font-size: 17px;
        bottom: -30px;
        position: relative;
        left: -140px;
        border: 1px solid black;
      }

      .SupplyCustom {
        padding-bottom: 80px;
       }

      .DescriptionIn {
        font-size: 19px;
        position: relative;
        top: -90px;
        z-index: 5;
        right: -195px;
      }

      .DescriptionInput {
       background-color: snow;
        color: black;
        border-radius: 0%;
        font-size: 17px;
        bottom: -30px;
        left: -158px;
       position: relative;
         width: 600px;
        height: 200px;
         border: 1px solid black;
       }

      .TraitsIn {
        font-size: 19px;
        position: relative;
        top: 20px;
        z-index: 5;
        right: -195px;
      }

      .close {
        z-index: 5;
       font-size: 30px;
       color: black;
        background-color: transparent;
        border: 1px solid snow;
        position: absolute;
        right: 1px;
        top: 1px;
        cursor: pointer;
       }

        .close:hover {
          color: grey;
      }
  </style>

   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  </head>

  <body>
   <div class="TopNav">
     <a
      href="http://127.0.0.1:5500/styles/HorizonsMarketplaceFrontEnd2.html"
       target="_self"
       class="Horizons"
       >H</a
     >
     <div class="ProfilePort">
       <i class="fa-solid fa-circle-user fa-2xl" style="color: #f1e598"></i>
     </div>
     <div class="profilePort"></div>
   </div>
   <div>
   <h1 class="MintingIntro">Create NFT</h1>
  </div>

  
   //This is where my button and images are placed. Basically allowing a user to upload an image and then delete it when they want to with the X button in the corner.

  <div class="NftCard">
    <button class="close" onclick="closeButton">&times;</button>
    <img src="images/nft.png" id="Nft-Image" class="NftUpload" />
     <label class="upload-file" for="input-file">Update File</label>
     <input
      class="Upload"
      type="file"
      accept="img/jpeg, img/png, img/jpg"
      id="input-file"
    />
  


 </div>
  <div class="Customization">
   <div class="NameCustom">
     <label for="Name" class="NameIn">Name of your NFT</label>
     <input type="text" class="NameInput" name="Name" />
   </div>
   <div class="SupplyCustom">
     <label for="Supply" class="SupplyIn">Supply</label>
     <input type="text" class="SupplyInput" supply="Supply" />
    </div>
    <div class="DescriptionCustom">
      <label for="Description" class="DescriptionIn">Description</label>
      <input type="text" class="DescriptionInput" description="Description" />
    </div>
    <div class="Traits">
      <label for="Traits" class="TraitsIn">NFT Traits</label>
      <i class="fa-solid fa-plus fa-2xl"></i>
    </div>
  </div>
  <script>
    let nftImage = document.getElementById("Nft-Image");
    let inputFile = document.getElementById("input-file");

    inputFile.onchange = function () {
      nftImage.src = URL.createObjectURL(inputFile.files[0]);
    };
  
  
  
    </script>
  </body>
 </html>

I am new to Java so I am trying to figure this all out. I believe I have to add an event listener with the event method click?

I have tried multiple fucntions, addEventListeners, etc and nothing has worked. I am new to javascript so I am probably messing up syntax and such.

using javascript to pass my current post to ajax

I have alpine.js making a api call from Ajax.

In my Ajax I need to have the post type update depending on the current post type, I can set this is a php file.

I would like to get the current post type with wp php

    $post_type = get_post_type( $post->ID );
    $postType = get_queried_object();
 $args = array(
                    'post_type'      => $post_type,
                    'post_status'    => 'publish',
                );

then pass into my javascript and have that post type known with the ajax so no matter what post type i have the ajax and js know.

**js
**

 import Alpine from 'alpinejs'


Alpine.data("filterPosts", (adminURL) => ({
    posts: "",
    limit: 10,
    category: null,
    showDefault: true,
    showFiltered: false,

    filterPosts(id) {
        this.showDefault = false;
        this.showFiltered = true;
        this.category = id;
        this.fetchPosts();
    },

    fetchPosts() {
        var formData = new FormData();
        formData.append("action", "filterPosts");
        formData.append("offset", this.offset);
        formData.append("limit", this.limit);

        if (this.category) {
            formData.append("category", this.category);
        }

        fetch(adminURL, {
            method: "POST",
            body: formData,
        })
        .then((res) => res.json())
        .then((res) => {
            this.posts = res.posts;
        });
    }
}));
 
window.Alpine = Alpine
Alpine.start()

Ajax

?php
// the ajax function
add_action('wp_ajax_filterPosts', 'filterPosts');
add_action('wp_ajax_nopriv_filterPosts', 'filterPosts');

function filterPosts()
{
    $response = [
        'posts' => "",
    ];

    $filter_args = array(
        'post_status' => 'publish',
        'post_type' => 'post'
    );

    if ($_POST['limit']) {
        $filter_args['posts_per_page'] = $_POST['limit'];
    }

    if ($_POST['category']) {
        $filter_args['cat'] = $_POST['category'];
    }

    $filter_query = new WP_Query($filter_args);

    if ($filter_query->have_posts()) :
        while ($filter_query->have_posts()) : $filter_query->the_post();
            $response['posts'] .= load_template_part('/template-parts/posts-filter/single-post');
        endwhile;
        wp_reset_postdata();
    endif;

    echo json_encode($response);
    die();

Does TinyMCE have any function to convert HTML to Plain text?

Does TinyMCE have any function to convert HTML to Plain text?
I want to convert HTML to plain text outside the rich text editor, Like the function in Draft JS (htmlToDraft(html)), I’m working on react JS, I don’t want to use a package just for converting HTML to Plain text, if there’s any fucnction provided by TinyMCE itself, it would be useful. If anyone knows any idea or any workaround for this, Please help me. I’m a bit inexperienced. 🙂

Multiple same Splide Sliders in multiple different pages (Splide.js)

I have a splide slider that i placed it in two pages, one in home page and one in about page. these are completely same and properly working, but when i add it in third page its totally ruined while others still working properly. here is my code:

HTML:

<div class="splide my-5 d-flex align-content-center" id="clients">
            <div class="splide__track">
              <ul class="splide__list">
                <li class="splide__slide pt-3 pb-5 me-3">
                  <a class="qcard clientcard">
                    <img alt="compony" src="/assets/logo/png.monster-19.png" />
                  </a>
                </li>
                <li class="splide__slide pt-3 pb-5 me-3">
                  <a class="qcard clientcard">
                    <img alt="compony" src="/assets/logo/pngimg.com - google_PNG19644.png" />
                  </a>
                </li>
                <li class="splide__slide pt-3 pb-5 me-3">
                  <a class="qcard clientcard">
                    <img alt="compony" src="/assets/logo/SpaceX_logo_black.svg.png" />
                  </a>
                </li>
                <li class="splide__slide pt-3 pb-5 me-3">
                  <a class="qcard clientcard">
                    <img alt="compony" src="/assets/logo/Star_Wars_Ahsoka_Logo.png" />
                  </a>
                </li>
                <li class="splide__slide pt-3 pb-5 me-3">
                  <a class="qcard clientcard">
                    <img alt="compony" src="/assets/logo/ubisoft-2-logo-png-transparent.png" />
                  </a>
                </li>
              </ul>
            </div>
          </div>

JS:

$(document).ready(function () {
  new Splide("#clients", {
    type: "loop",
    perPage: 3,
    perMove: 1,
    rewind: false,
    focus: "center",
    pagination: false,
    autoWidth: true,
    breakpoints: {
      530: {
        perPage: 1,
      },
      975: {
        perPage: 2,
      },
      1200: {
        perPage: 3,
      },
    },
  }).mount();
});

I want use it just like two other pages with no any changes.

Background image is not set properly in ios devices

Recently I have been contributing to a website, However I am getting an issue in IOS mobile devices wrt to bg Images The images a kind of zoomed when i see it on IOS mobile device , and it is perfect for desktop, Android, And responsive console in Developer Tools (Inspect ). I need help to make it properly set the Images are give below

Devloper console image

(https://i.stack.imgur.com/3x8KJ.png)

The below Image Is on actual mobile device which is the issue i am facing with bg images.
IOS Mobile phone bg image which is the issue

This next image is desktop view:
Destop image
below is the code we are using for that image:

.bg-pronight {
    width: 100vw;
    height: 200vh;
    background-image: url('../assets/pronighthpimg.png');
    background-repeat: no-repeat;
    background-attachment: fixed;
    background-size: cover;
    -webkit-background-size: cover;
    -moz-background-size: cover;
    -o-background-size: cover;
}
.headingpronight{
    font-size: 8vw;
    margin: 0px;
}
#aboutus{
    padding: 0.5% 0%;
    /* margin-top: 3.5rem; */
}
@media only screen and (max-width:500px){
    .headingpronight{
        font-size: 14vw;
    }
    .bg-pronight{
        background-position: center;
        background-size: auto;
    }
}

I did tried some Stackover flow question and anser but didn’t worked out. I need help to fix that image zoom on IOS mobile device Thank you.

Javascript to remove text before point

I have a Google spreadsheet that receives new rows of data when a website form is submitted. I need to find the latest entry and remove a piece of text from that row in column N.

I have the js to find the last row of data and remove a piece of text, but it’s removing everything AFTER “Postcode: ” and I need it to remove everything BEFORE “Postcode: “, leaving everything after Postcode.

function removeTags(){
  var column = 14
  var sh = SpreadsheetApp.getActive().getActiveSheet();
  var data = sh.getRange(1,column,sh.getLastRow(),1).getValues();
  for(var n in data){
    if(data[n][0]==''){continue};// if cell empty then continue
    var str = data[n][0].toString();
    data[n][0]= str.substring(0,str.indexOf("Postcode: "));//remove everything after Postcode: 
  }
  sh.getRange(1,column,sh.getLastRow(),1).setValues(data);// update sheet with new data
}

Can someone look at the script and tell me how to swap what is deleted?

I tried to remove some str. and other tweaks to try and fix it, but I’m new to js and don’t fully understand the order of operations

Too many calls to Location or History APIs within a short timeframe

Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn’t have a dependency array, or one of the dependencies changes on every render.

 const OwnerPage = () => {
  const onOpen = useAgencyModal((state) => state.onOpen);
  const isOpen = useAgencyModal((state) => state.isOpen);
  const params = useParams();
  const [owner, setOwner] = useState<User | null>(null);
  const [agency, setAgency] = useState<any>([]);
  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(`/api/owner/${params.ownerId}`, {
          method: "GET",
        });
        const data = await response.json();
        setAgency(data.agency);
        setOwner(data.owner);
        console.log("agency: " + data.agency);
        console.log("owner: " + data.owner);
      } catch (error) {
        console.error("Error occurred while fetching data:", error);
      }
    };
    fetchData();
  }, [params.ownerId]);
  const memoizedData = useMemo(() => {
    return { agency, owner };
  }, [agency, owner]);
  useEffect(() => {
    if (memoizedData.agency !== null) {
      if (!isOpen) {
        onOpen();
      }
    } else {
      redirect(`/dashboard/owner/${params.ownerId}/dashboard`);
    }
  }, [memoizedData.agency, isOpen, onOpen, params.ownerId]);
  return null;
};

useRef to focus specific input element when there are multiple input elements

I’m using React, the parent component prints out child components, each time creating multiple inputs inside the parent. When you update the input, I’m updating some state, the child is re-rendering loses the focus on the input.

Is there a way to keep the focus after re-rendering?

I try using the id of the input element, not working:

    const ref: any = useRef();
            
            useEffect(() => {
                    if (ref.current) {
                        ref.current[fixture.id].focus();
                    }
                }, []);
            
return (
            <input
               id={fixture.id}
               ref={ref}
               type="number"
               value={home}
               onChange={handleChangeHome}
               min={0}
               max={10}
            />
    
    )

Remember there are multiple input elements so when I just do:

useEffect(() => {
        if (ref.current) {
            ref.current.focus();
        }
    }, []);

The focus defaults to the last input field after component is rendered

WebRTC “Perfect Renegotiation” collision problem

I have a chat application which I want to extend with voice and video chat capabilities. It is basically done but I can’t seem to fix a problem with renegotiation.

I used the Mozilla docs about Perfect Negotiation and it still gets into soft-locks which I can’t seem to fix.

The Problem:

  • User A sends a renegotiation Offer
  • User B receives Offer
  • User B sends Answer
  • User B sends Renegotiation Offer immediately after the Answer
  • User A receives Answer and starts processing…
  • User A receives Offer and drops it because Answer is still processing
  • User A Finishes processing Answer
  • User B is stuck in waiting for Answer

Code that handles SDP-s:

public async createSDP() {
  await this.rtcPeerConnection.setLocalDescription();
  const sdp = this.rtcPeerConnection.localDescription;
  if (!sdp) return false;
  return sdp;
}

public async readSDP(sdp: any) {
  let ignoreOffer = false;
  try {
    const offerCollision =
      sdp.type === "offer" &&
      (this.makingOffer || this.rtcPeerConnection.signalingState !== "stable");
    ignoreOffer = !this.polite && offerCollision;
    if (ignoreOffer) {
      return false;
    }
    await this.rtcPeerConnection.setRemoteDescription(sdp);
    return true;
  } catch (err) {
    console.error(err);
  }
  return false;
}

I tried adding an iteration if ignoreOffer is true, to try 3 times with gradually encreasing delays to process the offer that would be dropped. I expected it to read it properly and send out an Answer, but got an error of “invalid order of m-lines”

I tried adding a timeout after sending out a renegotiation offer to check if after ~20 seconds signalingState is still in “have-local-offer”, got the same “invalid order of m-lines” error.

WordPress: Custom block array caching on system

I’ve built a custom block that takes values from apiFetch and maps them into an array of attributes. The problem is I think the custom block has cached on the system so the attribute array isn’t updating with the values. Here is the code.
Edit.js is where I am mapping the values and the function adds to the array

const { backgroundColor, textColor, selectedValue, myNumber, menuItems  } = attributes;
...

    function upadateMenuItems (src, heading, para, href, index){
        const updateMenuItems = [...menuItems];
        updateMenuItems[index] = { src: src, heading: heading, para: para, href: href };
        setAttributes({ menuItems: updateMenuItems });
        console.log(menuItems); 
    }

...

        <p class="blog-stamp-parent">
                {newArray.filter((m, idx) => idx < myNumber && m.catagory == selectedValue).map((m) => {

                    return (
                        <div class="blog-stamp">
                            <img src={m.image} width="200" height="200" />
                            <RichText
                                tagName="h3"
                                class="stamp-title"
                                onChange={(heading) => upadateMenuItems(src, heading, para, href)}
                                value={m.label}
                            />
                            <RichText
                                tagName="p"
                                class="stamp-excerpt"
                                onChange={(para) => upadateMenuItems(src, heading, para, href)}
                                value={m.content}
                            />
                            <p class="button-parent">
                                <a
                                    href={m.link}
                                    className="sfs-button"
                                    style={{ backgroundColor: backgroundColor, color: textColor }}
                                >
                                    {'Read more'}
                                </a>
                            </p>
                        </div>)
                })}
            </p>

block.json converts the values into an array of attributes

    "attributes": {
        "content": {
          "type": "string",
          "source": "html",
          "selector": "h2"
        },
        "menuItems":{
            "type":"array",
            "source": "query",
            "selector": "div",
            "query" : {
                "src": {
                    "type":"string",
                    "source": "attribute",
                    "selector": "img",
                    "attribute": "src"
                },
                "heading": {
                    "type": "string"
                },
                "para": {
                    "type": "string",
                    "selector": "p",
                    "source" : "text"
                },
                "href": {
                    "type":"string",
                    "source": "attribute",
                    "selector": "a",
                    "attribute": "href"
                }
            }
        }
      },

Save.js is where it takes the menuItems array and renders it on the website

import { useBlockProps } from '@wordpress/block-editor';

export default function save({attributes}) {
    const { backgroundColor, textColor, selectedValue, myNumber, menuItems  } = attributes;
    const mapFile =  menuItems.map(({src, heading, para, href}) => { 
        return (
        <div class="blog-stamp"><img src={src} width="200" height="200" /><h3 class="stamp-title">{heading}</h3><p class="stamp-excerpt">{para}</p><p class="button-parent"><a href={href} class="sfs-button" style={{ backgroundColor: backgroundColor, color: textColor }}>Read more</a></p></div>) });
    return (
        <div{ ...useBlockProps.save() }>
        <p class="blog-stamp-parent">{mapFile}</p>
        </div>
    );
}

It was working but now isn’t so think its cached. I’ve been trying to solve it for the last 2 days but can’t figure it out so any help will be appriciated.

Thanks in advance.

How can i separate the add to cart button from the single product page link

when i click on the add to cart button the single product page shows up.what can i do to seperate the add to cart button from the product page link when i click on add to cart button the product should be added to cart and the product page should not be displayed.

My Product Render Code


const renderProducts = () => {
  products.forEach((product) => {
    let newProduct = document.createElement("a");
    newProduct.href = `product.html?id=${product.id}`;
    newProduct.classList.add("product");
    let inCart = cart.find((x) => x.id === product.id);
    let disabled = inCart ? "disabled" : "";
    let text = inCart ? "In Cart" : "Add To Cart";
    newProduct.innerHTML = `
      <div class="product">
      <img
        src="${product.image}"
        alt=""
      />
      <h3>${product.title}</h3>
      <p>${product.category}</p>
      <h5>$${product.price}</h5>
      <button ${disabled} data-id="${product.id}">${text}</button>
    </div>
      `;
    selectors.products.appendChild(newProduct);
  });
};
My add to cart Code


const renderCart = () => {
  let cartQty = cart.reduce((sum, item) => {
    return sum + item.qty;
  }, 0);
  selectors.cartQty.innerHTML = cartQty;
  selectors.cartQty.classList.toggle("visible", cartQty);
  selectors.cartTotal.textContent = calculateTotal();
  if (cart.length === 0) {
    selectors.cartBody.innerHTML =
      '<div class="empty-cart">Your Cart is Empty</div>';
    return;
  }
  selectors.cartBody.innerHTML = cart.map(({ id, qty }) => {
    let product = products.find((x) => x.id === id);
    let { image, title, price } = product;
    let amount = price * qty;
    return `
    <div class="cart-item" data-id="${id}">
    <img src="${image}" alt="" />
   <div class="cart-item-detail">
      <h3>${title}</h3>
      <h5>$${price}</h5>
      <div class="cart-item-amount">
        <i class="bi bi-dash-lg" data-btn="decr"></i>
        <span class="qty">${qty}</span>
        <i class="bi bi-plus-lg" data-btn="incr"></i>
        <span class="cart-item-price">${amount}</span>
   </div>
    </div>
  </div>
        `;
  });
};

My Single Product Page Link Code

let products = [];
fetch("https://fakestoreapi.com/products")
  .then((res) => res.json())
  .then((data) => {
    products = data;
    showProducts();
  });
function showProducts() {
  let productId = new URLSearchParams(window.location.search).get("id");
  let thisProduct = products.filter((value) => {
    return value.id == productId;
  })[0];
  if (!productId) {
    window.location.href = "index.html";
  }
  document.querySelector(".singleImage").src = thisProduct.image;
  document.querySelector(".singleName").textContent = thisProduct.title;
  document.querySelector(".dingleDet").textContent = thisProduct.description;
  document.querySelector(".singlePrice").textContent = thisProduct.price;
  document.querySelector(".singleCat").textContent = thisProduct.category;
}

please help me to solve the problem my product page is showing oki with details but clicking the cart button navigates me to single product page.