React App crashes without invoking WebSocket.close() function in useEffect cleanup function

I’m building a test app that connects to a server via WebSocket. The app crashes when I don’t use the WebSocket.close() function in the cleanup part of the useEffect hook. The app works fine when WebSocket.close() is included, ofc I know I should close connection/clean after using it but error references the some kind of ?object? (Error is listed below). Why does this happen?

I’ve tried to understand the issue, and I wonder if it’s related to something that happens in the background. From what I know, cleanup happens after the render, so it shouldn’t directly impact the rendering or state before the component renders. Is my understanding correct, or am I missing something?

Uncaught runtime errors: ERROR An attempt was made to use an object that is not, or is no longer, usable ./src/CommunicationComponent.jsx/WebSocketComponent/
</socketRef.current.onopen@http://localhost:3000/static/js/bundle.js:191:27 EventHandlerNonNull*./src/CommunicationComponent.jsx/WebSocketComponent/<@http://localhost:3000/static/js/bundle.js:189:7 commitHookEffectListMount@http://localhost:3000/static/js/bundle.js:26115:30
  commitPassiveMountOnFiber@http://localhost:3000/static/js/bundle.js:27608:42 commitPassiveMountEffects_complete@http://localhost:3000/static/js/bundle.js:27580:38 commitPassiveMountEffects_begin@http://localhost:3000/static/js/bundle.js:27570:45 commitPassiveMountEffects@http://localhost:3000/static/js/bundle.js:27560:38
  flushPassiveEffectsImpl@http://localhost:3000/static/js/bundle.js:29443:32 flushPassiveEffects@http://localhost:3000/static/js/bundle.js:29396:18 ./node_modules/react-dom/cjs/react-dom.development.js/commitRootImpl/<@http://localhost:3000/static/js/bundle.js:29211:13
  workLoop@http://localhost:3000/static/js/bundle.js:36363:46 flushWork@http://localhost:3000/static/js/bundle.js:36341:18 performWorkUntilDeadline@http://localhost:3000/static/js/bundle.js:36578:25 EventHandlerNonNull*./node_modules/scheduler/cjs/scheduler.development.js/<@http://localhost:3000/static/js/bundle.js:36614:7
  ./node_modules/scheduler/cjs/scheduler.development.js@http://localhost:3000/static/js/bundle.js:36664:5 options.factory@http://localhost:3000/static/js/bundle.js:41901:31 __webpack_require__@http://localhost:3000/static/js/bundle.js:41331:32 fn@http://localhost:3000/static/js/bundle.js:41560:21
  ./node_modules/scheduler/index.js@http://localhost:3000/static/js/bundle.js:36679:20 options.factory@http://localhost:3000/static/js/bundle.js:41901:31 __webpack_require__@http://localhost:3000/static/js/bundle.js:41331:32 fn@http://localhost:3000/static/js/bundle.js:41560:21
  ./node_modules/react-dom/cjs/react-dom.development.js/<@http://localhost:3000/static/js/bundle.js:6083:40 ./node_modules/react-dom/cjs/react-dom.development.js@http://localhost:3000/static/js/bundle.js:31842:5 options.factory@http://localhost:3000/static/js/bundle.js:41901:31
  __webpack_require__@http://localhost:3000/static/js/bundle.js:41331:32 fn@http://localhost:3000/static/js/bundle.js:41560:21 ./node_modules/react-dom/index.js@http://localhost:3000/static/js/bundle.js:31913:20 options.factory@http://localhost:3000/static/js/bundle.js:41901:31
  __webpack_require__@http://localhost:3000/static/js/bundle.js:41331:32 fn@http://localhost:3000/static/js/bundle.js:41560:21 ./node_modules/react-dom/client.js@http://localhost:3000/static/js/bundle.js:31856:28 options.factory@http://localhost:3000/static/js/bundle.js:41901:31
  __webpack_require__@http://localhost:3000/static/js/bundle.js:41331:32 fn@http://localhost:3000/static/js/bundle.js:41560:21 ./src/index.js@http://localhost:3000/static/js/bundle.js:298:93 options.factory@http://localhost:3000/static/js/bundle.js:41901:31
  __webpack_require__@http://localhost:3000/static/js/bundle.js:41331:32 @http://localhost:3000/static/js/bundle.js:42544:56 @http://localhost:3000/static/js/bundle.js:42546:12

Working Client Code:

import React, { useEffect, useRef } from 'react';

export default function WebSocketComponent({ children }) {
  const socketRef = useRef(null);
  const isEstablish = useRef(false);  // Tracks if the WebSocket connection is established
  const inputRef = useRef(null);  // Ref for the input field

  useEffect(() => {
    if(!isEstablish.current)
    {
        socketRef.current = new WebSocket('ws://localhost:8080');

        socketRef.current.onopen = () => {
        console.log('WebSocket connection established.');
        socketRef.current.send('Hello Server!');
        isEstablish.current = true;  // Mark connection as established
        };

        socketRef.current.onmessage = (event) => {
        console.log('Message from server:', event.data);
        };

        socketRef.current.onerror = (error) => {
        console.error('WebSocket error:', error);
        };

        socketRef.current.onclose = () => {
        console.log('WebSocket connection closed.');
        isEstablish.current = false;  // Mark connection as closed
        };
    }
    // Cleanup function 
    return () => {
      if (socketRef.current) {
        socketRef.current.close(); //it must be here, return a error in other case
        isEstablish.current = false;
      }
    };
  }, []); 

  // Function to send data to the server
  const sendToServer = () => {

    if (isEstablish.current && socketRef.current.readyState === WebSocket.OPEN) {
      const message = inputRef.current.value;
      console.log('Sent to server:', message);
      socketRef.current.send(message);  // Send message only if connection is open
    } else {
      console.error('WebSocket is not open. Cannot send message.');
    }
  };

  return (
    <div>
      <h1>WebSocket Example</h1>
      <input ref={inputRef} />
      <button onClick={sendToServer}>Send</button>
    </div>
  );
}

Zoom webhooks validation in Node

im working on a webhook to take in finished webinar recordings.
I have followed the steps mentioned on the Zoom Docs and i have even copied the code directly from their example for Node. I can verify that my code is correctly hashing the token and is setting the correct information in the JSON object. Once again this is taken directly from the example from Zoom Docs. However when i try to verify, it doesnt work. Yes the site is live, with https, and the ENV variables are live as well. If anyone can help, i can provide the endpoint for you to test as well.

