Keep getting unique “key” error message even though a key has been added

I have created a .map for my fetched products from shopify but for some reason, even though everything is working, I am getting this error message that says Each child in a list should have a unique "key" prop. even though my <div> has a key added to it. Here is my code below.

Products.jsx:

const fetchProducts = async () => {
  const response = await fetch(`https://<MyShopifyStore>.myshopify.com/api/2024-10/graphql.json`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Shopify-Storefront-Access-Token': ''
    },
    body: JSON.stringify({
      query: `
        {
          products(first: 10) {
            edges {
              node {
                title
                descriptionHtml
                images(first: 1) {
                  edges {
                    node {
                      src
                    }
                  }
                }
              }
            }
          }
        }
      `,
    }),
  });

  const { data } = await response.json();
  return data.products.edges;
};

const Products = () => {
  const [productsList, setProductsList] = useState([]);
  
  useEffect(() => {
    const getProducts = async () => {
      const productData = await fetchProducts();
      setProductsList(productData)
    };

    getProducts();
  }, []);

// Problem is below
  
  return (
    <div className="product-container">
      {
        productsList.map(({ node }) => (
          <div key={node?.id}>
            <h2>{node.title}</h2>
            <div dangerouslySetInnerHTML={{ __html: node.descriptionHtml }} />
            {node.images.edges.length > 0 && (
              <img src={node.images.edges[0].node.src} alt={node.title} />
            )}
          </div>
        ))
      }
    </div>
  )
}

export default Products

What am I doing wrong?

Using Javascript to make a video player [closed]

I am using Javascript to make a video player that plays a video. All the codes seem to be added right. But the code does not seem to work. Here it is:

HTML

<video  id="myVideo">
    <source src="coolfirst2hours.mp4" type="video/mp4">
  </video>
<button id="playVid" type="button">PLAY</button>

Javascript

// Get the video and play button elements
const videoElem = document.getElementById('myVideo');
const playButton = document.getElementById('playVid');

// Add event listener to play button
playButton.addEventListener('click', () => {
  videoElem.play();
});

NextJS 15 canary & authjs GetSession

I’d like to recover my session with the dynamicIO enabled, which allows the use cache to be added. However, I’m facing multiple errors. I’ve tried several solutions based on what the docs suggest, such as adding an auxiliary function (GetSession) with a use cache or caching the page directly, but the errors persist.

When I use await auth directly in my page I get this error: [ Server ] Error: Route “/”: A component has accessed data, headers, parameters, searchParams, or a short-lived cache without a Suspense limit or “use cache” above. We haven’t yet added the exact line number to the error messages, but you can see which component in the stack below.

But when I use the auxiliary function (GetSession) I get a different error: [Cache ] Error: Route / used “headers” inside “use cache”. Access to dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function, use “headers” outside the cached function and pass the required dynamic data as an argument.

page.tsx

import { auth } from "@/auth";
import { GithubLoginForm } from "@/components/auth-form/github-login-form";

// const GetSession = async () => {
//   "use cache";
//   const session = await auth();
//   console.log(session);
//   return session;
// };

export default async function Home() {
  const session = (await auth());
  // const session = await GetSession();
  console.log(session);
  return (
    <div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
      <main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
        <GithubLoginForm />
      </main>
    </div>
  );
}

CORS Issue: Access-Control-Allow-Origin Header Set to Wildcard * Even When Specific Origins Are Allowed

I am developing a web application using Express.js for the backend and Elementor Frontend, which uses a plugged script to make api calls to the backend. I have implemented CORS (Cross-Origin Resource Sharing) to allow requests from specific origins. However, I’m encountering an issue where the Access-Control-Allow-Origin header is set to *, even when I have specified specific allowed origins.

When I attempt to fetch user data from my backend API, I receive a CORS error in the browser console, stating:

Access to fetch at 'https://b09b-2001-999-580-23c7-cea6-afdc-e9e6-cf43.ngrok-free.app/test' from origin 'https://talopakettiin.fi' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.

The script works for one page just fine, redirects to another one where the script doesn’t seem to gain access giving the above error, even though an origin is specified.

Here is the index.js code:

import express from "express";
import bodyParser from "body-parser";
import cors from "cors";
import dotenv from "dotenv";
import charRoutes from "./routes/charRoutes.js";
import userRoutes from "./routes/userRoutes.js";
import formRoutes from "./routes/formRoutes.js";
import wordPressRoutes from "./routes/wordPressRoutes.js";
import cookieParser from "cookie-parser";

dotenv.config();
const app = express();
const PORT = process.env.PORT || 8000;

// Define allowed origins
const allowedOrigins = [
  "https://talopakettiin.fi",
  "https://b09b-2001-999-580-23c7-cea6-afdc-e9e6-cf43.ngrok-free.app",
];

// CORS options
const corsOptions = {
  origin: allowedOrigins, // Directly allow this origin
  methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS",
  allowedHeaders:
    "Origin, X-Requested-With, Content-Type, Accept, Authorization",
  credentials: true,
};

// Middleware
app.options("*", (req, res) => {
  const origin = allowedOrigins.includes(req.headers.origin)
    ? req.headers.origin
    : null;
  console.log("Here");
  if (origin) {
    res.setHeader("Access-Control-Allow-Origin", origin);
    res.setHeader(
      "Access-Control-Allow-Methods",
      "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS"
    );
    res.setHeader(
      "Access-Control-Allow-Headers",
      "Origin, X-Requested-With, Content-Type, Accept, Authorization"
    );
    res.setHeader("Access-Control-Allow-Credentials", "true");
    res.sendStatus(204); 
  } else {
    res.sendStatus(403); 
  }
});
app.use(cors(corsOptions));
app.use(cookieParser());

// Body Parser Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

// Route Handlers
app.use("/chars", charRoutes);
app.use("/user", userRoutes);
app.use("/forms", formRoutes);
app.use("/wordpress", wordPressRoutes);
app.get("/test", (req, res) => {
  console.log("Test endpoint hit");
  res.json({ message: "This is a test." });
});

// Error Handling Middleware
app.use((err, req, res, next) => {
  console.error("Error is here: ", err.stack);
  res.status(500).json({ error: "Something went wrong!" });
});

app.use((req, res, next) => {
  res.on("finish", () => {
    console.log("Response headers:", res.getHeaders());
  });
  next();
});

// Start the server
app.listen(PORT, () =>
  console.log(`Server is running on http://localhost:${PORT}`)
);
 

and here is the script that won’t execute

<!DOCTYPE html>
<html>
  <body>
    <script>
      document.addEventListener("DOMContentLoaded", async function () {
        console.log("At welcome page, Fetching User");

        try {
          const response = await fetch(
            "https://b09b-2001-999-580-23c7-cea6-afdc-e9e6-cf43.ngrok-free.app/test",
            {
              method: "GET",
              credentials: "include",
            }
          );

          console.log("This is the response", response);

          if (response.ok) {
            const contentType = response.headers.get("Content-Type");
            if (contentType && contentType.includes("application/json")) {
              const userData = await response.json();
              document.getElementById(
                "welcomeMessage"
              ).innerText = `Welcome, ${userData.username}!`;
            } else {
              console.error("Unexpected response type:", contentType);
              alert("Unexpected response type from server.");
            }
          } else {
            alert("Failed to fetch user data: " + response.statusText);
          }
        } catch (error) {
          console.error("Error fetching user data:", error.message);
        }
      });
    </script>
  </body>
</html>

Ajax Error 400 (Bad Request) Handling in Asp.net core Razor Pages

I have a piece of code that collects the values of the inputs in the HTML page and posts them to a handler using AJAX.

However, when sending by AJAX I get a 400 Bad Request error and the handler is not called.

Where I am going wrong?

$("#sendButton").click(function (event) {
  event.preventDefault();
  var userId = $("#userId").val();
  var userName = $("#username").val();
  var message = $("#message").val();
  var groupId = $("#groupid").val();
  var attachFile = $("#f").get(0).files;

  var formData = new FormData();
  formData.append("FileAttach", attachFile);
  formData.append("UserId", userId);
  formData.append("UserName", userName);
  formData.append("Message", message);
  formData.append("GroupId", groupId);
     
  $.ajax({
    type: "POST",
    url: "Index?handler=SendMessage",
    data: formData,
    encyType: "multipart/form-data",
    processData: false,
    contentType: false
  });

  $("#message").val('').focus();
    const element = document.getElementById("preChat");
    element.scrollIntoView({ block: "end" });
  });

