Reconciling AWS Lambda SES node.js w/ Front End Javascript for emailing

Basically I cannot get a front end website message box to send message to an email as it should. I imagine I could easily do with with normal web hosting but I am using Amazon and trying to learn a lot all at once.

  • On AWS I’ve setup an IAM permission policy to include AmazonFullSES
  • I created a Lambda_function using SES, Simple Email Service, and the node.js code from Amazon’s documentation
  • I setup an API Gateway, linked to that Lambda_function
  1. API Gateway has POST configured
  2. API Gateway has OPTIONSs and lambda as a proxy setup
  3. API Gateway has CORS configured.

The lambda_function file successfully sends out test emails, the status is 200, and the email is received.

The API Gateway likewise tests successfully with status 200.

This is my front end javascript:

document.getElementById('contactForm').addEventListener('submit', async function(e) {
    e.preventDefault();

    const formData = new FormData(this);
    const data = {
        name: formData.get('name'),
        email: formData.get('email'),
        message: formData.get('message')
    };

    const apiUrl = 'KEY###';

    try {
        const response = await fetch(apiUrl, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({
                subject: "Message from My Website",
                body: data,
                source: "EMAIL###",
                recipient: "EMAIL###"
            })
        });

        const result = await response.json();

        if (response.ok) {
            alert('Email sent successfully!');
            document.getElementById('contactForm').reset();
        } else {
            alert('Failed to send email. Please try again.');
        }
    } catch (error) {
        console.error('Error:', error);
        alert('There was an error sending your message. Please try again later.');
    }
});

Then this is the code I am using on AWS Lambda as a node.js(20) function:

import { SESClient, SendEmailCommand } from "@aws-sdk/client-ses";
const ses = new SESClient({ region: "us-west-1" });

export const handler = async (event) => {
    // Parse the incoming JSON payload
    const requestBody = JSON.parse(event.body);
    const userData = JSON.parse(requestBody.body);

    const command = new SendEmailCommand({
        Destination: {
            ToAddresses: [requestBody.recipient], // Using the recipient from the request
        },
        Message: {
            Body: {
                Text: {
                    // Constructing email body with user data
                    Data: Name: ${userData.name}nEmail: ${userData.email}nMessage: ${userData.message}
                },
                Html: {
                    Data: <html><body><h1>New message from ${userData.name}</h1><p>Email: ${userData.email}</p><p>Message: ${userData.message}</p></body></html>
                }
            },
            Subject: { 
                Data: requestBody.subject // Using the subject from the request
            }
        },
        Source: requestBody.source // Using the source from the request
    });

    try {
        const response = await ses.send(command);
        console.log("Email sent!", response);
        return {
            isBase64Encoded: false,
            statusCode: 200,
            headers: {
                "Content-Type": "application/json",
                "Access-Control-Allow-Origin": "*" 
            },
            body: JSON.stringify({
                message: "Email sent successfully!",
                responseId: response.MessageId // Returning the SES MessageId
            })
        };
    } catch (error) {
        console.error("Failed to send email.", error);
        return {
            isBase64Encoded: false,
            statusCode: 500,
            headers: {
                "Content-Type": "application/json",
                "Access-Control-Allow-Origin": "*"
            },
            body: JSON.stringify({
                message: "Failed to send email",
                errorMessage: error.message
            })
        };
    }
};

The front end of the website, was previously showing error 405: Method Not Allowed, until I removed the IAM permission policy from the API Gateway and just used the default. At this point the front end website gave no errors, refreshed the page as it should when the form was properly submitted and tested successfully.

So all 3/3 tested successfully, but I am not getting emails from the message box! I’ve tried double encoding JSON from the front end script, and double decoding them in the lambda_function node.js, as well as single serializing into JSON objects. Neither has worked. I am not a great javascript writer and so I imagine somehow there must be discrepancies in how my message is being collected on the front end, and then received by lambda.

I don’t know what to do, but there must be something obvious between those two codes where they aren’t talking to each other. Two days working with ChatGPT4 didn’t solve it :/

Scrollable page, animated cursor

I would like to create a page like this https://www.krissrealestate.com/, basically having one line stable and the cursor moving while scrolling and on the line have content as here https://infovis.fh-potsdam.de/decolonizing/

Would love any kind of input

Thank you

Already tried this in css and java, but I am mostly not sure how to place the images on the line and then have a box with information appearing when the user clicks on them, my intention is to make an experience based on an exhibition.




body {
    cursor: none; /* Hide the default cursor */
    margin: 0;
    padding: 0;
}

#cursor-canvas {
    position: fixed;
    top: 0;
    left: 0;
    pointer-events: none; /* Ensure canvas does not interfere with other elements */
    z-index: 9999; /* Make sure it's on top */
}

.content {
    height: 300vh; /* Make content tall enough to scroll */
}

.section {
    height: 100vh;
    border-bottom: 1px solid #ddd;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 2em;
}