router.post("/verify", async (req, res) => {
  console.log("Video Completed");
  try {
    console.log(req.body.payload.plainToken);

    const token = crypto
      .createHmac("sha256", process.env.ZOOM_APP_TOKEN)
      .update(req.body.payload.plainToken)
      .digest("hex");

    console.log(token);

    res.status(200);
    res.json({
      plainToken: req.body.payload.plainToken,
      encryptedToken: token,
    });
  } catch (err) {
    console.log(err);
    res.status(500).send({ message: "Internal Server Error" });
  }
});

REACT, horizontal scrolling without width

I am attempting to create a navigation bar (navbar) for my website, but I am encountering an issue that I cannot seem to resolve. Specifically, I don’t understand why a scrollbar is appearing on the page when I have not explicitly set any width or height properties. I’ve checked the elements, and none of the tags involved have extra width or height applied to them, yet the scrollbar is still showing up. What’s even more puzzling is that the scrollbar disappears once I hover over the dropdown menu in the navbar. I am unsure of the cause of this behavior and would appreciate any insight or guidance on how to fix this. The example I used: https://codepen.io/riogrande/pen/nwmaNZ

const NavBarWithDrop = () => {
  const currentPath = window.location.pathname;
  const firstRouter = '/' + currentPath.split("/")[1];

  const navLinks = [
    { url: '/about', name: 'About' },
    { url: '/price-list', name: 'Price List' },
    { url: '/sales', name: 'Sales' },
    { url: '/reviews', name: 'Reviews' },
    { url: '/blog', name: 'Blog' },
    { url: '/contacts', name: 'Contacts' }
  ];

  const services = [
    {
      id: 1,
      name: 'Service 1',
      url: 'service-1',
      brands: [
        { id: 1, name: 'Brand 1', models: ['Model 1', 'Model 2'] },
        { id: 2, name: 'Brand 2', models: ['Model 3', 'Model 4'] }
      ]
    },
    {
      id: 2,
      name: 'Service 2',
      url: 'service-2',
      brands: [
        { id: 3, name: 'Brand 3', models: ['Model 5', 'Model 6'] }
      ]
    }
  ];

  return (
    <div className='flex justify-between items-center bg-shark-100 w-full border-b-2 border-shark-200 px-20'>
      <h1>Logo</h1>
      <nav className='text-base' id='cssmenu'>
        <ul className='!flex !items-center z-50 h-28'>
          {navLinks.map(link => (
            <li key={link.url} className={firstRouter === link.url ? 'active' : ''}>
              <a href={link.url}>{link.name}</a>
            </li>
          ))}

          <li className={`has-sub ${firstRouter.startsWith('/services') ? 'active' : ''}`}>
            <a href="/services">Services</a>
            <ul>
              {services.map(service => (
                <li key={service.id} className='has-sub'>
                  <a href={`/services/${service.url}`}>{service.name}</a>
                  <ul>
                    {service.brands.map(brand => (
                      <li key={brand.id} className='has-sub'>
                        <a href={`/services/${service.url}/${brand.id}`}>{brand.name}</a>
                        <ul>
                          {brand.models.map((model, idx) => (
                            <li key={idx}>
                              <a href={`/services/${service.url}/${brand.id}/${idx}`}>{model}</a>
                            </li>
                          ))}
                        </ul>
                      </li>
                    ))}
                  </ul>
                </li>
              ))}
            </ul>
          </li>
        </ul>
      </nav>

      <button onClick={() => alert('something')} className="w-40">Sign Up</button>
    </div>
  )
}

ReactDOM.createRoot(
    document.getElementById("root")
).render(
    <NavBarWithDrop />
);
*{
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

#cssmenu {
  padding: 0;
  margin: 0;
  border: 0;
  width: 100%
}

#cssmenu ul,
#cssmenu li {
  list-style: none;
  margin: 0;
  padding: 0;
}

#cssmenu ul {
  position: relative;
  z-index: 597;
}

#cssmenu ul li {
  float: left;
  min-height: 1px;
  vertical-align: middle;
}

#cssmenu ul li.hover,
#cssmenu ul li:hover {
  position: relative;
  z-index: 599;
  cursor: default;
}

#cssmenu ul ul {
  visibility: hidden;
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 598;
  width: 100%;
}

#cssmenu ul ul li {
  float: none;
}

#cssmenu ul ul ul {
  top: 0;
  left: auto;
  right: -99.5%;
}

#cssmenu ul li:hover > ul {
  visibility: visible;
}

#cssmenu ul ul {
  bottom: 0;
  left: 0;
}

#cssmenu ul ul {
  margin-top: 0;
}

#cssmenu ul ul li {
  font-weight: normal;
}

#cssmenu a {
  display: block;
  line-height: 1em;
  text-decoration: none;
}

#cssmenu > ul {
  display: inline-block;
}

#cssmenu:after,
#cssmenu ul:after {
  content: "";
  display: block;
  clear: both;
}

#cssmenu a {
  background: transparent;
  color: #333;
  padding: 0 20px;
  white-space: nowrap;
}

#cssmenu ul {
  text-transform: uppercase;
}

#cssmenu ul ul {
  border-top: 4px solid #1b9bff;
  text-transform: none;
  min-width: 220px;
}

#cssmenu ul ul a {
  background: #1b9bff;
  color: #fff;
  border: 1px solid #0082e7;
  border-top: 0 none;
  line-height: 150%;
  padding: 10px 20px;
}

#cssmenu ul ul ul {
  border-top: 0 none;
}

#cssmenu ul ul li {
  position: relative;
}

#cssmenu > ul > li > a {
  line-height: 48px;
}

#cssmenu ul ul li:first-child > a {
  border-top: 1px solid #0082e7;
}

#cssmenu ul ul li:hover > a {
  background: #35a6ff;
}

#cssmenu ul ul li:last-child > a {
  border-radius: 0 0 3px 3px;
  box-shadow: 0 1px 0 #1b9bff;
}

#cssmenu ul ul li:last-child:hover > a {
  border-radius: 0 0 0 3px;
}

#cssmenu ul ul li.has-sub > a:after {
  content: "+";
  position: absolute;
  top: 50%;
  right: 15px;
  margin-top: -8px;
}

#cssmenu ul li:hover > a,
#cssmenu ul li.active > a {
  color: #1b9bff;
  background: transparent;
}

#cssmenu ul li.has-sub > a:after {
  content: "+";
  margin-left: 5px;
}

#cssmenu ul li.last ul {
  left: auto;
  right: 0;
}

#cssmenu ul li.last ul ul {
  left: auto;
  right: 99.5%;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.3.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.3.1/umd/react-dom.production.min.js"></script>

How to chain two drop-down lists? [duplicate]

