Transform a constructor function into a `new` function, preserving type parameters

I’m coding a type-safe class system using ES5 and JSDoc.

There is no notion of new functions in ES5; the type of function that constructs an object is just (...args: any) => void.
To make it clear to JSDoc (and TypeScript) that a function in fact constructs an object, I transform it using Parameters and InstanceType:

type Prototype = { construtor: (...args: any) => void };
type Class = {
  new (...args: Parameters<Prototype["construtor"]>): InstanceType<
    Prototype["construtor"]
  > &
    Omit<Prototype, "construct">;
};

Ignore the error on InstanceType, as it is irrelevant.

For some reason the type parameters of constructor do not work properly: on properties that should infer type information from a generic parameter I get <Type parameter> extends <Constraint>, instead of qualified type.

/**
 * @param {Prototype & ThisType<InstanceType<Prototype["construtor"]> & Prototype>} prototype
 * @template {{ construtor: (...args: any) => void }} Prototype
 * @returns {{
 *  new (...args: Parameters<Prototype["construtor"]>): InstanceType<
 *    Prototype["construtor"]
 *  > &
 *    Omit<Prototype, "construct">;
 * }}
 */
function cls(prototype) {
  // Ignore the implementation
  return;
}

var A = cls({
  /**
   * @param {T} prop
   * @template {string} T
   */
  construtor: function A(prop) {
    this.prop = prop;
  },
  prop1: "prop1"
});

var a = new A("a");
a.prop; // T extends string
// should be "a"

I’ve found a solution to this problem, but it’s far from ideal:

  • Instead of passing the constructor as a property of prototype, pass it as its own argument
  • Intersect with Constructor in return type
/**
 * @param {Prototype & ThisType<InstanceType<Constructor> & Prototype>} prototype
 * @template {object} Prototype
 * @param {Constructor} constructor
 * @template {(...args: any) => void} Constructor
 * @returns {Constructor & {
 *  new (...args: Parameters<Constructor>): InstanceType<
 *    Constructor
 *  > &
 *    Omit<Prototype, "construct">;
 * }}
 */
function cls(prototype, constructor) {
  // Ignore the implementation
  return;
}

/** @class */
var A = cls(
  /** @lends A.prototype */
  {
    prop1: "prop1",
  },
  /**
   * @constructs A
   * @param {T} prop
   * @template {string} T
   */
  function A(prop) {
    this.prop = prop;
    this.prop1; // Property 'prop1' does not exist on type 'A<T>'. Did you mean 'prop'?ts(2551)
  }
);

var a = new A("a");
a.prop; // "a"
a.prop1; // Property 'prop1' does not exist on type 'A<"a">'. Did you mean 'prop'?ts(2551)

Problems with this approach:

  • There is no way to specify the type of this for constructor (cannot access prop1 in constructor)
  • Resulting new function has two signatures, one of them does not include Prototype in instance type (cannot access prop1 of instance)

Socket.io behind a Nginx reverse proxy not working

I am serving my front-end static files with Nginx as follows.

server {                                                                        
        server_name  <my_domain>;                                          
                                                                                    
        root /usr/share/nginx/myproject/dist;                                  
        index index.htm index.html;                                                 
                                                                                    
        access_log  /var/log/nginx/host.access.log  main;                  
        error_log /var/log/nginx/host.error.log notice;                    
                                                                                    
        location /socket.io/ {                                                      
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;              
          proxy_set_header Host $host;                                              
                                                                                    
          proxy_pass https://localhost:8000;                                         
                                                                                    
          proxy_http_version 1.1;                                                   
          proxy_set_header Upgrade $http_upgrade;                                   
          proxy_set_header Connection "upgrade";                                    
         }         

        location / {                                                                
             try_files $uri $uri/ =404;                                              
         }

        listen 443 ssl; # managed by Certbot                                        
        ssl_certificate  # managed by Certbot
        ssl_certificate_key # managed by Certbot
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot       
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot         
    }

When I visit the domain address, I can see the front-end just fine but it is not making any websocket connections with the backend. There is no error whatsoever and the requests are only for the front-end contents.
My server code looks like this.

import { readFileSync } from 'fs';
import http from 'http';
import https from 'https';
import express from "express";
import { Server } from 'socket.io';
import cors from "cors";

const app = express();
app.use(cors);