JAVA
document.addEventListener('DOMContentLoaded', () => {
    const canvas = document.getElementById('cursor-canvas');
    const ctx = canvas.getContext('2d');
    
    // Set canvas size
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    // Create a longer zigzag line that spans the scrollable height
    const lineSpacing = 50; // Horizontal distance between zigzag points
    const zigzagHeight = 30; // Vertical distance for each zigzag

    const startX = canvas.width / 2 - lineSpacing / 2;
    const endX = canvas.width / 2 + lineSpacing / 2;
    
    // Create a continuous zigzag line
    const zigzagPath = [];
    for (let y = -canvas.height; y < 2 * canvas.height; y += zigzagHeight) {
        let x1 = startX;
        let x2 = endX;
        if ((y / zigzagHeight) % 2 === 0) {
            x1 += Math.random() * 20; // Asymmetric shift
            x2 -= Math.random() * 20; // Asymmetric shift
        } else {
            x1 -= Math.random() * 20; // Asymmetric shift
            x2 += Math.random() * 20; // Asymmetric shift
        }
        zigzagPath.push({ x: x1, y });
        zigzagPath.push({ x: x2, y: y + zigzagHeight });
    }

    function drawZigzag() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
  
        ctx.lineWidth = 2; // Line width
        ctx.lineCap = 'round'; // Rounded end caps for smooth lines

        // Draw zigzag path with glowing effect
        ctx.shadowColor = 'rgba(255, 0, 0, 0.8)'; // Red glowing color
        ctx.shadowBlur = 10; // Glowing intensity
  
        ctx.strokeStyle = '#ff0000'; // Line color

        ctx.beginPath();
        ctx.moveTo(zigzagPath[0].x, zigzagPath[0].y);
        for (let i = 1; i < zigzagPath.length; i++) {
            ctx.lineTo(zigzagPath[i].x, zigzagPath[i].y);
        }
        ctx.stroke();
    }

    // Track cursor movement
    let lastX, lastY;
    const lines = [];
    document.addEventListener('mousemove', (event) => {
        const x = event.clientX;
        const y = event.clientY;
  
        if (lastX !== undefined && lastY !== undefined) {
            addLine(lastX, lastY, x, y);
        }
  
        lastX = x;
        lastY = y;
    });

    // Track scroll position and update cursor trail
    function addLine(x1, y1, x2, y2) {
        lines.push({ x1, y1, x2, y2 });
        if (lines.length > 100) {
            lines.shift(); // Remove the oldest line if exceeding limit
        }
        drawZigzag();
        drawLines();
    }
  
    function drawLines() {
        ctx.beginPath();
        lines.forEach(line => {
            ctx.moveTo(line.x1, line.y1);
            ctx.lineTo(line.x2, line.y2);
        });
        ctx.stroke();
    }

    // Draw initial zigzag line
    drawZigzag();
  
    // Update canvas size on window resize
    window.addEventListener('resize', () => {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;`
your text`
        drawZigzag();
    });



body {
    cursor: none; /* Hide the default cursor */
    margin: 0;
    padding: 0;
}

#cursor-canvas {
    position: fixed;
    top: 0;
    left: 0;
    pointer-events: none; /* Ensure canvas does not interfere with other elements */
    z-index: 9999; /* Make sure it's on top */
}

.content {
    height: 300vh; /* Make content tall enough to scroll */
}

.section {
    height: 100vh;
    border-bottom: 1px solid #ddd;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 2em;
}