How to chain two drop down lists. Individually, these dropdowns work without issue. Bellow is my code of working example. The thing is, I need both drop-down lists to work as a dynamic filter. The integers ‘category_ID’ that I want to use to combine both lists are included in the “category” and “subcategory” tables. How to do it?

Select boxes:

<div class="col-lg-3 col-md-3">
    <div class="form-group">
        <label for="category-Selector">Category:</label>
        <select id="category_Selector" class="form-select form-control" name="select_category" required>
            <option value="">Select Category...</option>
            <option disabled> ... </option>

                <?php
                    //Load All Customers
                    $ret = "SELECT category_ID, category_Name FROM  categories";
                    $stmt = $con->prepare($ret);
                    $stmt->execute();
                    $res = $stmt->get_result();
                    while ($cust = $res->fetch_object()) {
                ?>

            <option><?php echo $cust->category_Name; ?></option>

                <?php } ?>

        </select>
    </div>
</div>

<div class="col-lg-3 col-md-3">
    <div class="form-group">
        <label for="subcategory_Selector">Subategory:</label>
        <select id="subcategory_Selector" class="form-select form-control" name="select_subcategory" required>
            <option value="">Select Category first...</option>
            <option disabled> ... </option>

                <?php
                    //Load All Customers
                    $ret = "SELECT subcategory_Code, subcategory_Name FROM  subcategories";
                    $stmt = $con->prepare($ret);
                    $stmt->execute();
                    $res = $stmt->get_result();
                    while ($cust = $res->fetch_object()) {
                ?>

            <option><?php echo $cust->subcategory_Name; ?></option>

                <?php } ?>

        </select>
    </div>
</div>

Data tables:

--
-- Table structure for table `categories`
--