This is handler code

public class InsertChatViewModel
{
  public string UserId { get; set; }
  public string GroupId { get; set; }
  public string Message { get; set; }
  public string UserName { get; set; }
  public IFormFile FileAttach { get; set; }
}

public IActionResult OnPostSendMessage([FromForm] InsertChatViewModel model)
{
  // It is not called here
  return Page();
}

Error Image
Everything not cheanged when change ajax parameters

Unable to order based on joined table when using sequelize.literal()

I’m trying to order a set of paged results based on the existence of many to many relationship. I can write a query by hand that does this, but I’m trying to figure out how to do this the sequelize way.

My Code:

const include = [];
const page = 1;
const rowsPerPage = 30;
const sortByTagId = 1134;

include.concat([
  { association: User.Tags }, 
  { association: User.Location },
  { association: User.Privileges }
]);

await User.findAll({
  include,
  order: [
    fn('bool_or', literal(`"userTags"."tagId" = ${sortByTagId}`)),
    'ASC',
  ],
  group: ['UserModel.id'],
  offset: rowsPerPage * (page - 1),
  limit: rowsPerPage,
});

Sequelize then produces the following query:

SELECT 
  "id", 
  "createdAt",
  "deletedAt",
  "updatedAt",
  "firstName",
  "lastName",
  "suffix",
  "email",
  "phone"
FROM "users" AS "UserModel" 
WHERE ("WorkerModel"."deletedAt" IS NULL) 
GROUP BY "WorkerModel"."id" 
ORDER BY bool_or("userTags"."tagId" = 1134) ASC
LIMIT 30 
OFFSET 0;

This query is invalid and produces the following error:
missing FROM-clause entry for table "userTags"

I suspect sequelize is omitting my association because it doesn’t see any references to it but is ignoring my call to literal() I suspect that I might be able to solve this problem by getting rid of literal() but I haven’t been able to figure out how to do so while still specifying a value for tagId.

I’ve tried experimenting with raw: true but this doesn’t help in any way.

FYI: The code above is an approximation of something more complex from a proprietary codebase, I’ve modified it to make it easier to read and not proprietary. I apologize for any mistakes I might have made and will try to edit accordingly if necessary.

Cloudinary video doesn’t load when I navigate to my website from Instagram description Link

I have a video issue in my Next.js project. Specifically, when I navigate to my website from my Instagram page, the Cloudinary video doesn’t load. I’ve noticed this issue occurs only on iOS mobile devices.

 const url = getCldVideoUrl(
    {
      width: 1920,
      height: 1080,
      src: 'https://res.cloudinary.com/CLOUD_NAME/VIDEO_SRC.mp4',
      quality: 'auto',
    },
    {
      cloud: {
        cloudName: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,
        apiKey: process.env.CLOUDINARY_API,
        apiSecret: process.env.CLOUDINARY_API_SECRET,
      },
    },
  );

return   <video
        src={url}
        autoPlay
        loop
        muted
        playsInline
        controls={false}
        className="absolute inset-0 size-full object-cover"
      >
        Your browser does not support the video tag.
      </video>

Can someone please explain what the potential problems might be? Is it related to CORS or something else?

Should I even use sync loops in JS? [closed]

All is in the title: should we even use sync loops when we have Promise.all ? Here is an example

const values = ["foo", "bar", "baz"];

const upperValuesMap = values.map((s) => s.toUpperCase());
const upperValuesPromises = await Promise.all(values.map(async (s) => s.toUpperCase())); // seems faster, but is it really ?

Is the method with Promise.all always faster than the loop ? If not, what are the case in which we would prefer a loop ?

How can dark mode be saved on all pages of the site?

I’ve made a dark mode button, and my code saves the user’s preference (if they’ve chosen dark mode, when they refresh the page it will remain in dark mode). But when he goes to another page the chosen mode doesn’t apply, the other pages go back to standard mode.

How do I make it so that, regardless of the page, if the user chooses dark mode, all pages are saved in that mode?

let trilho = document.getElementById('trilho');
let body = document.querySelector('body');

// Check local storage for the user's theme preference
const theme = window.localStorage.getItem("theme");