JAVA
document.addEventListener('DOMContentLoaded', () => {
    const canvas = document.getElementById('cursor-canvas');
    const ctx = canvas.getContext('2d');
    
    // Set canvas size
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;

    // Create a longer zigzag line that spans the scrollable height
    const lineSpacing = 50; // Horizontal distance between zigzag points
    const zigzagHeight = 30; // Vertical distance for each zigzag

    const startX = canvas.width / 2 - lineSpacing / 2;
    const endX = canvas.width / 2 + lineSpacing / 2;
    
    // Create a continuous zigzag line
    const zigzagPath = [];
    for (let y = -canvas.height; y < 2 * canvas.height; y += zigzagHeight) {
        let x1 = startX;
        let x2 = endX;
        if ((y / zigzagHeight) % 2 === 0) {
            x1 += Math.random() * 20; // Asymmetric shift
            x2 -= Math.random() * 20; // Asymmetric shift
        } else {
            x1 -= Math.random() * 20; // Asymmetric shift
            x2 += Math.random() * 20; // Asymmetric shift
        }
        zigzagPath.push({ x: x1, y });
        zigzagPath.push({ x: x2, y: y + zigzagHeight });
    }

    function drawZigzag() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
  
        ctx.lineWidth = 2; // Line width
        ctx.lineCap = 'round'; // Rounded end caps for smooth lines

        // Draw zigzag path with glowing effect
        ctx.shadowColor = 'rgba(255, 0, 0, 0.8)'; // Red glowing color
        ctx.shadowBlur = 10; // Glowing intensity
  
        ctx.strokeStyle = '#ff0000'; // Line color

        ctx.beginPath();
        ctx.moveTo(zigzagPath[0].x, zigzagPath[0].y);
        for (let i = 1; i < zigzagPath.length; i++) {
            ctx.lineTo(zigzagPath[i].x, zigzagPath[i].y);
        }
        ctx.stroke();
    }

    // Track cursor movement
    let lastX, lastY;
    const lines = [];
    document.addEventListener('mousemove', (event) => {
        const x = event.clientX;
        const y = event.clientY;
  
        if (lastX !== undefined && lastY !== undefined) {
            addLine(lastX, lastY, x, y);
        }
  
        lastX = x;
        lastY = y;
    });

    // Track scroll position and update cursor trail
    function addLine(x1, y1, x2, y2) {
        lines.push({ x1, y1, x2, y2 });
        if (lines.length > 100) {
            lines.shift(); // Remove the oldest line if exceeding limit
        }
        drawZigzag();
        drawLines();
    }
  
    function drawLines() {
        ctx.beginPath();
        lines.forEach(line => {
            ctx.moveTo(line.x1, line.y1);
            ctx.lineTo(line.x2, line.y2);
        });
        ctx.stroke();
    }

    // Draw initial zigzag line
    drawZigzag();
  
    // Update canvas size on window resize
    window.addEventListener('resize', () => {
        canvas.width = window.innerWidth;
        canvas.height = window.innerHeight;`
your text`
        drawZigzag();
    });

Is it possible to pause celery tasks during execution

I have an app that does transcription. I want that if the user tries to reload they are alerted that reloading will stop their task from completing. I tried to use it with unload but it didn’t work most of the time and I know that it is inconsistent. I would like to pause the task if the user tries to reload. They will get the browser warning alert and if they decide that they still want to reload the task will be terminated.

The idea I have is to trigger a beforeunload which will give them a warning and trigger the pausing of the celery task. I have a polling function on the frontend set up using JS that polls if the task is still running every 5 seconds. If the user reloads this polling function will stop running and so the backend will stop being polled which will trigger the celery task termination.

Otherwise, the function will receive the poll if the user cancels or hasn’t reloaded and the task is unpaused.

Here is my polling function:

function pollTaskStatus(taskId) {
    currentTaskId = taskId;
    console.log(currentTaskId)
    pollInterval = setInterval(() => {
        const xhr = new XMLHttpRequest();
        xhr.onload = function() {
            if (xhr.status == 200) {
                const response = JSON.parse(xhr.responseText);
                if (response.status === 'completed') {
                    console.log('completed');
                    if (response.reload) {
                        // Reload the page to load the correct HTML template
                        window.location.reload();
                    }
                    clearInterval(pollInterval); // Stop polling once completed
                    isTranscribing = false; // Set to false when transcription is complete
                }
            } else {
                showError('An error occurred.');
                clearInterval(pollInterval); // Stop polling on error
                isTranscribing = false; // Set to false on errors
            }
        };
        xhr.onerror = function() {
            showError('Connection error. Please check your network connection and try again.');
            clearInterval(pollInterval); // Stop polling on network error
            isTranscribing = false; // Set to false on network error
            
        };
        xhr.open('GET', `/transcribe/poll_task_status/${taskId}/`, true);
        xhr.send();
    }, 5000); // Poll every 5 seconds
}

I’m also open to any better ideas for dealing with potential customer reload during a running task. My backend is django and I am running celery[redis].

Working with React: Question on deconstructing components

So, I’m going through some React studying and I’m current just doing small code. I was writing two files. The first is where my data is in my variable. I’m using a basic map method to loop through my array of objects and displaying the information as is. Extremely simple and makes perfect sense. screenshot of my code from my gitHub repository.

However, after that I wanted to try practicing with props and deconstructing my array of objects. So, i created a new file component labeled as Movie and passed it through to my Movies map method. Using props, I pass through the movie name and release year through to render. I understand that using this method turns each list item into it’s own component, but I’m not understanding exactly why this can be useful? Can someone give me some examples of this being used in a practical situation or point me to some repository’s where it’s being used better so I can better understand this use of deconstructing an array of objects? Thank you!

I think the most confusing part for me is I’m still calling Movies in my App.jsx to render the data, so it feels like I could just not have the Movie component at all and have kept them within the one component.

Movie File
Movies file after using props to pass information along to the Movie file.

Code works as intended. Just curious on the logic behind why it works.

Can’t make dynamic page changes

I want to make a dynamic page, but when I make a request the page just refreshes, although it should return an alert when no data is entered (I have not entered any).

views.py

def adding(request):
    return render(request, 'matrix_app/adding_matrix.html')

create_matrix.js

function create_matrix(){
    let rows1 = document.getElementById("rows_1_matrix").textContent;
    let columns1 = document.getElementById("columns_1_matrix").textContent;
    let rows2 = document.getElementById("rows_2_matrix").textContent;
    let columns2 = document.getElementById("columns_2_matrix").textContent;

    nums = [rows1, rows2, columns1, columns2];
    for(let i = 0; i < 4; i++){
        if (nums[i] === false || nums[i] === '0') {
           return alert("All fields are ruqired");
        }
    }
}

adding_matrix.html

% extends 'base.html' %}
{% load static %}
{% block content %}
<script src="{% static 'js/create_matrix.js' %}"></script>
<form onsubmit="create_matrix()" id="matrix" method="post">
  {% csrf_token %}
  <div>
    <label>text</label>
    <input type="number" id="rows_1_matrix"/>
  </div>
  <div>
    <label>text</label>
    <input type="number" id="columns_1_matrix"/>
  </div>
  <div>
    <label>text</label>
    <input type="number" id="rows_2_matrix" />
  </div>
  <div>
    <label>text</label>
    <input type="number" id="columns_2_matrix"/>
  </div>
  <input type="submit" value="generate"/>