let server;
if (process.env.NODE_ENV == "development") {
    server = http.createServer(app);
} else {
    server = https.createServer({
        cert: readFileSync("/etc/letsencrypt/live/<my_domain>/cert.pem", "utf8"),
        key: readFileSync("/etc/letsencrypt/live/<my_domain>/privkey.pem", "utf8"),
        ca: readFileSync("/etc/letsencrypt/live/<my_domain>/chain.pem", "utf8"),
    }, app);
}
let origin;
if (process.env.NODE_ENV == "development") {
    origin = "http://localhost:5173";
} else {
    origin = "http://<domain_name>";
}
const io = new Server(server, {
    cors: {
        origin: "*",
        methods: ["GET", "POST"]
    },
});


io.on('connection', (sock) => {
    sock.on('disconnect', (sock_id) => {
        console.log('user disconnected.')
    })

});

server.on('error', (err) => console.error(err));

server.listen(8000, () => {
    console.log('server is ready on 8000');
})

The client-side uses the socket object, which is exported as follows, in a single component.

import { io, Socket } from "socket.io-client";
import { ServerToClientEvents, ClientToServerEvents } from "./types";

let socket: Socket<ServerToClientEvents, ClientToServerEvents>;
if (process.env.NODE_ENV == 'development') {
    socket = io("http://localhost:8000",);
} else {
    socket = io(
        "https://<my_domain>/", { transports: ["websockets"], });
}

export default socket;

Of course I have tested in development but it is just not working on my VPS host. I have tried custom paths as mentioned on socket.io documentations.
I have tried both http and https server for my express backend.
I have tried both localhost and 127.0.0.1 on my nginx configurations.
I have tried multiple ports for the server.

Pausing a Vimeo video as soon as it loads using API – doesn’t always work

I am loading a Vimeo video using the API and want it paused as soon as it loads so I can control the playing depending on whether it is on screen or not. This seems to work some of the time but not all the time and doesn’t appear to follow a pattern. So, I wondered if I am doing anything wrong?

This is what I have…

This code is in the PHP page itself

<div id="UNIQUEPLAYERNAME"></div>
document.addEventListener("DOMContentLoaded", (event) => {
  var options = {
    url: '<?php echo $video_url; ?>',
    autoplay: 0,
    autopause: 0,
    dnt: 0,
    background: 1,
    loop: 1
  };
  var UNIQUEPLAYERNAME = fncNewVideo("UNIQUEPLAYERNAME", options);
  var onLoaded = function(data) {                         
    UNIQUEPLAYERNAME.ready().then(function() {
      fncWorkVideoInView('.trigger', UNIQUEPLAYERNAME);
    });
  };
  UNIQUEPLAYERNAME.on('loaded', onLoaded);
      
  var eventList = ["scroll", "resize", "orientationchange"];
  for(event of eventList) {
    window.addEventListener(event, function() {
      fncWorkVideoInView('.trigger', UNIQUEPLAYERNAME);
    });
  }
});

UNIQUEPLAYERNAME is generated using PHP as there are multiple videos on the page
This code is in the master JS file and loads the video correctly

window.fncNewVideo = (player, options) => {
  return new Player(player, options);
}

The function fncWorkVideoInView is also in the master.js file and works fine. It triggers correctly on first call but the pause doesn’t happen every time so, I am pretty sure it is something to do with calling the pause directly after loading but I thought using the event listener “loaded” would make sure everything is ready to control.

so in the bold highlighted i seem to be getting error 500 internal server [closed]

export default function Register() {
  const [username, setUsername] = useState('');
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  const router = useRouter();


  const handleSubmit = async (e) => {
    e.preventDefault();
    setError('');
    try {
      const res = await fetch('/api/auth/register', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, email, password }),
      });

      const data = await res.json();
      if (res.ok) {
        router.push('/login');
        // Redirect to login page on successful registration
      } else {
        //const data = await res.json();
        console.log('miss error');
        setError(data.message || 'Registration failed');
      }

    } catch (error) {
      setError('An error occurred. Please try again.');
    }
  }
}

so in the bold highlighted i seem to be getting error 500 internal server on that line when i try to fetch the data from the path

Return a forms multiple selected options in JavaScript

I’m currently using the following JavaScript code to return all HTML form elements and their contents, to then submit to a PHP API:

var formdata = Object.fromEntries(formData);
formdata = new URLSearchParams(formdata).toString()
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
xhttp.open("POST", "action.php", true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send(formdata);

This works fine, however the form now needs to use a “select multiple” rather than just a standard “select”, which only seems to return the last selected option rather than all selected options. Any ideas how to return all selected options on the “select multiple” box please?

Using a pair of dropdown elements to augment URL inside an HTML embed on the Wix platform using Javascript messaging

Product:
Velo, iFrame, HTML embed (Wix.com, isolated from other elements), dropdown

What are you trying to achieve:
I want to display an archive of PDF files on a single page on our public library’s new Wix site. I have basic embedding of the PDF viewer worked out and a license.

What I’m interested in this: my PDF Viewer is embedded in the Wix page using the html embed element, using an iframe. I have two drop-down selection boxes on this page, and they are for year and month selection, respectively. I want to message the selected year and then the selected month to the PDF viewer script to simply augment and load a different URL based on the selection. All PDFs are hosted elsewhere on a Digital Ocean droplet, just for file serving.

Is this possible? My current html embed that I want to automate with dropdown selectors is this:

    <div id="pspdfkit" style="width: 100%; height: 100%; max-width: 1920px;"></div>
    <script src="https://cdn.cloud.pspdfkit.com/[email protected]/pspdfkit.js"></script>
    <script>
        document.addEventListener("DOMContentLoaded", () => {
            if (PSPDFKit !== undefined && typeof PSPDFKit === "object") {
                PSPDFKit.load({
                    container: "#pspdfkit",
                    document: "https://domain.tld/1962_December_etc.pdf",
                    // licenseKey: "foo-bar",
                });
            } else {
                console.error("PSPDFKit library not found");
            }
        });
    </script>

How feasible is this, using two dropdowns? I’m not familiar with JavaScript coding, though I can vaguely follow along and work things out slowly. (I am generally a good learner and have some experience using Python successfully with some help from StackOverflow to fill in the gaps in my understanding.)

We have about twenty years of newspaper issues (they’ve been combined by month), so this sort of solution is key to surfacing them without creating a million Wix pages.

What have you already tried:
My question hinges on the ability of Wix to allow Javascript messaging between otherwise isolated HTML embeds and their Velo framework. Since this is mostly really a Javascript question, I was hoping for some explanation to help work through the problem and illustrate a solution so that I can not just implement this bit of functionality but learn from the issue and grow my ability to mentally parse these sorts of problems. Link to the Velo documentation: (Working with the HTML iFrame element with Velo.) (https://dev.wix.com/docs/develop-websites/articles/wix-editor-elements/other-elements/html-i-frame-element/working-with-the-html-iframe-element#velo-working-with-the-html-iframe-element)

Are my assumptions correct about being able to use a pair of drop-down elements to augment the URL otherwise hardcoded into the HTML embed using Javascript messaging?

Additional information:
This project is to surface historical records to the public via the new website for a public library, particularly old local newspapers. Your help is invaluable and much appreciated.

Refreshing a cell with a custom CellRenderer in AgGrid causes it to flash

I’ve got the following AgGrid which has a button to multiply the price column value and a custom cell renderer in the first column.

<template>
  <button @click="convertPrice()">Convert</button>
  <AgGridVue
    :rowData="rowData"
    :columnDefs="colDefs"
    :gridOptions="defaultOptions"
    style="height: 500px"
    class="ag-theme-quartz"
    ref="grid"
  />
</template>

<script setup>
import { ref } from "vue";
import "ag-grid-community/styles/ag-grid.css";
import "ag-grid-community/styles/ag-theme-quartz.css";
import { AgGridVue } from "ag-grid-vue3";
import CustomRenderer from "./components/CustomRenderer.vue";

const grid = ref(null);
const rowData = ref([
  { make: "Tesla", model: "Model Y", price: 64950, electric: true },
  { make: "Ford", model: "F-Series", price: 33850, electric: false },
  { make: "Toyota", model: "Corolla", price: 29600, electric: false },
]);
const colDefs = ref([
  {
    colId: "make",
    cellRenderer: CustomRenderer,
    valueGetter: (params) => ({
      electric: params.data.electric,
      model: params.data.model,
    }),
  },
  { field: "model" },
  { field: "price" },
  { field: "electric" },
]);

let defaultOptions = {
  domLayout: "autoHeight",
  cellFlashDuration: 250,
  cellFadeDuration: 800,
  defaultColDef: {
    enableCellChangeFlash: true,
  },
};

function convertPrice() {
  const gridApi = grid.value.api;

  gridApi.forEachNode((row) => {
    row.data.price = row.data.price * 1.5;
    row.updateData(row.data);
  });
}
</script>

Working example

If you press the Convert button above the grid both price and make columns will flash as if both contents were updated, but only the price values are changing. Why is AgGrid flashing the first column if it doesn’t have any updates?

How can I prevent this from happening, so that the only cells that flash are in fact the ones that get updated?

Is using a dummy variable a good practice to trigger a function call reactively in Svelte

My code computes the number of characters the user has input. I allows users to save the entered text to localstorage and then when they visit the page the character count is shown. This is how I load the data:

// App.Svelte
<script>
  import Description from "./lib/Description.svelte";
  import Save from "./lib/Save.svelte";
  import { description, extraSections } from "./lib/store";
  import { onMount } from "svelte";

  onMount(() => {
    if (localStorage.getItem("extraSections") != null) {
      $extraSections = JSON.parse(localStorage.getItem("extraSections"));
    }
    $description = localStorage.getItem("description");
  });
</script>

<div class="container">
  <div class="inputs" id="inputs">
    <Title />
    <Description bind:text={$description} />
    {#each $extraSections as section (section.id)}
      <Description heading="Additional Section" bind:text={section.value} />
    {/each}
...

The problem I was facing was that my Description component wasn’t showing the count for the text entered from the localstorage since my function to compute that ran only on keyup:

// Description.Svelte
<script>
  let count = 0;
  let words = 0;
  function handleKeyUp() {
    count = text.length;
    words = text.trim().split(/s+/).length;
    if (text.length == 0) {
      words = 0;
    }
  }
  export let text = "";
  export let heading = "Description";
</script>

<div>
  <label for="title">{heading}</label>
  <textarea
    on:keyup={handleKeyUp}
    id="description"
    name="description"
    bind:value={text}
  ></textarea>
  <p>Characters: {count}</p>
  <p>Words: {words}</p>
</div>

To fix this I did this made the handleKeyUp function reactive to a change in value of text using the $: syntax:

<script>
  let count = 0;
  let words = 0;
  function handleKeyUp(dummy) {
    count = text.length;
    words = text.trim().split(/s+/).length;
    if (text.length == 0) {
      words = 0;
    }
  }
  // whenever text changes (when loading from localstorage)
  // we want to rerun handleKeyUp
  $: handleKeyUp(text);
  export let text = "";
  export let heading = "Description";
</script>

I wanted to ask if this is a valid practice in Svelte? Or is there a better way to do this? Thanks!

AG Grid – agSetColumnFilter needs dynamic API search integration

I am currently using AG Grid with React, and I am new to AG Grid implementation. I have searched through Google and Chat, but the suggestions I found did not help me. Furthermore, I need suggestions on how to trigger an API call every time the filter text changes instead of filtering the users locally. The getUsers method below currently makes an API call and gets a list of 20 users added to the filter. However, my new requirement is to make new API calls with filter text when the filter text changes and display the results in the filter.

Below My columnDef Code

{
    headerName: 'users',
    field: 'users',
    sortable: true,
    resizable: true,
    minWidth: 80,
    filter: 'agSetColumnFilter',
    filterParams: {
      values: getUsers
    },
    cellRenderer: (....)
  }

Sending SPL Token 2022 using Web3

I wrote the following line of java script code and ran it in nodejs, the script displays the ATA address and the balance amount, but when sending it always fails, it seems like I missed something, please provide a solution.

const { Connection, Keypair, PublicKey, sendAndConfirmTransaction, Transaction } = require('@solana/web3.js');
const { createCloseAccountInstruction, createTransferInstruction, TOKEN_2022_PROGRAM_ID } = require('@solana/spl-token');
const bs58 = require('bs58'); require("dotenv").config();

const connection = new Connection((process.env.PROVIDER_URL));
const feePayer = Keypair.fromSecretKey(bs58.decode((process.env.SIGNER_FEEPAYER)));
const alice = Keypair.fromSecretKey(bs58.decode((process.env.SIGNER_ALICE1)));
const kePenampung = new PublicKey((process.env.PENAMPUNG_USDT));

async function sendTokens() {
const tokenAccounts = await connection.getParsedTokenAccountsByOwner(alice.publicKey, { programId: TOKEN_2022_PROGRAM_ID });
console.log('Alamat ATA =', tokenAccounts.value[0].pubkey.toString());
const tokenAccountPubkey = new PublicKey(tokenAccounts.value[0].pubkey.toString());
let tokenAmount = await connection.getTokenAccountBalance(tokenAccountPubkey);
console.log(`Total Saldo : 0.0${tokenAmount.value.amount} USDT`);

const tx = new Transaction();
tx.add(createTransferInstruction(tokenAccountPubkey,kePenampung,alice.publicKey,tokenAmount.value.amount,6,))
.add(createCloseAccountInstruction(tokenAccountPubkey,feePayer.publicKey,alice.publicKey));



const signature = await sendAndConfirmTransaction(connection,tx,[feePayer, alice,]);}
sendTokens();

The script gives an error message like below, how should the code be written?

logs: [
    'Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [1]',
    'Program log: Instruction: Transfer',
    'Program log: Mint required for this account to transfer tokens, use `trans
fer_checked` or `transfer_checked_with_fee`',
    'Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 1326 of 40000
0 compute units',
    'Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb failed: custom program
 error: 0x1f'
  ]

thank you for your help

multiple-select js is getting disabled

I am using multiple-select js plugin for rendering the select elements. The problem is that currently I have a div which initially would be kept hidden. When I click on a button, this div would be displayed. Within the div I have the select element which needs to be rendered using multiple-select.

I am using this plugin multiple-select and the specific version is 1.2.1.

This is the jQuery code that I have used.

jQuery("select#inclusionIPRange").multipleSelect({
        width:350,
        filter:true,
        single:true
});

Then I have another button which is used to save the profile and we would use AJAX for it. After saving the profile we would again make this div hidden.
Now if I click on this add profile button again, the multiple select which worked before became unselectable. I tried refresh but instead of refreshing the existing multiple select it is creating a new multiple select. What could be the issue here?

I have attached the code for saving the profile.

if(window.XMLHttpRequest){
        req = new XMLHttpRequest();
    }else if(window.ActiveXObject){
        req = new ActiveXObject("Microsoft.XMLHTTP");
    }
    var url = "";
    url="Discovery_AddProfiles?SelectedSite="+document.form1.siteID.value+"&specialAjax=yes&Name="+encodeURIComponent(document.getElementById('inclusionIPName').value)+"&Option=8&param="+encodeURIComponent(parametervalue);
    req.open("GET", url, false);
    req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    req.send(null);
    if (req.readyState == 4) {
        if (req.status == 200) {
            inclusionIPFlag=1;
            printData(req.responseXML);
        } 
    }
    document.getElementById('InculsionIPListADD').innerHTML=document.getElementById('InculsionIPListADD').innerHTML+"<input type='hidden' id='hiddeninclusionName' name='hiddeninclusionName' value='"+document.getElementById("inclusionIPName").value+"'/>"     
    document.form1.hiddeninclusionNameCount.value=Number(document.form1.hiddeninclusionNameCount.value+1);
    cancelinclusionIPList();
    document.getElementById("inclusionIPName").value="";
    document.getElementById("inclusionList_Div").innerHTML="";
    ipRange="";
    individualIpList="";
    systemList="";
    domainList="";
    locationList="";
    baseLineRuleList ="";
    document.getElementById("rowCount").value=0;
    document.getElementById("inclusionIPListRangeStartIP").value="";
    document.getElementById("inclusionIPListRangeEndIP").value="";
    document.getElementById("inclusionIPListIndividualIP").value="";
    displaySelectedinclusionRange();

And functions are:

function printData(validXML){
    var cart = validXML.getElementsByTagName("Root")[0];
    var generated = cart.getAttribute("generated");
    var key;
    var value;
    if(generated > lastCartUpdate){
        lastCartUpdate = generated;
        var rowdata=cart.getElementsByTagName("Row-Data");
        var nrows = rowdata.length;
        var resp = "";
        for (var I = 0 ; I < nrows; I++) {
            var item = rowdata[I];
            value = item.getElementsByTagName("Row-Value")[0].firstChild.nodeValue;
            key = item.getElementsByTagName("Row-Value")[1].firstChild.nodeValue;
        }
    }
    var optn1 = document.createElement("OPTION");
    if(value.indexOf("-->")!=-1){
        optn1.text = value.split("-->")[1];
    }else{
        optn1.text = value;
    }        
    optn1.value = key.split("$")[1];        
    optn1.selected = "selected";
    document.form1.profile.options.add(optn1);
}

function cancelinclusionIPList(){
    document.getElementById('InculsionIPListADD').style.display = 'none';
}

function displaySelectedinclusionRange(){
    if(document.form1.inclusionIPRange.value == "Range"){
        document.getElementById('inclusionIPListRange').style.display='block'; 
        document.getElementById('inclusionIPListIndividual').style.display='none';
        document.getElementById('inclusionIPListDomain').style.display='none'; 
        document.getElementById('inclusionIPListLocation').style.display='none';
        document.getElementById('inclusionIPListNodeLevel').style.display='none';
        document.getElementById('inclusionIPListBaseLineRule').style.display='none';
    }else if(document.form1.inclusionIPRange.value=="Individual"){
        document.getElementById('inclusionIPListRange').style.display='none'; 
        document.getElementById('inclusionIPListIndividual').style.display='block';
        document.getElementById('inclusionIPListDomain').style.display='none'; 
        document.getElementById('inclusionIPListLocation').style.display='none';
        document.getElementById('inclusionIPListNodeLevel').style.display='none';
        document.getElementById('inclusionIPListBaseLineRule').style.display='none';
    }else if(document.form1.inclusionIPRange.value=="Domain"){
        document.getElementById('inclusionIPListRange').style.display='none'; 
        document.getElementById('inclusionIPListIndividual').style.display='none';
        document.getElementById('inclusionIPListDomain').style.display='block';
        document.getElementById('inclusionIPListLocation').style.display='none';
        document.getElementById('inclusionIPListNodeLevel').style.display='none';
        document.getElementById('inclusionIPListBaseLineRule').style.display='none';
    }else if(document.form1.inclusionIPRange.value=="Location"){
        document.getElementById('inclusionIPListRange').style.display='none'; 
        document.getElementById('inclusionIPListIndividual').style.display='none';
        document.getElementById('inclusionIPListDomain').style.display='none';
        document.getElementById('inclusionIPListLocation').style.display='block';
        document.getElementById('inclusionIPListNodeLevel').style.display='none';
        document.getElementById('inclusionIPListBaseLineRule').style.display='none';
    }else if(document.form1.inclusionIPRange.value=="Nodelevel"){
        document.getElementById('inclusionIPListRange').style.display='none'; 
        document.getElementById('inclusionIPListIndividual').style.display='none';
        document.getElementById('inclusionIPListDomain').style.display='none';
        document.getElementById('inclusionIPListLocation').style.display='none';
        document.getElementById('inclusionIPListNodeLevel').style.display='block';
        document.getElementById('inclusionIPListBaseLineRule').style.display='none';
        var url="HostsSearch.jsp?siteid="+document.form1.siteID.value+"&ModuleName=FileSystemTrawl&SelectListName=inclusionIPListSystemListNode&isNewUIPopupAvailable=yes";
        popUpPage(url,1050,330," ");
    }else if(document.form1.inclusionIPRange.value=="BaseLine Rules"){
        document.getElementById('inclusionIPListRange').style.display='none'; 
        document.getElementById('inclusionIPListIndividual').style.display='none';
        document.getElementById('inclusionIPListDomain').style.display='none';
        document.getElementById('inclusionIPListLocation').style.display='none';
        document.getElementById('inclusionIPListNodeLevel').style.display='none';
        document.getElementById('inclusionIPListBaseLineRule').style.display='block';
    }
}

Plugin grapesjs-preset-newsletter not found

I am trying to integrate grapejs to my wordpress site. I am following the below links

https://github.com/GrapesJS/preset-newsletter/tree/master

https://github.com/GrapesJS/blocks-basic

I loaded both js files

enter image description here

The editor is initialized and the js files are also loaded. But I am getting plugin not found.

enter image description here

Below is my js code

    jQuery(function(){
        setTimeout(()=>{
            var editor = grapesjs.init({
                container : '#gjs',
                plugins: ['grapesjs-preset-newsletter', "gjs-blocks-basic"],
                pluginsOpts: {
                'grapesjs-preset-newsletter': {
                    // options
                },
                "gjs-blocks-basic":{
        
                }
                }
            });
        

        })
    
    })

This issue was addressed in 2018, but they suggest to go with requirejs. I tried to include requirejs into my wordpress app and I am getting lots of error.

https://github.com/GrapesJS/grapesjs/issues/902

Has anyone tried to use GrapeJS plugins on plain JS.

Which is the proper way to write API services using composables?

In Vue 2 I’m used to write API services like this

// user.service.js

import axios from 'axios'

const BASE_URL = '/users'

function getUsers() {
  return axios.get(BASE_URL)
}

function addUser(user) {
  return axios.post(BASE_URL, user)
}

export default { getUsers, addUser }

and use them like this

<!-- UsersView.vue -->

<template>
  <!-- display users -->
</template>

<script>
import UserService from '@/services/user.service'

export default {
  data() {
    usersAreLoading: false,
    users: []
  },
  created() {
    this.usersAreLoading = true

    UserService.findUsers().then(
      ({ data }) => {
        this.users = data
        this.usersAreLoading = false
      },
      (err) => {
        // show the error
        this.usersAreLoading = false
      }
    )
  }
}
</script>

but in Vue 3 it’s possible to extract logic and state thanks to composables. Composables should look something like this.

I’m kinda new to composables, so I don’t really know how to properly translate the above example from “API service” to “API composable”. I would do something like this

// user.service.js

import { ref } from 'vue'
import axios from 'axios'

const BASE_URL = '/users'

function useGetUsers() {
  const users = ref([])
  const usersAreLoading = ref(false)
  
  const getUsers = () => {
    usersAreLoading.value = true

    return axios.get(BASE_URL).then(
      ({ data }) => {
        users.value = data
        usersAreLoading.value = false
      },
      (err) => {
        // do something with the error
        usersAreLoading.value = false
      }
    )
  }

  getUsers()

  return { users, usersAreLoading, getUsers }
}

function useAddUser(user) {
  const addedUser = ref()
  const isLoading = ref(false)

  const addUser = (user) => {
    isLoading.value = true

    return axios.post(BASE_URL, user).then(
      ({ data }) => {
        addedUser.value = data
        isLoading.value = false
      },
      (err) => {
        // do something with the error
        isLoading.value = false
      }
    )
  }

  addUser(user)

  return { addedUser, isLoading, addUser }
}

export default { useGetUsers, useAddUser }

and use it like this

<!-- UsersView.vue -->

<template>
  <!-- display users -->
</template>

<script setup>
import UserService from '@/services/user.service'

const { users, usersAreLoading } = UserService.useGetUsers()
</script>

As I said I’m kinda new to this, therefore I don’t really know if this is the proper way of doing in Vue 3. My naming conventions might be wrong, this could also be a worst practice.

So, what is the proper way to deal with API services in Vue 3?

No routes matched location ‘/shop/hats’

I’m working on a React application with routing using React Router and state management using Redux. I’m encountering an issue where navigating to /shop/hats results in a console error: “No routes matched location '/shop/hats'

App.js:

import React from "react";
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import ShopPage from "./pages/shop/shop.component";
import HomePage from "./pages/homepage/homepage.component";
import CheckoutPage from "./pages/checkout/checkout.component";
import SignInAndSignUpPage from "./pages/sign-in-and-sign-up/sign-in-and-sign-up.component";

const App = () => (
  <Router>
    <Routes>
      <Route path="/" element={<HomePage />} />
      <Route path="/shop/*" element={<ShopPage />} />
      <Route path="/checkout" element={<CheckoutPage />} />
      <Route path="/signin" element={<SignInAndSignUpPage />} />
    </Routes>
  </Router>
);

export default App;

ShopPage.js:

import React from "react";
import { Routes, Route } from 'react-router-dom';
import CollectionsOverview from '../../components/collections-overview/collections-overview.component';
import CollectionPage from "../collection/collection.component";

const ShopPage = () => (
  <div className="shop-page">
    <Routes>
      <Route path="/" element={<CollectionsOverview />} />
      <Route path="/:categoryId" element={<CollectionPage />} />
    </Routes>
  </div>
);

export default ShopPage;

directory.reducer.js:

const INITIAL_STATE = {
  sections: [
    {
      title: 'hats',
      imageUrl: 'https://i.ibb.co/cvpntL1/hats.png',
      id: '1',
      linkUrl: 'shop/hats'
    },
    {
      title: 'jackets',
      imageUrl: 'https://i.ibb.co/px2tCc3/jackets.png',
      id: '2',
      linkUrl: 'shop/jackets'
    },
    // ... other sections
  ]
};

const directoryReducer = (state = INITIAL_STATE, action) => {
  switch(action.type) {
    default:
      return state;
  }
};

export default directoryReducer;

shop.reducer.js:

import SHOP_DATA from "./shop.data";

const INITIAL_STATE = {
  collections: SHOP_DATA
}

const shopReducer = (state = INITIAL_STATE, action) => {
  switch(action.type) {
    default:
      return state;
  }
};

export default shopReducer;

shop.selectors.js:

import { createSelector } from "reselect";

const selectShop = state => state.shop;

export const selectCollections = createSelector(
  [selectShop],
  shop => shop.collections
);

export const selectCollection = collectionUrlParam => (
  createSelector(
    [selectCollections],
    collections => collections.find(
      collection => collection.id === COLLECTION_ID_MAP[collectionUrlParam]
    )
  )
);

When navigating to routes such as "/shop/hats", "/shop/jackets", or "/shop/sneakers", and others remaining. I expected the application to display the CollectionPage component with the corresponding images and data for each collection.

How to maintain the continuity in connecter lines for zero values in a Plotly.js Waterfall Chart?

I’m working with a Waterfall chart in Plotly.js and facing an issue where the connecting lines between bars break when any of the values are zero. I want to maintain the continuity of the connecting lines even if there are zero values in the dataset.

Here’s a simplified version of my code:

const capex = this.data["CapEx ($/Kg)"] || 0;
const stackReplacements = this.data["Stack Replacements ($/Kg)"] || 0;
const electricityCost = this.data["Electricity Cost ($/Kg)"] || 0;
const fixedOpex = this.data["Fixed Opex ($/Kg)"] || 0;
const ptc = this.data["Production Tax Credit ($/Kg)"] || 0;
const lcoh = capex + stackReplacements + electricityCost + fixedOpex;
const post45VLCOH = lcoh + ptc;    
const yValues = [capex, stackReplacements, electricityCost, fixedOpex, lcoh, ptc, post45VLCOH];
const smallValue = 0.001; // Small value to replace zero
const adjustedYValues = yValues.map(value => value === 0 ? smallValue : value);
const textValues = yValues.map(value => value.toFixed(2));

const trace = {
  type: 'waterfall',
  name: 'Waterfall Chart',
  orientation: 'v',
  measure: ['relative', 'relative', 'relative', 'relative', 'total', 'relative', 'total'],
  x: ['Capex', 'Cell service', 'Electricity', 'Fixed Opex', 'LCOH', 'PTC', 'Post-45V LCOH'],
  y: adjustedYValues,
  connector: { 
    line: { 
      color: 'rgba(0, 0, 0, 0.5)',
      width: 1,
      dash: 'solid' // Set the line style
    }
  },
  decreasing: { marker: { color: '#F066AC' } },
  increasing: { marker: { color: '#046CC4' } },
  totals: { marker: { color: '#3C8454' } },
  text: textValues,
  textposition: 'outside',
};

const layout = {
  xaxis: {
    fixedrange: true,
  },
  yaxis: {
    title: 'Contributions to LCOH ($/Kg)',
    fixedrange: true,
  },
  showlegend: false,
  dragmode: false,
  bargap: 0.3,
  margin: { t: 50, b: 50, r: 50, l: 50 },
};

const options = {
  responsive: true,
  displayModeBar: true,
  modeBarButtonsToRemove: ['select2d', 'lasso2d'],
  toImageButtonOptions: {
    format: 'png',
    filename: 'LCOH Breakdown',
    height: 500,
    width: 700,
    scale: 1,
  },
  displaylogo: false,
};

Plotly.newPlot(this.elementRef.nativeElement, [trace], layout, options);

When I run this code, the connecting lines break at the “Zero” value. Is there a way to modify the chart or the data so that the lines remain continuous throughout the chart, regardless of zero values? Any suggestions or workarounds would be greatly appreciated!