// Set the initial theme based on localStorage
if (theme === "dark") {
    body.classList.add("dark");
    trilho.classList.add("dark"); // Ensure the button reflects dark mode
} else {
    trilho.classList.remove("dark"); // Ensure the button reflects light mode
}

// Add event listener to toggle dark mode
trilho.addEventListener('click', () => {
    trilho.classList.toggle('dark');
    body.classList.toggle('dark');

    // Update localStorage with the current theme
    if (body.classList.contains("dark")) {
        window.localStorage.setItem("theme", "dark");
        trilho.classList.add("dark"); // Dark mode button
    } else {
        window.localStorage.setItem("theme", "light");
        trilho.classList.remove("dark"); // Light mode button
    }
});

Display a picture taken with my android in my webapp

I’m trying to retrieve a photo taken in my android application and display it in my webview project; my web pages are local file:///android_asset/

with this function snapet i can launch the camera

   @JavascriptInterface 
    public void prendreUnePhoto() {
                        
                  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                  pickMedia.launch(intent);
            }

// the callbak

  ActivityResultLauncher<Intent> pickMedia =
                    registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result-> {
                       
                        if (result.getResultCode() == RESULT_OK && result.getData() !=null) {
                            Log.d("PhotoPicker", "Selected URI: " + result.getData().getExtras());
                            Bundle bundle = result.getData().getExtras();
                            Bitmap bitmap = (Bitmap) bundle.get("data");
                            b= (Bitmap) bundle.get("data");
                           
                        } else {
                            Log.d("PhotoPicker", "No media selected");
                        }
                    });

Here i convert the photo taken in Base64

@JavascriptInterface
        public String getPhoto() {
            ByteArrayOutputStream stream = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.PNG, 10, stream);
            byte[] bytesArray = stream.toByteArray();
            String str = Base64.encodeToString(bytesArray, Base64.DEFAULT);


            return str;
        }

then from the webapp side (webview client) i can launch the camera (JAVASCRIPT SNIPPET)

 but.addEventListener("click", () => {
         
          v = Android.prendreUnePhoto();
          Android.getPhoto();
         
        });

I would like to know how i can display the photo taken by the camera directly on a div on my client (webview)

I’m oblige to create another button then I can call the function getPhoto() and by this way i can display the photo taken

 verifier.addEventListener("click", () => {
          var b = Android.getPhoto();
          var src = "data:image/jpeg;base64," + b;
          $("#img").append(`<img src="${src}"/>`);
          console.log(src);
          //alert(typeof b);
        });

Extract metadata from font dynamically

DESCRIPTION

I’m creating the project which has ability to set custom fonts and apply them to the text. I have default fonts and custom fonts.

I want to add ability for users to add their own fonts from links by the scheme:

  1. User inputs/inserts the link to the font in a format https://link.com/fonts/Arial/arial-bold.ttf
  2. Program takes the link and extracts metadata from the font, namely: (name, font-weight, font-style)

SOME CODE

Font type:

type TFontType = 'recent' | 'default' | 'custom';

type TFontStyle = {
   weight: number | string;
   style: string;
   link?: Nullable<string>;
};

type TFont = {
   id?: string;
   family: string;
   link?: Nullable<string>;
   styles: TFontStyle[];
   type?: TFontType;
};

type TFontController = {
   selected: Nullable<TFont>;
   fonts: TFont[];
};

**The function I already tried to use:**
export async function loadFontFromURL(url: string, fontFamily: string) {
   try {
      const font = new FontFace(fontFamily, `url(${url})`);
      await font.load();
      document.fonts.add(font);

      const fontStyle = font.style || 'normal';
      const fontWeight = font.weight || '400';

      return {
         fontFamily,
         fontStyle,
         fontWeight,
      };
   } catch (error) {
      console.error({ error });
      console.error(`Failed to load font: ${fontFamily} from URL: ${url}`);
      return null;
   }
}

In fact it extracts some styles but they are incorrect.

For example I tried to load Arial Bold Italic font and got:

Problem show

So, no matter what I upload it will show me normal normal normal…

In addition this way to fetch the font won’t show me font family which is also required

How to prevent clicking outside a popover HTML element?

I’m using a popover element to display a loading spinner while performing an asynchronous action like so