</form>


<a href="/operations"><button>Back</button></a>
{% endblock %}

Arrows are not working or showing for table content that is not displayed in the initial viewport

Some of the content career route connections do not work for the table’s last row. So, I’m wondering that the information that we need to scroll to click on is experiencing arrow pathway connection issues. Because the first viewport content arrow for both the second and third columns works exceptionally well. However, those material that are outside of the first view port or that require us to scroll down to view are experiencing problems.

When I select Entry-level Positions, Research Roles, Doctorate or PhD in Science. Issues are evident more clearly.
To further understand the issues, following screenshots are supplied – enter image description hereenter image description here

I tried to remedy it by updating the route positions dynamically based on the scroll event to guarantee that your items are precisely positioned, but it didn’t work either.

My code –

document.addEventListener("DOMContentLoaded", () => {
  const credentials = document.querySelectorAll(".credential li");
  const careers = document.querySelectorAll(".career li");
  const table = document.querySelector(".table-container");

  function resetUI() {
    // Reset all elements
    credentials.forEach((c) => {
      c.classList.remove("selected");
      c.classList.remove("disabled");
    });
    careers.forEach((c) => {
      c.classList.remove("selected");
      c.classList.remove("disabled");
    });

    // Clear all arrows from the SVG
    const svg = document.getElementById("arrows");
    while (svg.firstChild) {
      svg.removeChild(svg.firstChild);
    }

    // Update the SVG dimensions to cover the entire table
    const tableRect = table.getBoundingClientRect();
    svg.setAttribute("width", tableRect.width);
    svg.setAttribute("height", tableRect.height);

    // Re-append the arrowhead marker
    const marker = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "marker"
    );
    marker.setAttribute("id", "arrowhead");
    marker.setAttribute("markerWidth", "10");
    marker.setAttribute("markerHeight", "7");
    marker.setAttribute("refX", "0");
    marker.setAttribute("refY", "3.5");
    marker.setAttribute("orient", "auto");
    const arrowhead = document.createElementNS(
      "http://www.w3.org/2000/svg",
      "polygon"
    );
    arrowhead.setAttribute("points", "0 0, 10 3.5, 0 7");
    arrowhead.setAttribute("fill", "orange");
    marker.appendChild(arrowhead);
    svg.appendChild(marker);
  }

  function drawArrowFromCertificateToCareer(fromElement, toElement) {
    const svg = document.getElementById("arrows");
    const fromRect = fromElement.getBoundingClientRect();
    const toRect = toElement.getBoundingClientRect();
    const svgRect = svg.getBoundingClientRect();

    // Calculate positions relative to the table container
    const fromX = fromRect.right - svgRect.left; // Right side of the certificate
    const fromY = fromRect.top + fromRect.height / 2 - svgRect.top; // Middle of the certificate
    const toX = toRect.left - svgRect.left; // Left side of the career
    const toY = toRect.top + toRect.height / 2 - svgRect.top; // Middle of the career

    const arrow = document.createElementNS("http://www.w3.org/2000/svg", "line");
    arrow.setAttribute("x1", fromX);
    arrow.setAttribute("y1", fromY);
    arrow.setAttribute("x2", toX);
    arrow.setAttribute("y2", toY);
    arrow.setAttribute("stroke", "orange");
    arrow.setAttribute("stroke-width", "1.0");
    arrow.setAttribute("marker-end", "url(#arrowhead)");

    svg.appendChild(arrow);
  }

  function drawCircularArrows(fromCareer, credentialIds) {
    const svg = document.getElementById("arrows");
    const svgRect = svg.getBoundingClientRect();
    const careerRect = fromCareer.getBoundingClientRect();

    // Sort credentials by their vertical position
    const sortedCredentials = credentialIds
      .map((id) => document.getElementById(id))
      .filter((el) => el !== null)
      .sort(
        (a, b) => a.getBoundingClientRect().top - b.getBoundingClientRect().top
      );

    // Draw arrows between credentials
    for (let i = 0; i < sortedCredentials.length - 1; i++) {
      const fromRect = sortedCredentials[i].getBoundingClientRect();
      const toRect = sortedCredentials[i + 1].getBoundingClientRect();

      const arrow = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "line"
      );
      arrow.setAttribute(
        "x1",
        fromRect.left - svgRect.left + fromRect.width / 2
      );
      arrow.setAttribute("y1", fromRect.bottom - svgRect.top);
      arrow.setAttribute("x2", toRect.left - svgRect.left + toRect.width / 2);
      arrow.setAttribute("y2", toRect.top - svgRect.top);
      arrow.setAttribute("stroke", "orange");
      arrow.setAttribute("stroke-width", "2");
      arrow.setAttribute("marker-end", "url(#arrowhead)");

      svg.appendChild(arrow);
    }

    // Draw arrow from last credential to career
    if (sortedCredentials.length > 0) {
      const lastCredRect =
        sortedCredentials[sortedCredentials.length - 1].getBoundingClientRect();

      const arrow = document.createElementNS(
        "http://www.w3.org/2000/svg",
        "line"
      );
      arrow.setAttribute(
        "x1",
        lastCredRect.left - svgRect.left + lastCredRect.width / 2
      );
      arrow.setAttribute("y1", lastCredRect.bottom - svgRect.top);
      arrow.setAttribute("x2", careerRect.left - svgRect.left); // Point to the left edge of the career span
      arrow.setAttribute(
        "y2",
        careerRect.top - svgRect.top + careerRect.height / 2
      ); // Point to the vertical middle of the career span
      arrow.setAttribute("stroke", "orange");
      arrow.setAttribute("stroke-width", "2");
      arrow.setAttribute("marker-end", "url(#arrowhead)");

      svg.appendChild(arrow);
    }
  }

  function highlightRelatedItems(selectedItem, _items, dataAttribute) {
    const relatedItems = selectedItem.dataset[dataAttribute].split(",");

    // Highlight related items
    relatedItems.forEach((itemId) => {
      const itemElement = document.getElementById(itemId);
      if (itemElement) {
        itemElement.classList.remove("disabled");
        itemElement.classList.add("selected");
        if (selectedItem.closest(".credential")) {
          // Draw arrow from credential to career
          drawArrowFromCertificateToCareer(selectedItem, itemElement);
        } else {
          // Draw circular arrows for careers
          drawCircularArrows(selectedItem, relatedItems);
        }
      }
    });
  }

  // Add event listeners to credentials (Certificate, Degree or Credential column)
  credentials.forEach((credential) => {
    credential.addEventListener("click", (event) => {
      event.stopPropagation(); // Prevent click event from propagating to the document

      // Reset all elements
      resetUI();

      // Highlight the selected credential and disable others
      credential.classList.add("selected");
      credentials.forEach((c) => {
        if (c !== credential) {
          c.classList.add("disabled");
        }
      });

      // Highlight related careers and draw arrows from credential to career
      highlightRelatedItems(credential, careers, "related");

      // Disable unselected careers
      careers.forEach((c) => {
        if (!c.classList.contains("selected")) {
          c.classList.add("disabled");
        }
      });
    });
  });

  // Add event listeners to careers
  careers.forEach((career) => {
    career.addEventListener("click", (event) => {
      event.stopPropagation(); // Prevent click event from propagating to the document

      // Reset all elements
      resetUI();

      // Highlight the selected career and disable others
      career.classList.add("selected");
      careers.forEach((c) => {
        if (c !== career) {
          c.classList.add("disabled");
        }
      });

      // Retrieve related credentials from the data-related attribute
      const relatedCredentials = career.dataset.related.split(",");

      // Draw the circular arrows from career to credentials and back
      drawCircularArrows(career, relatedCredentials);

      // Highlight related credentials
      highlightRelatedItems(career, credentials, "related");

      // Disable unrelated credentials
      credentials.forEach((c) => {
        if (!c.classList.contains("selected")) {
          c.classList.add("disabled");
        }
      });
    });
  });

  document.addEventListener("click", resetUI);
  window.addEventListener("resize", resetUI);
});
/* Improved typography and color scheme */