DROP TABLE IF EXISTS `categories`;
CREATE TABLE IF NOT EXISTS `categories` (
  `id` int NOT NULL AUTO_INCREMENT,
  `category_ID` int NOT NULL,
  `category_Name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `category_Description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

--
-- Dumping data for table `categories`
--

INSERT INTO `categories` (`id`, `category_ID`, `category_Name`, `category_Description`) VALUES
(1, 1, 'Opel', 'Passenger cars'),
(2, 2, 'Ford', 'Passenger cars & trucks'),
(3, 3, 'Volvo', 'Passenger cars & trucks');


--
-- Table structure for table `subcategories`
--

DROP TABLE IF EXISTS `subcategories`;
CREATE TABLE IF NOT EXISTS `subcategories` (
  `id` int NOT NULL AUTO_INCREMENT,
  `category_ID` int DEFAULT NULL,
  `subcategory_Name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `subcategory_Code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=63 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

--
-- Dumping data for table `subcategories`
--

INSERT INTO `subcategories` (`id`, `category_ID`, `subcategory_Name`, `subcategory_Code`) VALUES
(1, 1, 'Meriva', '01'),
(2, 1, 'Monerey', '02'),
(3, 1, 'Zefira', '03'),
(4, 1, 'Insignia', '04'),
(5, 2, 'Focus', '01'),
(6, 2, 'Bronco', '02'),
(7, 2, 'F360', '03'),
(8, 2, 'Town Car', '04'),
(9, 3, 'S70', '01'),
(10, 3, 'XC90', '02'),
(11, 3, 'FH-12', '03');

Parent dropdown

<select ...   onChange="getSubcategory(this.value);">

ajax
……………

function getSubcategory(val) {
    $.ajax({
    type: "POST",
    url: "get_subcategory.php",
    data:'category_ID='+val,
    success: function(data){
        $("#subcategory_Selector").html(data);
    }
    });
}

.php

<?php
    require_once("config/db-connect.php");
    //$db_handle = new DBController();


    $query ="SELECT * FROM subcategories WHERE category_ID = '" . $_POST["category_ID"] . "'";
    $results = $con->query($query);
?>
    <option value="">Select Subcategory</option>
<?php
    while($row=$results->fetch_assoc()) {
?>
    <option value="<?php echo $row["subcategory_Code"]; ?>"><?php echo $row["subcategory_Name"]; ?></option>
<?php

}
?>

Downloading binary file in javascript (node) from github

I am trying to download a file from github, but I am getting inconsistent sizes than if I download the raw file from github, I am using the download_url that I get from the directory listing from github. My code is:

async function getWaveDataFile(url) {
    try {
        const token = 'github_pat_personal-acess-token';
       
        
        const instance = axios.create({
            baseURL: url,
            timeout: 5000,
            headers: { Authorization: 'token ' + token },
        });
        console.log("axios Created BaseURL", instance.defaults.baseURL);


        const res = await instance.get();
        
        return res.data;
    } catch (err) {
        console.error('GG', err);
    }
}

I get binary data result from this, but if I save it to a file the size is inconsistent with a manual file download.

Any help with this would be much appreciated

jQuery SyntaxError in Edge: Unrecognized Expression

I’m encountering a peculiar issue in Edge where my jQuery selector is throwing a Uncaught error: SYntax Error, unrecognized expression: for the following selector:

$('.usname,.myNameValue,details .fRght ul li').tooltip({})

Any insights or solutions to resolve this Edge-specific issue would be greatly appreciated.

Additional Information:

  • Edge Version: 131.0.39
  • jQuery Version: 3.7

This selector works fine in other browsers chrome, but Edge seems to be having trouble parsing it.

Clearing Edge Browser cache works for first time.

How to force Facebook to open the mobile version?

To deal with the problem of not being able to click a Facebook div with randomized selector, I want to use the mobile version of Facebook (m.facebook.com):

import puppeteer from "npm:puppeteer";
const browser = await puppeteer.launch({
  executablePath: "C:/Users/ganuo/.cache/puppeteer/chrome/win64-125.0.6422.60/chrome-win64/chrome.exe",
  headless: false,
  userDataDir: "./user_data",
});
const page = await browser.newPage();
await page.goto("https://m.facebook.com/qua.cau.the.sphere");

The page redirects to the desktop version. I then change the user agent to the mobile one:

await page.setUserAgent(
  "Mozilla/5.0 (Linux; Android 12; M2003J15SC Build/SP1A.210812.016; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/130.0.6723.99 Mobile Safari/537.36 [FB_IAB/FB4A;FBAV/489.0.0.66.81;IABMV/1;]",
);

then it returns this error:

error: Uncaught (in promise) Error: net::ERR_ABORTED at https://m.facebook.com/qua.cau.the.sphere
    at navigate (file:///C:/Users/ganuo/AppData/Local/deno/npm/registry.npmjs.org/puppeteer-core/23.6.1/lib/esm/puppeteer/cdp/Frame.js:181:27)
    at eventLoopTick (ext:core/01_core.js:214:9)
    at async Function.race (file:///C:/Users/ganuo/AppData/Local/deno/npm/registry.npmjs.org/puppeteer-core/23.6.1/lib/esm/puppeteer/util/Deferred.js:33:20)
    at async CdpFrame.goto (file:///C:/Users/ganuo/AppData/Local/deno/npm/registry.npmjs.org/puppeteer-core/23.6.1/lib/esm/puppeteer/cdp/Frame.js:147:25)
    at async CdpPage.goto (file:///C:/Users/ganuo/AppData/Local/deno/npm/registry.npmjs.org/puppeteer-core/23.6.1/lib/esm/puppeteer/api/Page.js:570:20)
    at async file:///D:/QC supplements/Code/Apps/Xây nhân hiệu tự động/puppeteer.js:15:1

A couple other user-agents from UserAgents.io don’t work as well. I can workaround this by using mbasic.facebook.com, but for the sake of learning I wonder why the code doesn’t work.

Why custom writable stream is producing log in sync?

In Following Code :

const { Writable } = require("stream");

class Custome extends Writable {
    constructor(option) {
        super(option);
    }

    _write(chunk, config, callback) {
        console.log(chunk.toString());

        setTimeout(() => {
            callback(null)
        }, 1000);
    }
}

const log = new Custome({ highWaterMark: 16,defaultEncoding:"utf-8" });

log.write("0");
log.on("drain",()=>{
    console.log("Strem drained")
})

for (let i = 1; i < 51; i++) {
    const writable = log.write(i.toString());
    console.log(writable);
}

Here is custom writable stream with watermark of 16 Bytes, I thought as each number can be represent by 1 byte in utf-8, so buffer will at first 16 number i.e. till 15 after that overflowing will start, and further data will may get lost, But there was 11 true, and after that all false, excepted was 16 true. After buffer is full and then buffer data is processed , drain event will be emitted, but it was emitted at end, do known why? and all number was printed sync without any loss why? As buffer full and there is no space data should be lost na?

Excepted

>0
>true * 15
>false (remaining)
1 ... 15 
and remaining log but with missing number

Actual

>0 
>true * 11
> false ...
>1  to 50

Multiple code Execution

Conditional Routing Not working – using react router

i’d love some help with this. Its been driving me crazy for a couple of days. I”m
trying to route to a component based on a condition and it does it, but only if i refresh the page. I’ve had to add forceUpdate() to get it to work, but that is killing performance. Maybe this is something that would stand out easily for you guys, but for me it is scaping. any help would be appreciated. the following route is the one in question

<Route element={<RequireAuth />}>
        {/* <Route path="/" element={<InvoiceList2 />} /> */}
        <Route
          path="/"
          element={userrole === "Browser" ? <ViewOnly /> : 
    <InvoiceList2 />}
        />
</Route>

full code below:

import React, { useEffect, useState, useReducer } from "react";
import { BrowserRouter, Routes, Route, Navigate } from "react- 
router-dom";
    
import {
  useFetchVendorsQuery,
  useFetchInvoicesQuery,
  useFetchTrackersQuery,
} from "../src/features/Invoices/InvoiceSlice";

const ROLES = {
  User: "user",
  Admin: "Admin",
};

const Wildcard = () => {
  <Navigate to="/login" />;
};

const App = () => {
  // const auth = useAuth();
  // let { user, role, userid } = auth.auth;

  const GetVendors = useFetchVendorsQuery();
  const [vendors, setVendors] = useState([]);

  const GetTrackers = useFetchTrackersQuery();
  const [trackers, setTrackers] = useState([]);

  const GetInvoices = useFetchInvoicesQuery();
  const [invoices, setInvoices] = useState([]);
  const [userrole, setUserRole] = useState("");

  const User = JSON.parse(localStorage.getItem("user"));

  const [ignored, forceUpdate] = useReducer((x) => x + 1, 0);

  const handleOnload = () => {
    forceUpdate();
  };

  useEffect(() => {
    // setVendors(GetVendors);
    // setInvoices(GetInvoices);
    // setTrackers(GetTrackers);
    setUserRole(User?.role);
    forceUpdate();
  }, [userrole, ignored]);

  return (
    <div className="content">
      <BrowserRouter>
        {/* <Header /> */}
        <Routes>
          <Route path="/login" element={<Login />} />
          <Route path="/register" element={<Registration />} />
          <Route element={<RequireAuth />}>
            {/* <Route path="/" element={<InvoiceList2 />} /> */}
            <Route
              path="/"
              element={userrole === "Browser" ? <ViewOnly /> : <InvoiceList2 />}
            />
          </Route>
          <Route element={<RequireAuth />}>
            <Route path="/admin" element={<Admin />} />
          </Route>
          <Route element={<RequireAuth />}>
            <Route path="/manual" element={<ManualEntry />} />
          </Route>
          <Route path="/*" element={Wildcard} />
        </Routes>
        <Footer1 />
      </BrowserRouter>
    </div>
    );
  };

 export default App;

Issue with new ESLint configuration

I am upgrading my ESLint configuration from 8 to 9.14.0. But now after creating the eslint.config.js file, when I am running npx eslint, it is giving me weird errors. For example I have a component file named QuestionnaireTabNavigator.js, which is using some components and I am using them in my code but still getting issue in ESLint like:

   3:10  error  'Tab' is defined but never used                           no-unused-vars
   3:15  error  'Tabs' is defined but never used                          no-unused-vars
   9:8   error  'AssessmentSelector' is defined but never used            no-unused-vars
  10:8   error  'AssessmentsTable' is defined but never used              no-unused-vars
  11:8   error  'ErrorBoundaryWrapper' is defined but never used          no-unused-vars
  12:8   error  'Loader' is defined but never used                        no-unused-vars
  13:8   error  'QuestionnaireDetailedResults' is defined but never used  no-unused-vars
  14:8   error  'QuestionnaireSummary' is defined but never used  

What is the issue?

eslint.config.mjs:

import babelParser from "@babel/eslint-parser";
import pluginJs from "@eslint/js";
import importConfig from "eslint-plugin-import";
import pluginJest from "eslint-plugin-jest";
import jsdocConfig from "eslint-plugin-jsdoc";
import jsoncConfig from "eslint-plugin-jsonc";
import pluginPrettier from "eslint-plugin-prettier";
import pluginReact from "eslint-plugin-react";
import reactHooks from "eslint-plugin-react-hooks";
import sortKeysFixConfig from "eslint-plugin-sort-keys-fix";
import spellcheck from "eslint-plugin-spellcheck";
import testingPlugin from "eslint-plugin-testing-library";
import globals from "globals";

const config = [
    pluginJs.configs.recommended,
    {
        "files": ["**/*.{js,mjs,cjs,jsx}"],
        "languageOptions": {
            "ecmaVersion": 2020,
            "globals": {
                ...globals.es2021,
                ...globals.node,
                ...globals.browser,
                "describe": "readonly",
                "expect": "readonly",
                "indexedDB": "readonly",
                "it": "readonly",
                "jest": "readonly",
                "test": "readonly"
            },
            "parser": babelParser,
            "parserOptions": {
                "babelOptions": {
                    "babelrc": false,
                    "configFile": false,
                    "presets": [["@babel/preset-react", { "runtime": "automatic" }], "@babel/preset-env"]
                },
                "ecmaFeatures": {
                    "jsx": true
                },
                "requireConfigFile": false,
                "sourceType": "module"
            }
        },
        "plugins": {
            "import": importConfig,
            "jsdoc": jsdocConfig,
            "jsonc": jsoncConfig,
            "prettier": pluginPrettier,
            "react": pluginReact,
            "react-hooks": reactHooks,
            "sort-keys-fix": sortKeysFixConfig,
            "spellcheck": spellcheck,
            "testing-library": testingPlugin
        },
        "rules": {
            "array-bracket-newline": ["error", "consistent"],
            "array-bracket-spacing": ["error", "never"],
            "array-callback-return": "error",
            "array-element-newline": [
                "error",
                {
                    "ArrayExpression": "consistent",
                    "ArrayPattern": { "minItems": 3 }
                }
            ],
            "arrow-spacing": "error",
            "brace-style": ["error", "1tbs"],
            "camelcase": ["error", { "ignoreDestructuring": true, "properties": "never" }],
            "comma-dangle": ["error", "never"],
            "comma-spacing": [
                "error",
                {
                    "after": true,
                    "before": false
                }
            ],
            "curly": ["error", "all"],
            "default-case": "off",
            "eqeqeq": ["error", "always"],
            "func-call-spacing": ["error", "never"],
            "function-call-argument-newline": ["error", "consistent"],
            "import/first": "off",
            "import/order": ["error", { "groups": ["builtin", "external", "internal", "parent", "sibling", "index"] }],
            "indent": ["error", 4, { "SwitchCase": 1, "ignoredNodes": ["ConditionalExpression"] }],
            "jest/no-mocks-import": "off",
            "jsdoc/newline-after-description": 0,
            // Required for vs code auto formatting
            "jsdoc/require-hyphen-before-param-description": 1,

            "jsdoc/require-jsdoc": [
                "error",
                {
                    "require": {
                        "ArrowFunctionExpression": true,
                        "ClassExpression": true,
                        "FunctionDeclaration": true,
                        "FunctionExpression": true,
                        "MethodDefinition": true
                    }
                }
            ],
            "jsonc/sort-keys": "error",
            "keyword-spacing": ["error", { "after": true, "before": true }],
            "max-len": [
                "error",
                {
                    "code": 120,
                    "ignorePattern": "".*": ".*"" // Ignore pattern for strings in json as they can't be broken in multi lines
                }
            ],
            "no-console": "error",
            "no-dupe-else-if": "error",
            "no-extend-native": "off",
            "no-nested-ternary": "error",
            // "no-unused-vars": "warn",
            "no-useless-escape": "off",
            "no-var": "error",
            "object-curly-newline": ["error", { "consistent": true, "multiline": true }],
            "object-curly-spacing": ["error", "always"],
            "object-property-newline": ["error", { "allowAllPropertiesOnSameLine": true }],
            "padding-line-between-statements": [
                "error",
                // Always one empty line before return statement
                {
                    "blankLine": "always",
                    "next": "return",
                    "prev": "*"
                },
                // Always one empty line between methods
                {
                    "blankLine": "always",
                    "next": ["block-like", "multiline-block-like"],
                    "prev": ["block-like", "multiline-block-like"]
                },
                // Avoids more than one empty line
                {
                    "blankLine": "never",
                    "next": "empty",
                    "prev": "empty"
                }
            ],
            "prefer-const": "error",
            "quote-props": ["error", "always"],
            "quotes": ["error", "double"],
            "radix": "off",
            "react-hooks/exhaustive-deps": "error",
            "react-hooks/rules-of-hooks": "error",
            "react/jsx-boolean-value": ["warn", "always"],
            "react/jsx-closing-bracket-location": "error",
            "react/jsx-closing-tag-location": "error",
            "react/jsx-curly-brace-presence": [
                "error",
                {
                    "children": "ignore",
                    "props": "always"
                }
            ],
            "react/jsx-tag-spacing": ["error", { "beforeSelfClosing": "always" }],
            "react/no-multi-comp": ["error", { "ignoreStateless": true }],
            "react/react-in-jsx-scope": "off",
            "semi": ["error", "always"],
            "sort-keys": [
                "error",
                "asc",
                {
                    "caseSensitive": true,
                    "minKeys": 2,
                    "natural": false
                }
            ],
            "sort-keys-fix/sort-keys-fix": "error",
            "space-before-blocks": "error",
            "space-infix-ops": ["error", { "int32Hint": false }],
            "testing-library/no-node-access": "off"
        },
        "settings": {
            "react": {
                // Only needed for older React versions
                "fragment": "Fragment",
                // Automatically detect React version
                "pragma": "React",
                // Only needed for older React versions
                "runtime": "automatic",
                "version": "detect" // Use React 17+ JSX transform
            }
        }
    },
    {
        "files": ["**/*.test.{js,mjs,cjs,jsx}"],
        "languageOptions": {
            "globals": {
                ...globals.jest,
                ...globals.browser,
                ...globals.node,
                "describe": "readonly",
                "expect": "readonly",
                "indexedDB": "readonly",
                "it": "readonly",
                "jest": "readonly",
                "test": "readonly"
            }
        },
        "plugins": {
            "jest": pluginJest
        },
        "rules": {
            "react/react-in-jsx-scope": "off"
        },
        "settings": {
            "react": {
                "version": "detect"
            }
        }
    }
];

export default config;

package.json file:

{
    "browserslist": [
        ">0.2%",
        "not dead",
        "not op_mini all"
    ],
    "dependencies": {
        "@types/jest": "^29.5.11",
        "ajv": "^8.17.1",
        "awesome-debounce-promise": "~2.1.0",
        "axios": "~1.7.2",
        "bootstrap": "~5.3.2",
        "dotenv": "^16.4.1",
        "i18next": "~23.15.1",
        "i18next-http-backend": "~2.6.1",
        "jest-mock-axios": "~4.7.3",
        "lodash": "~4.17.21",
        "moment": "~2.30.1",
        "moment-timezone": "^0.5.43",
        "react": "~18.3.1",
        "react-bootstrap": "~2.10.2",
        "react-bootstrap-icons": "~1.11.4",
        "react-component-export-image": "~1.0.6",
        "react-dom": "~18.3.1",
        "react-draggable": "^4.4.6",
        "react-dropzone": "^14.2.3",
        "react-error-boundary": "^4.0.13",
        "react-i18next": "~15.0.1",
        "react-icons": "~5.3.0",
        "react-infinite-scroller": "~1.2.6",
        "react-intersection-observer": "~9.13.0",
        "react-leaflet": "~4.2.1",
        "react-multi-select-component": "~4.3.4",
        "react-router": "~6.26.2",
        "react-router-dom": "~6.26.2",
        "react-router-prompt": "^0.7.0",
        "react-scroll": "~1.9.0",
        "react-select": "~5.8.0",
        "react-switch": "~7.0.0",
        "react-tabs": "~6.0.2",
        "react-toastify": "~10.0.4",
        "socket.io-client": "~4.8.0",
        "use-csv-downloader": "0.0.0",
        "web-vitals": "~4.2.2"
    },
    "devDependencies": {
        "@babel/eslint-parser": "~7.25.9",
        "@eslint/js": "^9.14.0",
        "@testing-library/dom": "~10.4.0",
        "@testing-library/jest-dom": "~6.6.3",
        "@testing-library/react": "~16.0.1",
        "@testing-library/user-event": "~14.5.1",
        "@typescript-eslint/eslint-plugin": "~8.13.0",
        "@typescript-eslint/parser": "~8.13.0",
        "eslint": "~9.14.0",
        "eslint-config-prettier": "~9.1.0",
        "eslint-config-react-app": "~7.0.1",
        "eslint-plugin-import": "~2.31.0",
        "eslint-plugin-jest": "~28.9.0",
        "eslint-plugin-jsdoc": "^50.5.0",
        "eslint-plugin-jsonc": "~2.18.1",
        "eslint-plugin-jsx-a11y": "~6.10.2",
        "eslint-plugin-prettier": "~5.2.1",
        "eslint-plugin-react": "^7.37.2",
        "eslint-plugin-react-hooks": "~5.0.0",
        "eslint-plugin-sort-keys-fix": "~1.1.2",
        "eslint-plugin-spellcheck": "0.0.20",
        "eslint-plugin-testing-library": "~6.4.0",
        "fake-indexeddb": "6.0.0",
        "globals": "^15.12.0",
        "pre-commit": "~1.2.2",
        "prettier": "~3.3.3",
        "prettier-eslint": "~16.3.0",
        "prettier-eslint-cli": "~8.0.1",
        "react-scripts": "~5.0.1",
        "sass": "~1.80.6",
        "stylelint": "~16.10.0",
        "stylelint-config-prettier": "~9.0.5",
        "stylelint-config-standard-scss": "~13.1.0",
        "stylelint-prettier": "~5.0.2",
        "stylelint-scss": "~6.8.1"
    },
    "eslintConfig": {
        "extends": [
            "react-app",
            "react-app/jest"
        ]
    },
    "jest": {
        "collectCoverageFrom": [
            "src/**/*.js",
            "!src/**/*.scss",
            "!src/assets/**/*.js"
        ],
        "coverageThreshold": {
            "global": {
                "branches": 50,
                "functions": 50,
                "lines": 65,
                "statements": 65
            }
        }
    },
    "name": "imp-fe",
    "pre-commit": [
        "lint-pre-commit",
        "test-pre-commit"
    ],
    "private": true,
    "scripts": {
        "build": "react-scripts build",
        "eject": "react-scripts eject",
        "format": "npm run prettier-fix && npm run lint-fix",
        "lint": "npm run lint-check && npm run stylelint",
        "lint-check": "eslint src/ --ignore-pattern 'src/__mocks__/'",
        "lint-fix": "npm run lint-check -- --fix",
        "lint-pre-commit": "FILE_DIFF=$(git diff --name-only --cached --diff-filter=ACMR | grep .js$ | sed 's#/[^/]*$##') && [ '$FILE_DIFF' != '' ] && (eslint --ext .jsx --ext .js --ext .json $(echo $FILE_DIFF) && npm run stylelint) || exit 0",
        "prettier-fix": "npx prettier --write src/",
        "pull-latest-and-verify": "npm run pull-latest-code && node ./.dev/check_if_env_latest.js",
        "pull-latest-code": "git pull && npm i",
        "start": "react-scripts start",
        "stylelint": "npx stylelint 'src/**/*.{css,scss}'",
        "stylelint-fix": "npm run stylelint -- --fix",
        "test": "react-scripts test --maxWorkers 1",
        "test-coverage": "npm run test -- --coverage --watchAll=false",
        "test-no-watch": "npm run test -- --watchAll=false",
        "test-pre-commit": "FILE_DIFF=$(git diff --name-only --cached --diff-filter=ACMR | grep .js$ | sed 's#/[^/]*$##') && [ '$FILE_DIFF' != '' ] && (npm run test-no-watch -- $(echo $FILE_DIFF) --passWithNoTests) || exit 0"
    },
    "version": "24.8.0"
}

Component file QuestionnaireTabNavigator.js:

import PropTypes from "prop-types";
import { useCallback, useContext, useState } from "react";
import { Tab, Tabs } from "react-bootstrap";
import { withTranslation } from "react-i18next";
import { useNavigate, useSearchParams } from "react-router-dom";
import { CATEGORY_WISE_DATA_STRUCTURE, CATEGORY_WISE_DETAILED_RESULT } from "../../App.Constants";
import { QuestionnaireContext } from "../../contexts/QuestionnaireContext";
import { UserContext } from "../../contexts/UserContext";
import AssessmentSelector from "../AssessmentSelector/AssessmentSelector";
import AssessmentsTable from "../AssessmentsTable/AssessmentsTable";
import ErrorBoundaryWrapper from "../ErrorBoundaryWrapper/ErrorBoundaryWrapper";
import Loader from "../Loaders/Loader/Loader";
import QuestionnaireDetailedResults from "../QuestionnaireDetailedResults/QuestionnaireDetailedResults";
import QuestionnaireSummary from "../QuestionnaireSummary/QuestionnaireSummary";
import "./QuestionnaireTabNavigator.scss";

/**
 *
 * @param {*} props - Parent props
 * @returns {Element} - Returns multiple tabs with different sub components
 */
const QuestionnaireTabNavigator = ({ t }) => {
    const navigate = useNavigate();
    const { fetchAssessmentsList, "allAssessmentsData": apiResponse } = useContext(QuestionnaireContext);

    const { user } = useContext(UserContext);
    const [searchParams] = useSearchParams();
    const section = searchParams.get("section");

    /**
     * Added this extra check for apiResponse for smooth transitioning.
     * Loader will be visible based on current state value.
     * Also updating these state values from AssessmentSelector component.
     */
    const [benchmarkingData, setBenchmarkingData] = useState(!apiResponse.length ? CATEGORY_WISE_DATA_STRUCTURE : null);
    const [detailedResults, setDetailedResults] = useState(!apiResponse.length ? CATEGORY_WISE_DETAILED_RESULT : null);

    let tab = "assessments";
    if (window.location.pathname.includes("summary")) {
        tab = "summary";
    } else if (window.location.pathname.includes("detailed-results")) {
        tab = "detailed-results";
    }
    const [selectedTab, setSelectedTab] = useState(tab);

    const handleTabSelect = useCallback(
        (tab) => {
            if (tab === "detailed-results") {
                navigate(
                    `/${user.selectedTenant}/maturity-assessment/detailed-results${
                        section ? `section=${section}` : ""
                    }`,
                    {
                        "state": { "redirect": "no" }
                    }
                );
            } else if (tab === "summary") {
                navigate(`/${user.selectedTenant}/maturity-assessment/summary`, {
                    "state": { "redirect": "no" }
                });
            } else {
                navigate(`/${user.selectedTenant}/maturity-assessment/all-assessments`, {
                    "state": { "redirect": "no" }
                });
            }
        },
        [navigate, section, user.selectedTenant]
    );

    const setStateMethod = selectedTab === "detailed-results" ? setDetailedResults : setBenchmarkingData;

    return (
        <div
            className={"QuestionnaireTabNavigator bottom-border-tab-variant"}
            data-testid={"QuestionnaireTabNavigator"}
        >
            {selectedTab !== "assessments" ? (
                <AssessmentSelector
                    assessmentsList={apiResponse}
                    selectedTab={selectedTab}
                    setStateMethod={setStateMethod}
                />
            ) : null}

            <Tabs
                activeKey={selectedTab}
                id={"questionnaire-tab-navigator"}
                transition={false}
                onSelect={handleTabSelect}
            >
                <Tab eventKey={"assessments"} title={t("questionnaire.tab_navigator.assessments")}>
                    <ErrorBoundaryWrapper componentTitle={"Assessment table"}>
                        <AssessmentsTable apiResponse={apiResponse} fetchAssessmentsList={fetchAssessmentsList} />
                    </ErrorBoundaryWrapper>
                </Tab>
                <Tab eventKey={"summary"} title={t("questionnaire.tab_navigator.summary")}>
                    <ErrorBoundaryWrapper componentTitle={"Questionnaire Summary"}>
                        {benchmarkingData ? (
                            <QuestionnaireSummary benchmarkingData={benchmarkingData} setSelectedTab={setSelectedTab} />
                        ) : (
                            <Loader />
                        )}
                    </ErrorBoundaryWrapper>
                </Tab>
                <Tab eventKey={"detailed-results"} title={t("questionnaire.tab_navigator.detailed-results")}>
                    <ErrorBoundaryWrapper componentTitle={"Questionnaire Detailed Results"}>
                        {detailedResults ? (
                            <QuestionnaireDetailedResults detailedResults={detailedResults} />
                        ) : (
                            <Loader />
                        )}
                    </ErrorBoundaryWrapper>
                </Tab>
            </Tabs>
        </div>
    );
};

QuestionnaireTabNavigator.propTypes = {
    "t": PropTypes.func
};

export default withTranslation()(QuestionnaireTabNavigator);

How can I upload a screenshot from puppeteer to fastapi?

I am trying to create some code which uploads a screenshot from pupeteer to a fastapi endpoint. The endpoint currently looks like:

@nodes_router.post("/node/{node_name}/screenshot")
async def set_node_screenshot(node_name: str, screenshot: UploadFile = File(...)):
    screenshot_path = screenshot_folder / f"{node_name}.jpg"  # Save as .jpg
    # Reset the file pointer after reading for logging
    screenshot.file.seek(0)

    try:
        # Save the uploaded .jpg file
        with screenshot_path.open("wb") as buffer:
            shutil.copyfileobj(screenshot.file, buffer)
        return {"message": "Screenshot uploaded successfully"}
    except Exception as e:
        return {"error": f"Error uploading screenshot: {str(e)}"}

And the javascript side looks like:

async function uploadScreenshot(nodeName, page) {
  try {
    // Define the path where the screenshot will be saved (in jpg format)
    const screenshotPath = path.join(__dirname, `screenshot.jpg`);

    // Take the screenshot and save it as a .jpg file
    await page.screenshot({ path: screenshotPath, type: "jpeg" }); // Save as JPEG

    // Create a FormData instance
    const formData = new FormData();
    formData.append("screenshot", fs.createReadStream(screenshotPath), {
      filename: `${nodeName}.jpg`, // Ensure the filename is passed
      contentType: "image/jpeg", // Set the correct content type
    });

    const screenshotUrl = `http://localhost:5000/node/${nodeName}/screenshot`;

    // Perform the upload using fetch
    const response = await fetch(screenshotUrl, {
      method: "POST",
      body: formData,
      headers: formData.getHeaders(),
    });

    if (response.ok) {
      console.log("Upload successful");
    } else {
      const text = await response.text();
      console.error("Error uploading file:", response.statusText, text);
    }
  } catch (error) {
    console.error("Error uploading screenshot:", error);
  }
}

// code to get a page with puppeteer...

const pages = await browser.pages();
const page = pages[0];
await uploadScreenshot("foo", page);

however it is saying:

Error uploading file: Bad Request {"detail":"There was an error parsing the body"}

When I look on the server logs of fastapi it says:

Expected boundary character 45, got 91 at index 2
INFO:     127.0.0.1:58121 - "POST /node/2b76e38a-7585-4fbe-b777-d846d30f0aa8/screenshot HTTP/1.1" 400 Bad Request

if I remove the headers: formData.getHeaders(), code, it doesnt have the Expected boundary character error, but it says it is an unprocessable entity:

Error uploading file: Unprocessable Entity {"detail":[{"type":"missing","loc":["body","screenshot"],"msg":"Field required","input":null}]}

Why is this happening and how can I fix it?

How to fix Horizontal Gap Issue in Reorder Function with Animation

I’ve built a reorder function that comes with a smooth animation, and it works pretty well. However, I’m facing an issue with the gap between the items. I want to have a consistent gap of 10px horizontally and vertically, but while the vertical gap is perfect, the horizontal gap is not behaving as expected.

Any suggestions or optimizations for fixing the horizontal gap would be greatly appreciated!

Details:

The vertical gap is fine and works as expected.
I am using position: absolute for the items.
The gap between items is defined as 10px in the layout.
Thanks in advance for your help!

The code : https://stackblitz.com/edit/plain-html-la5wdv?file=index.ts,index.html,style.css

How can I set the width of cells in a table using the docx library for Node.js?

I’m generating a .docx file using Node.js. In my header, I have a table that contains one row and two cells. The table spans the full width, and I’m trying to set the width of the cells as follows:

  • The left cell, which contains an image, should be as wide as the
    image.
  • The right cell, which contains text, should take up the remaining
    width of the row (100% of the row width minus the width of the
    image).

As per the documentation, I’m using the width property to define both the table’s width and the individual cell widths, but I can’t get it to work.
I also tried setting, as in the following example, the total table width to 100% and the two cells to 20% and 80%, respectively, but they still remain 50% wide each.

const headerImage = new ImageRun({
    type: 'png',
    data: fs.readFileSync("./public/assets/images/test-image.png"),
    transformation: {
        width: 120,
        height: 120,
    },
});

const heading = new Table({
    width: {
        size: 100,
        type: WidthType.PERCENTAGE,
    },
    rows: [
        new TableRow({
            children: [
                new TableCell({
                    width: {
                        size: 20,
                        type: WidthType.PERCENTAGE
                    },
                    verticalAlign: "center",
                    children: [
                        new Paragraph({
                            children: [
                                headerImage
                            ]
                        })
                    ],
                }),
                new TableCell({
                    width: {
                        size: 80,
                        type: WidthType.PERCENTAGE
                    },
                    verticalAlign: "center",
                    children: [
                        new Paragraph({
                            alignment: "center",
                            children: [
                                new TextRun({
                                    text: "Text1",
                                    bold: true,
                                    size: "30pt",
                                }),
                                new TextRun({
                                    text: "Text2",
                                    italics: true,
                                    bold: true,
                                    size: "10pt",
                                    break: 1

                                }),
                                new TextRun({
                                    text: "Text3",
                                    size: "10pt",
                                    break: 1

                                }),
                                new TextRun({
                                    text: "TEXT4 ",
                                    size: "10pt",
                                    break: 1
                                }),
                                new TextRun({
                                    text: "Text5",
                                    underline: {},
                                    color: "0000FF",
                                })
                            ]
                        }),
                    ],
                }),
            ],
        }),
    ],
});

How can I simplify this piece of jaascript code

I’m sure this JS piece of JavaScript code can be simplified… I’m trying to reduce the amount of code lines and believe this can be condensed and merged into a shorter version? It’s basically a decision tree with yes | no choices and results appear according to user yes | no selection.

Much appreciated, many thanks in advance.

<script>
    document.getElementById('yesButton').addEventListener('click', function() {
        document.getElementById('noPanel').classList.add('hidden');
        document.getElementById('yesPanel').classList.remove('hidden');
        document.getElementById('keepPanel').classList.add('hidden');
        document.getElementById('sellPanel').classList.add('hidden');
        document.getElementById('gainNoPanel').classList.add('hidden');
        document.getElementById('gainYesPanel').classList.add('hidden');
        document.getElementById('cgtNoPanel').classList.add('hidden');
        document.getElementById('cgtYesPanel').classList.add('hidden');
    });

    document.getElementById('noButton').addEventListener('click', function() {
        document.getElementById('yesPanel').classList.add('hidden');
        document.getElementById('noPanel').classList.remove('hidden');
        document.getElementById('keepPanel').classList.add('hidden');
        document.getElementById('sellPanel').classList.add('hidden');
        document.getElementById('gainNoPanel').classList.add('hidden');
        document.getElementById('gainYesPanel').classList.add('hidden');
        document.getElementById('cgtNoPanel').classList.add('hidden');
        document.getElementById('cgtYesPanel').classList.add('hidden');
     });

     document.getElementById('keepButton').addEventListener('click', function() {
         document.getElementById('keepPanel').classList.remove('hidden');
         document.getElementById('sellPanel').classList.add('hidden');
         document.getElementById('gainNoPanel').classList.add('hidden');
         document.getElementById('gainYesPanel').classList.add('hidden');
         document.getElementById('cgtNoPanel').classList.add('hidden');
         document.getElementById('cgtYesPanel').classList.add('hidden');
        });

    document.getElementById('sellButton').addEventListener('click', function() {
        document.getElementById('sellPanel').classList.remove('hidden');
        document.getElementById('keepPanel').classList.add('hidden');
        document.getElementById('gainNoPanel').classList.add('hidden');
        document.getElementById('gainYesPanel').classList.add('hidden');
        document.getElementById('cgtNoPanel').classList.add('hidden');
        document.getElementById('cgtYesPanel').classList.add('hidden');
    });

    document.getElementById('gainNoButton').addEventListener('click', function() {
        document.getElementById('gainNoPanel').classList.remove('hidden');
        document.getElementById('gainYesPanel').classList.add('hidden');
        document.getElementById('cgtNoPanel').classList.add('hidden');
        document.getElementById('cgtYesPanel').classList.add('hidden');
    });

    document.getElementById('gainYesButton').addEventListener('click', function() {
        document.getElementById('gainYesPanel').classList.remove('hidden');
        document.getElementById('gainNoPanel').classList.add('hidden');
        document.getElementById('cgtNoPanel').classList.add('hidden');
        document.getElementById('cgtYesPanel').classList.add('hidden');
    });

    document.getElementById('cgtNoButton').addEventListener('click', function() {
        document.getElementById('cgtNoPanel').classList.remove('hidden');
        document.getElementById('cgtYesPanel').classList.add('hidden');
    });

    document.getElementById('cgtYesButton').addEventListener('click', function() {
        document.getElementById('cgtYesPanel').classList.remove('hidden');
        document.getElementById('cgtNoPanel').classList.add('hidden');
    });
</script>

How to replace double quotes within a string (only in the middle)

I asked this question to chatGpt, he gave me this regex and example:

let str = '"This is a "test" string with "quotes" in the middle."';
let result = str.replace(/(?<=^|[^"])(")(?=S.*")/g, 'replacement');
console.log(result);

But I still get the first double quotes in the begining of the string replaced

//replacementThis is a replacementtest" string with replacementquotes" in the middle."

I need only the doubles quotes in the middle to be replaced, not in the beginning or the end.