function doAsyncStuff() {
  const loadingScreenHtmlElement = document.getElementById("loading-screen");

  loadingScreenHtmlElement.showPopover();

  setTimeout(() => {
    loadingScreenHtmlElement.hidePopover();
  }, 3000);
}
#loading-screen::backdrop {
  background: rgba(0, 0, 0, 0.7);
}
<div popover id="loading-screen">
  <div>Loading spinner goes here</div>
</div>

<button onclick="doAsyncStuff()">Do it</button>

As you can see there are 2 problems

  • One can close the shown popover by clicking outside
  • One can access elements “behind” the popover, e.g. clicking the button again

Based on How to disable clicking inside div adding pointer-events:none to #loading-screen did not help. How do I fix this?

How to clear Twitter card cache when the URL is shared without HTTP in front?

These links when shared on Twitter produce different previews: https://###.com and http://###.com and ###.com.

I’m not sure if it was placebo or not, but when I added it to bitly and ran on Twitter card validator and then shared it showed the updated version.

The issue I’m having is that ###.com is still cached. Twitter card validator requires http in front, and I guess bitly and similar redirect websites always prefix with http.

So, I don’t know what I can possibly do to request an update on ###.com, is there a way to do it?

Firebase Functions v2 triggers and the ES6 syntax

I’m developing an Android Cordova app that uses Firebase services (Auth, Firestore) on VS Code (Windows 11 Pro).

I use pure JS and the latest modular version of Firebase SDK (no plugins): in my Cordova project I’ve installed all the necessity Node modules for working in this way (I do not want to use CDN for accessing API). I also use Firebase emulators.

I want to use Firebase Functions v2, using ES6 syntax, modifying appropriately the functions/package.json file, adding "type": "module";
now, if you can modify the functions/package.json in that manner it means that you can use the ES6 syntax officially.

The problem is that I am not able to figure out how to use the triggers in ES6 syntax on the official docs neither using ai chatbots and VS Code intellisense does not give any clue. For example, the onCreate or onDelete triggers for Auth, suggested by all the ai chatbots and contemplated on the official documentation, seems that not exist in ES6 syntax or I was not able to find them.

In other words: how to use Functions v2 Auth and Firestore triggers in pure JS and ES6 syntax? Where are they? How are they called?

The sankey diagram made with d3 fails and has overlapping nodes