body {
  font-family: 'Roboto', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  background-color: #f5f7fa;
  color: #333;
  line-height: 1.6;
  margin: 0;
  padding: 20px;
}


/* Container with rounded corners and subtle shadow */

.table-container {
  max-width: 1000px;
  margin: 0 auto;
  background-color: #ffffff;
  border-radius: 12px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  padding: 24px;
  position: relative;
  /* For positioning the arrows SVG */
  overflow-x: auto;
}


/* Table styling with alternating row colors */

table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  margin-bottom: 20px;
  overflow-x: auto;
}

th,
td {
  padding: 12px 16px;
  text-align: left;
  border-bottom: 1px solid #e0e0e0;
}

table thead tr th:first-child {
  width: 20%;
}

table thead tr th:nth-child(2),
table tbody tr th:nth-child(3) {
  width: 40%;
}

th {
  background-color: #f0f4f8;
  font-weight: 600;
  color: #2c3e50;
  text-transform: uppercase;
  font-size: 0.9em;
  letter-spacing: 0.5px;
}

tr:nth-child(even) {
  background-color: #f9fafb;
}


/* Rounded corners for first and last cells in a row */

tr:first-child th:first-child {
  border-top-left-radius: 8px;
}

tr:first-child th:last-child {
  border-top-right-radius: 8px;
}

tr:last-child td:first-child {
  border-bottom-left-radius: 8px;
}

tr:last-child td:last-child {
  border-bottom-right-radius: 8px;
}


/* Remove list-style and add styling for credential and career items */

.credential ul,
.career ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
}

.credential li,
.career li {
  transition: all 0.3s ease;
  padding: 8px 12px;
  margin: 4px 0;
  border-radius: 6px;
  cursor: pointer;
}

.credential li:hover,
.career li:hover {
  background-color: #e8f0fe;
  transform: translateY(-2px);
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}


/* Selected state with color highlight */

.selected {
  background-color: #3498db;
  color: white;
  font-weight: 500;
}


/* Disabled state */

.disabled {
  opacity: 0.5;
  pointer-events: none;
}


/* Education level styling */

.education-level {
  font-weight: 600;
  padding: 12px;
  border-radius: 6px;
}


/* Color scheme for education levels */

.high-school {
  background-color: #e8eaf6;
}

.one-year-college {
  background-color: #fff3e0;
}

.two-years-college {
  background-color: #e0f2f1;
}

.four-years-college {
  background-color: #fce4ec;
}

.six-years-college {
  background-color: #e1f5fe;
}

.six-plus-years-college {
  background-color: #fff8e1;
}


/* Arrows SVG styling */

#arrows {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 10;
}


/* Responsive design */

@media (max-width: 768px) {
  .table-container {
    padding: 16px;
  }
  table {
    min-width: 400px;
  }
  table thead tr th:nth-child(2),
  table tbody tr th:nth-child(3) {
    width: 46%;
  }
  table tbody tr td:first-child {
    padding: 84px 0px;
    text-align: center;
    border-radius: 0;
  }
  table tbody tr td:first-child span {
    transform: rotate(270deg);
    white-space: nowrap;
    /* Prevent the text from wrapping */
    display: inline-block;
    text-align: left;
    /* Adjust alignment */
  }
  table td,
  table th {
    font-size: 12px;
  }
  table tbody tr td:nth-child(2),
  table tbody tr td:nth-child(3) {
    vertical-align: top;
  }
  th,
  td {
    padding: 8px;
  }
  td:before {
    content: attr(data-label);
    position: absolute;
    left: 6px;
    width: 45%;
    padding-right: 10px;
    white-space: nowrap;
    font-weight: bold;
  }
  .credential li,
  .career li {
    padding: 6px 8px;
    margin: 2px 0;
  }
  #arrows line {
    stroke-width: 0.5;
    /* Thinner lines for small screens */
  }
  /* Adjust the size of arrowheads */
  marker polygon {
    points: "0 0, 5 1.75, 0 3.5";
    /* Smaller arrowhead size */
  }
}

@media (hover: none) {
  .credential li,
  .career li {
    padding: 12px 8px;
  }
}


/* Smooth transitions for all interactive elements */

a,
button,
input,
select,
textarea {
  transition: all 0.3s ease;
}


/* Optimize white space */

li {
  margin-bottom: 8px;
}
<main>
  <div class="table-container" role="table" aria-label="Career Pathways Table">
    <table>
      <thead>
        <tr>
          <th scope="col">Education Level</th>
          <th scope="col">Certificate, Degree or Credential</th>
          <th scope="col">Career</th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td class="education-level high-school"><span>High School</span></td>
          <td class="credential">
            <ul>
              <li id="high-school-diploma" data-related="assistant,technician,specialist">High School Diploma
              </li>
              <li id="vocational-certificate" data-related="assistant">Vocational Certificate</li>
            </ul>
          </td>
          <td class="career">
            <ul>
              <li id="entry-level" data-related="associate-degree,certificate,vocational-certificate,phd-science">
                Entry-level positions</li>
              <li id="assistant" data-related="high-school-diploma,vocational-certificate">Assistant roles
              </li>
            </ul>
          </td>
        </tr>
        <tr>
          <td class="education-level one-year-college"><span>1 Year College</span></td>
          <td class="credential">
            <ul>
              <li id="certificate" data-related="assistant,technician">Certificate</li>
              <li id="technical-certificate" data-related="technician">Technical Certificate</li>
            </ul>
          </td>
          <td class="career">
            <ul>
              <li id="assistant" data-related="certificate,technical-certificate">Assistant roles</li>
              <li id="technician" data-related="certificate,technical-certificate">Technician roles
              </li>
            </ul>
          </td>
        </tr>

        <tr>
          <td class="education-level two-years-college"><span>2 Years College</span></td>
          <td class="credential">
            <ul>
              <li id="associate-degree" data-related="professional,specialist">Associate Degree</li>
              <li id="diploma-technology" data-related="professional,specialist">Diploma in Technology
              </li>
            </ul>
          </td>
          <td class="career">
            <ul>
              <li id="professional" data-related="associate-degree,diploma-technology">Professional roles
              </li>
              <li id="specialist" data-related="associate-degree,diploma-technology">Specialist roles
              </li>
            </ul>
          </td>
        </tr>

        <tr>
          <td class="education-level four-years-college"><span>4 Years College</span></td>
          <td class="credential">
            <ul>
              <li id="bachelors-degree" data-related="specialist,manager">Bachelor's Degree</li>
              <li id="bachelor-arts" data-related="manager">Bachelor of Arts</li>
            </ul>
          </td>
          <td class="career">
            <ul>
              <li id="specialist" data-related="bachelors-degree,bachelor-arts">Specialist roles</li>
              <li id="manager" data-related="bachelors-degree,bachelor-arts">Management roles</li>
            </ul>
          </td>
        </tr>

        <tr>
          <td class="education-level six-years-college"><span>6 Years College</span></td>
          <td class="credential">
            <ul>
              <li id="masters-degree" data-related="expert,consultant">Master's Degree</li>
              <li id="mba" data-related="expert,consultant">MBA</li>
            </ul>
          </td>
          <td class="career">
            <ul>
              <li id="expert" data-related="masters-degree,mba">Expert roles</li>
              <li id="consultant" data-related="masters-degree,mba">Consultant roles</li>
            </ul>
          </td>
        </tr>

        <tr>
          <td class="education-level six-plus-years-college"><span>6+ Years College</span></td>
          <td class="credential">
            <ul>
              <li id="doctorate" data-related="expert,researcher">Doctorate</li>
              <li id="phd-science" data-related="expert,researcher">PhD in Science</li>
            </ul>
          </td>
          <td class="career">
            <ul>
              <li id="expert" data-related="associate-degree,bachelors-degree,doctorate">Expert roles
              </li>
              <li id="researcher" data-related="doctorate,phd-science">Research roles</li>
            </ul>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
</main>
<svg id="arrows" aria-hidden="true"></svg>

Why does this JS code not work in a Vue component template?

My question title might be wrong but this is the basic code:

const messages = [
{"userid": 1, 
"messageid": 80, 
"Statuses":[{"userid": 1, "statusid": 1}, {"userid": 5, "statusid": 2}, {"userid": 99, "statusid": 1}, {"userid": 26, "statusid": 4}]
},
];

messages.forEach((message)=>{
console.log(message.Statuses.find((status)=>status.userid === 99).statusid===1);
})

What I am trying to do is find the object within Statuses array where the userid is 99 and then check if the statusid===1. The above code works and returns true/false if there is a match.

If I use the same code in a Vue template it fails e.g.:

<ul>
  <li v-for="message in messages">
    <div v-if="message.Statuses.find((status)=>status.userid === 99).statusid===1">
    <p>Status is 1</p>
    </div>
  </li>
</ul>

I get an error saying "message.Statuses.find(...) is undefined".

If I change the code to remove the statusid check then it works fine:

 <div v-if="message.Statuses.find((status)=>status.userid === 99)">

What’s up with this and how can I do this within the template HTML?

How to prevent the columns to stretch up to the size of the whole chart if less data is provided?

I dont display static data and as such the size of the data varies. I show data for each month (xAxis) and year (yAxis). When the data only fills one row (lets say I have January-December) for only 2024, then the colums stretch up to the end of the chart. How can I prevent that, so the cells stay same size?

An example of what I mean: https://jsfiddle.net/J_Code_2025/du97b4ka/2/

The code:

$(function () {
    $('#container').highcharts({
        chart: {
            type: 'heatmap',
            marginTop: 40,
            marginBottom: 80,
            plotBorderWidth: 1
        },
        title: {
            text: 'Sales per employee per weekday'
        },
        xAxis: {
            categories: ['Alexander', 'Marie', 'Maximilian', 'Sophia', 'Lukas', 'Maria', 'Leon', 'Anna', 'Tim', 'Laura']
        },
        yAxis: {
         tickWidth: 1,
        tickLength: 0,
            categories: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'],
            title: null
        },
        colorAxis: {
            min: 0,
            minColor: '#FFFFFF',
            maxColor: Highcharts.getOptions().colors[0]
        },
        series: [{
            name: 'Sales per employee',
            borderWidth: 4,
            data: [
                [0, 0, 10],
                ...
            ],
            dataLabels: {
                enabled: true,
                color: '#000000'
            }
        }]

    });
});

How to use a url from json file as link for a button html using javascript

I’m almost at the end of a thing I like to make for my website.
I have a json file that I use as a sort of database for my thumbails for the recipes…
The most info displays as I would like (thanks to this forum by the way)
See the print screen.
enter image description here

The button that says ‘MAKE IT YOURSELF’ shoud use the link (url) from the json file, here a little peace of the json file…

[
  {
    "postid": "3",
    "title": "Pizza",
    "text": "some text to explain the recept in short",
    "img": "../assets/images/recipes-thumbnail/pizza.png",
    "ingredients": "flour, yeast, water, salt, honey, olive oil",
    "url": "../pages/pizza.html"
  }
]

This is the html

  <!--  Card  -->
  <div class="user-cards" data-post-cards-container data-aos="zoom-in" data-aos-delay="100"></div>

  <template data-post-template>
    <div id="card-container" class="container">
      <div class="card">
        <div class="card-image_2">
          <div class="photo" data-photo></div>
        </div>

        <div class="card-data">
          <h3 class="header" data-header></h3>
          <p class="body" data-body></p>
          <p class="ingre" data-ingre></p>

          <!-- This is the button now -->
          <a href="#" class="card-button" data-link>Make it yourself</a>

        </div>
      </div>
    </div>
  </template>
  <!--  End Card  -->

This is the javascript part, this is the code that works fine for the moment

I don’t now how to adapt it for the link

fetch("../jsn/posts-main.json")
  .then(res => res.json())
  .then(data => {
    posts = data.map(post => {
      const card = postCardTemplate.content.cloneNode(true).children[0]
      const header = card.querySelector("[data-header]")
      const body = card.querySelector("[data-body]")
      const ingre = card.querySelector("[data-ingre]")
      const img = card.querySelector("[data-photo]")

      header.textContent = post.title
      body.textContent = post.text
      ingre.textContent = post.ingredients

      const imgContent = document.createElement('img');
      imgContent.src = post.img;
      img.appendChild(imgContent);

      postCardContainer.append(card)
      return {
        title: post.title,
        ingre: post.ingredients,
        element: card
      }
    })
})

Sorry for the long post, I thank you guys already

I tried to search on google and forums, but can’t find exactly what i need

Sequelize – Avoid named binding parameters when using sequelize.fn

I am using Sequelize 6.21.3 to access a MariaDB 10.8 database. In that database i have a Users table that contains a password column which is encoded with the SQL function ENCODE.

Now I’d like to update a user’s password via sequelize. I do the following:

user.update({
    password: sequelize.fn(
        "encode",
        password,
        "ENCODE_PASSWORD"
    )
});

However if the password contains a $ sign (like in "123$foo"), the update query results in an error:

Named bind parameter "$foo" has no value in the given object.

I understand that sequelize tries to fill the value of the $foo parameter, however $foo is not supposed to be a parameter, it is just a literal string.

How do I avoid this behavior?

math calculation error using HTML and JavaScript [closed]

I am trying to build an application that automatically calculates costs given specific values. But for some reason, the output is off. Thank you in advance for any help.
profit margin and total cost is wrong

Here is the code for my profit margin and total cost:

const plancredit = 5;
const alignercredit = 5;
const plancost = 7000;
const max = Math.max(upalign, lowalign);
const planningCostA = plancredit * 1400;
const planningCostB = plancredit * 1300;
const planningCostC = plancredit * 1200;
const planningCostD = plancredit * 1000;
const totalaligner = upalign + lowalign;
const treatmentduration = max * 14;
const alignercost = (totalaligner * 1500);
const alignercosta = (totalaligner * 1400);
const alignercostb = (totalaligner * 1300);
const alignercostc = (totalaligner * 1200);
const alignercostd = (totalaligner * 1000);
const totalcost = (plancost + alignercost + other + scanfee);
const totalcosta = (plancost + alignercosta + other + scanfee);
const totalcostb = (plancost + alignercostb + other + scanfee);
const totalcostc = (plancost + alignercostc + other + scanfee);
const totalcostd = (plancost + alignercostd + other + scanfee);
<tr>
  <td>Profit Margin (%)</td>
  <td>${(((patcharge-totalcost) / patcharge)*100).toFixed(1)}</td>
  <td>${(((patcharge-totalcosta) / patcharge)*100).toFixed(1)}</td>
  <td>${(((patcharge-totalcostb) / patcharge)*100).toFixed(1)}</td>
  <td>${(((patcharge-totalcostc) / patcharge)*100).toFixed(1)}</td>
  <td>${(((patcharge-totalcostd) / patcharge)*100).toFixed(1)}</td>
</tr>

The total cost are off by either 500, 1000, and 2000.

Dockerized App cant connect to a local mongodb server without docker

I’m trying to connect to MongoDB in a dockerized app, and i dont know how to do this.

My Docker File

FROM node:18-bullseye

WORKDIR /Bot_granada_rp

COPY . .

RUN npm install

RUN npm install -g pm2

EXPOSE 27017


CMD ["pm2-runtime", "start", "index.js"]

It’s a discord bot. I have an anti-crash. The anti-crash sends this:

MongooseError: Operation criptos.find() buffering timed out after 10000ms at Timeout.(/Bot_granada_rp/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js:185:23)
at listOnTimeout (node:internal/timers:569:17)
at process.processTimers (node:internal/timers:512:7)

I’m trying to insert the time in a text field inverted

I’m trying to create a regular expression mask to insert the time starting with seconds, minutes and finally the hour.

For example, showing 12:34:56 after typing – 6, 5, 4, 3, 2, 1

I tried creating a regular expression, but I’m not very experienced. If you can help me I would be very grateful
This is my regex that didn’t work:

function timeinvert(v) {
  v = v.replace(/D/g, "");
  v = v.replace(/(d)(d)/, "$2$1")
  v = v.replace(/(d)(d)(d)/, "$3:$2$1")
  v = v.replace(/(d)(d)(d)(d)/, "$4$3:$2$1")
  v = v.replace(/(d)(d)(d)(d)(d)/, "$5:$4$3:$2$1")
  v = v.replace(/(d)(d)(d)(d)(d)(d)d+?$/, "$6$5:$4$3:$2$1")
  // v = v.replace(/(d{2})(d)/, "$1:$2")
  // v = v.replace(/(d{2})d+?$/, "$1")
  return v;
}