I have a problem making a sankey chart with d3. It has to be in an html file without using frameworks so that it can be sent without further ado. The problem is that nodes 4 and 5 are overlapping and after node 7 there is a space that should be occupied by the nodes above. I don’t know if it is a problem of percentages in the values of the links, but they are fine because it is more or less the size I want them to be. If in addition to solve that you help me to put the text at the top left inside the nodes, great. This is the code:
`

<!DOCTYPE html>
<html lang="es">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Gráfico de Sankey</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3-sankey/0.12.3/d3-sankey.min.js"></script>

    <style>
        body,
        html {
            margin: 5px;
            padding: 10px;
            overflow: hidden;
            font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif, sans-serif;
            display: flex;
            flex-direction: column;
        }

        h1 {
            color: #004B87;
            margin: 20px 0;

        }

        #container {
            width: 100vw;
            height: 80vh;
        }

        .link {
            fill: none;
            stroke-opacity: 0.6;
        }

        .node text {
            fill: #000000;
            font-size: 16px;
            text-anchor: middle;
        }

        .node rect {
            cursor: default;
            fill-opacity: .9;
        }

        .tooltip {
            position: fixed;
            background-color: #ccecf1;
            border: 1px solid #ccc;
            padding: 10px;
            border-radius: 4px;
            pointer-events: none;
            font-size: 16px;
            display: none;
            max-width: 300px;
            max-height: 130px;
        }

        .link:hover {
            stroke-opacity: 1.5;
        }
    </style>
</head>

<body>
    <h1 style="color: #004B87;">Journey de Use Case Discovery</h1>
    <div id="container">
        <svg></svg>
        <div class="tooltip"></div>
    </div>

    <script>

        const container = document.getElementById("container");
        const width = container.offsetWidth;
        const height = container.offsetHeight;

        const svg = d3.select("svg")
            .attr("viewBox", `0 0 ${width} ${height}`)
            .attr("preserveAspectRatio", "xMidYMid meet");

        const sankey = d3.sankey()
            .nodeWidth(130)
            .nodePadding(10)
            .size([width, height]);
        const data = {
            "nodes": [
                { "node": 0, "name": "0", "level": 0 },
                { "node": 1, "name": "1", "level": 1 },
                { "node": 2, "name": "2", "level": 1 },
                { "node": 3, "name": "3", "level": 1 },
                { "node": 4, "name": "4", "level": 2 },
                { "node": 5, "name": "5", "level": 2 },
                { "node": 6, "name": "6", "level": 2 },
                { "node": 7, "name": "7", "level": 2 },
                { "node": 8, "name": "8", "level": 2 },
                { "node": 9, "name": "9", "level": 2 },
                { "node": 10, "name": "10", "level": 2 },
                { "node": 11, "name": "11", "level": 3 },
                { "node": 12, "name": "12", "level": 3 },
                { "node": 13, "name": "13", "level": 3 },
                { "node": 14, "name": "14", "level": 4 }
            ],
            "links": [
                { "source": 0, "target": 1, "value": 0.57, "message": "hola" },
                { "source": 0, "target": 2, "value": 0.350, "message": "hola" },
                { "source": 0, "target": 3, "value": 0.14, "message": "hola" },
                { "source": 1, "target": 4, "value": 0.19195, "message": "hola" },
                { "source": 1, "target": 5, "value": 0.15735, "message": "hola" },
                { "source": 1, "target": 6, "value": 0.12365, "message": "hola" },
                { "source": 1, "target": 7, "value": 0.09705, "message": "hola" },
                { "source": 2, "target": 4, "value": 0.1527, "message": "hola" },
                { "source": 2, "target": 8, "value": 0.1164, "message": "hola" },
                { "source": 2, "target": 9, "value": 0.0809, "message": "hola" },
                { "source": 3, "target": 10, "value": 0.14, "message": "hola" },
                { "source": 5, "target": 11, "value": 0.1446, "message": "hola" },
                { "source": 6, "target": 12, "value": 0.133, "message": "hola" },
                { "source": 7, "target": 12, "value": 0.09705, "message": "hola" },
                { "source": 8, "target": 11, "value": 0.1164, "message": "hola" },
                { "source": 9, "target": 13, "value": 0.0809, "message": "hola" },
                { "source": 13, "target": 14, "value": 0.0809, "message": "hola" }
            ]
        }


        const graph = sankey(data);

        graph.nodes.forEach(node => {
            node.x0 = node.level * (width / 5);
            node.x1 = node.x0 + sankey.nodeWidth();
        });



        const tooltip = d3.select(".tooltip");

        svg.append("g")
            .selectAll(".link")
            .data(graph.links)
            .join("path")
            .attr("class", "link")
            .attr("d", d3.sankeyLinkHorizontal())
            .style("stroke-width", d => Math.max(1, d.width))
            .style("stroke", d => d.source.index === 0 ? "#9EC2E5" : "#FFCC80")
            .on("mouseover", (event, d) => {
                tooltip.style("display", "block")
                    .html(d.message)
                    .style("left", `${event.pageX + 5}px`)
                    .style("top", `${event.pageY - 20}px`);
            })
            .on("mouseout", () => tooltip.style("display", "none"));

        const node = svg.append("g")
            .selectAll(".node")
            .data(graph.nodes)
            .join("g")
            .attr("class", "node")
            .attr("transform", d => `translate(${d.x0},${d.y0})`)

        node.append("rect")
            .attr("height", d => d.y1 - d.y0)
            .attr("width", sankey.nodeWidth())
            .style("fill", d => d.level === 0 ? "#4E79A6" : d.level === 1 ? "#9FCBE8" : "#F28E2C")
            .append("title")
            .text(d => d.name);

        node.append("text")
            .attr("x", sankey.nodeWidth() / 2)
            .attr("y", d => (d.y1 - d.y0) / 2)
            .attr("dy", "0.35em")
            .text(d => d.name);
    </script>
</body>

</html>

`

I have a problem making a sankey chart with d3. It has to be in an html file without using frameworks so that it can be sent without further ado. The problem is that nodes 4 and 5 are overlapping and after node 7 there is a space that should be occupied by the nodes above. If in addition to solve that you help me to put the text at the top left inside the nodes, great. This is the code: