JavaScript error with dynamic buttons that open a Modal

I have a sql query that returns an html table that includes first name, last name, empid and a dynamic button. the following code creates the button.

`echo ‘Add Employee’;

I use the following Javascript:

<script>
$(document).ready(function() {
  // Attach click event listener to a parent element of the "Add Employee" buttons
  $(document).on('click', '.add-employee-btn', function(event) {
    event.stopPropagation();
    event.preventDefault();
    var firstName = $(this).data('firstname');
    var lastName = $(this).data('lastname');
    var empId = $(this).data('empid');
    var modal = $('#addEmployeeModal'); // Get the modal element

    // Load the content from newemployee.php into the modal
    $('#modalContent', modal).load('../../newemployee.php', function() {
      // Set the values in the form fields
      $('input[name="FstName"]', modal).val(firstName);
      $('input[name="LstName"]', modal).val(lastName);
      $('input[name="EmpID"]', modal).val(empId);

      // Open the modal popup after the content is loaded
      modal.modal('show');
    });
  });

  // Clean up the modal content and reset form fields when the modal is hidden
  $(document).on('hide.bs.modal', '#addEmployeeModal', function() {
    var modal = $(this);
    modal.find('.modal-content').empty();
    modal.find('form')[0].reset();
  });

  // Submit the form inside the modal
  $(document).on('submit', '#addEmployeeForm', function(e) {
    e.preventDefault();

    // Perform AJAX request to add the employee
    // ...

    // Close the modal popup after submitting the form
    $('#addEmployeeModal').modal('hide');
  });

  // Add click event listener to the table rows
  $(document).on('click', '.clickable', function() {
    var persId = $(this).data('persid');
    var isCompleteMatch = $(this).data('completematch');

    if (isCompleteMatch) {
      window.location.href = '../../emp.php?persid=' + persId;
    }
  });
});

</script>

When the page loads, lets say there are rows in the table, I can click on any of the buttons and the Modal opens up with the right data being populated into the Modal. When I close the Modal and try to click on any of the buttons at that point the code errors out with modal.modal(‘show’) is not a function.

Is there a way to fix this so that I can open any of them via the created button, close them then open another one without refreshing the page?
`

I have tried moving the script within the page, I have tried sending info to the console and can get the Modal opens and the Modal closes but when I click on any link at that point it returns the same error.

DOM 2 arrays iteration in one function

I'm trying to iterate 2 document object model arrays at the same time but only 2nd array is iterating. even if I clicked other [div] "news" show This[0] keep showing.

var  articles =  document. query Selector All('.news');
var  show This =  document. get Elements By Class Name('check-icon');

articles. for Each((read) => {
    read. add Event Listener("click", () => {
        if (articles[0].click) {
            show This[0].style. Display = 'block';
        }
    });
    console.log("read");
});

I want my code to specifically show check icon every time I click an specific news div

Javascript Async Functions Not Awaiting Properly [duplicate]

The 2 fadeInList functions define the fading behavior of a single continuous fading list, split onto 2 lines. They typeOutText function types out some text and then (should) call the List1 function, await its completion, then call the List2 function. Before I added the enter/spacebar key events, the 2nd list properly awaited the completion of the first before rendering. Now, for some infuriating reason that even 4 hours of debugging cannot find, the lists render simultaneously.

async function fadeInSlide3List1() {
  const slide3List1 = slide3Lists[0];
  const container = document.querySelector(".design-list1");
  container.style.visibility = "hidden";

  let nextItemSpeed = 2000;

  for (let i = 0; i < slide3List1.length; i++) {
    const listItem = document.createElement("li");
    listItem.textContent = slide3List1[i];
    listItem.style.opacity = 0;
    listItem.style.transition = "opacity 1.5s";

    container.appendChild(listItem);
  }

  container.style.visibility = "visible";

  const fadeNextItem = async (index) => {
    if (index < slide3List1.length) {
      const listItem = container.children[index];
      await new Promise((resolve) => setTimeout(resolve, nextItemSpeed));
      listItem.style.opacity = 1;
      fadeNextItem(index + 1);
    } else {
      window.removeEventListener("keydown", handleKeyPress); // Remove the event listener after the final list item is faded in
    }
  };

  const handleKeyPress = (event) => {
    const keyPressed = event.key;
    if (keyPressed === "Enter" || keyPressed === " ") {
      nextItemSpeed = 500;
      fadeNextItem(0);
    }
  };

  window.addEventListener("keydown", handleKeyPress);

  fadeNextItem(0);
}

async function fadeInSlide3List2() {
  const slide3List2 = slide3Lists[1];
  const container = document.querySelector(".design-list2");
  container.style.visibility = "hidden";

  let nextItemSpeed = 2000;

  for (let i = 0; i < slide3List2.length; i++) {
    const listItem = document.createElement("li");
    listItem.textContent = slide3List2[i];
    listItem.style.opacity = 0;
    listItem.style.transition = "opacity 1.5s";

    container.appendChild(listItem);
  }

  container.style.visibility = "visible";

  const fadeNextItem = async (index) => {
    if (index < slide3List2.length) {
      const listItem = container.children[index];
      await new Promise((resolve) => setTimeout(resolve, nextItemSpeed));
      listItem.style.opacity = 1;
      fadeNextItem(index + 1);
    } else {
      window.removeEventListener("keydown", handleKeyPress); // Remove the event listener after the final list item is faded in
    }
  };

  const handleKeyPress = (event) => {
    const keyPressed = event.key;
    if (keyPressed === "Enter" || keyPressed === " ") {
      nextItemSpeed = 500;
      fadeNextItem(0);
    }
  };

  window.addEventListener("keydown", handleKeyPress);

  fadeNextItem(0);
}

async function typeOutSlide3() {
  const slide3Lines = slideLines[2];
  const content = slideContent[2];

  if (content) {
    slide3Container.innerHTML = content;
  } else {
    let nextLineSpeed = typingSpeed;
    let delay = 1500;

    const renderNextLine = async (i) => {
      const line = slide3Lines[i];
      await new Promise((resolve) => setTimeout(resolve, delay));
      await typeOutText(
        slide3Container,
        line.text,
        nextLineSpeed,
        line.elementType,
      );
    };

    const handleKeyPress = (event) => {
      const keyPressed = event.key;
      if (keyPressed === "Enter" || keyPressed === " ") {
        window.removeEventListener("keydown", handleKeyPress);
        nextLineSpeed = 10;
        delay = 500;
      }
    };

    window.addEventListener("keydown", handleKeyPress);

    for (let i = 0; i < slide3Lines.length; i++) {
      await renderNextLine(i);
    }

    slideContent[2] = slide3Container.innerHTML;
  }

  const paragraphs = slide3Container.getElementsByTagName("p");
  if (paragraphs.length >= 2) {
    paragraphs[1].classList.add("second-p");
  }

  await fadeInSlide3List1(); // Render and fade-in list 1
  await fadeInSlide3List2(); // Render and fade-in list 2
}

I’ve been debugging for hours. I have exhausted my knowledge. I am not particularly skilled with JavaScript.

Dropdown option not being selected even though conditions are met. Checkboxes and Dropdown – JS

I’m trying to set up conditions so that as checkboxes in Columns D, E, F, and G are checked, the dropdown selection in Column I goes from “ESG? What’s that?” > “Tier 3” > “Tier 2” > “Tier 1”.

Right now, the code is working well, except it doesn’t register the checkbox in column G, so the dropdown option “Tier 1” is never selected.

For clarity, the drop-down options only change when the checkboxes are checked in order (eg D+E= Tier 3, D+E+F= Tier 2, etc).

`// Sheet 2 (Target Accounts)
function onEditTargetAccounts(e) {
var sheet = e.source.getSheetByName(“Target Accounts”);

if (sheet.getName() == “Target Accounts”) {
if (sheet.getLastColumn() >= 11) { // Check if Column K exists
sheet.getRange(“A2:K”).sort([{column: 11, ascending: true}, {column: 1, ascending: true}]); // Sort the rows based on Column K and then Column A
}

var range = e.range;
var column = range.getColumn();
var row = range.getRow();

if (column >= 4 && column <= 7) { // Columns D, E, F, G
  var checkboxE = sheet.getRange(row, 5).isChecked();
  var checkboxF = sheet.getRange(row, 6).isChecked();
  var checkboxG = sheet.getRange(row, 7).isChecked();
  var checkboxH = sheet.getRange(row, 8).isChecked();
  
  if (checkboxE && !checkboxF && !checkboxG && !checkboxH) {
    sheet.getRange(row, 9).setValue("ESG? What's that?"); // Automatically select "ESG? What's that?" option from the dropdown in Column I
  } else if (checkboxE && checkboxF && !checkboxG && !checkboxH) {
    sheet.getRange(row, 9).setValue("Tier 3"); // Automatically select "Tier 3" option from the dropdown in Column I
  } else if (checkboxE && checkboxF && checkboxG && !checkboxH) {
    sheet.getRange(row, 9).setValue("Tier 2"); // Automatically select "Tier 2" option from the dropdown in Column I
  } else if (checkboxE && checkboxF && checkboxG && checkboxH) {
    sheet.getRange(row, 9).setValue("Tier 1"); // Automatically select "Tier 1" option from the dropdown in Column I
  } else {
    sheet.getRange(row, 9).clearContent(); // Clear the value in Column I if none of the conditions are met
  }
}

}
}`

why typescript’s structure typing is inconsistent?

we know that typescript applies structure typing as below example shows:

interface Vector {
   x: number;
   y: number;
}

interface NamedVector {
   x: number;
   y: number;
   name: string;
}

function calculateLength(v: Vector) {
   return Math.sqrt(v.x * v.x + v.y * v.y);
}

const v: NamedVector = { x: 3, y: 4, name: 'zee' };
calculateLength(v);   // compile OK, result is 5

It allowed calculateLength to be called with a NamedVector because its structure was compatible with Vector.

But in term of assignment, it doesn’t use structure typing anymore:

const v: Vector = { x: 3, y: 4, name: 'Zee' };  // compile error, 'name' does not exist in type 'Vector'

according to the definition of structure typing, { x: 3, y: 4, name: 'Zee' } is also compatible with Vector, so why structure typing doesn’t work here?

and what is the Utility Types I can use to describe a type that must contains x and y fields and also some other fields so I can do:

const v: XXX<Vector> = { x: 3, y: 4, name: 'Zee' };

Chatbot replies not displayed on web page, only in terminal

Description:
I have developed a chatbot using Flask and JavaScript. While the chatbot works correctly in my terminal, I’m encountering an issue when I access it through a web page. Upon running python3 app.py and navigating to “http://127.0.0.1:5001”, the chatbot replies are not displayed on the web page. Strangely, when I send a message, my input appears in the chatbox, but the chatbot’s response remains invisible. Consequently, the chatbot only seems to function when I run it on my terminal on VS Code.

Below is the code for my app.js file:

class Chatbox {
  constructor() {
    this.args = {
      openButton: document.querySelector(".chatbox__button"),
      sendButton: document.querySelector(".send__button"),
    };

    this.state = false;
    this.messages = [];
  }

  toggleState(chatBox) {
    chatBox.classList.toggle("chatbox--active");
  }

  onSendButton(chatBox) {
    var textField = chatBox.querySelector("input");
    let text1 = textField.value;
    if (text1 === "") {
      return;
    }

    let msg1 = { name: "User", message: text1 };
    this.messages.push(msg1);

    fetch($SCRIPT_ROOT + "/predict", {
      method: "POST",
      body: JSON.stringify({ message: text1 }),
      mode: "cors",
      headers: {
        "Content-Type": "application/json",
      },
    })
      .then((r) => r.json())
      .then((r) => {
        let msg2 = { name: "Sam", message: r.answer };
        this.messages.push(msg2);
        this.updateChatText(chatBox);
        textField.value = "";
      })
      .catch((error) => {
        console.error("Error:", error);
        this.updateChatText(chatBox);
        textField.value = "";
      });
  }

  updateChatText(chatBox) {
    var html = "";
    this.messages
      .slice()
      .reverse()
      .forEach(function (item, index) {
        if (item.name === "Sam") {
          html +=
            '<div class="messages__item messages__item--visitor">' +
            item.message +
            "</div>";
        } else {
          html +=
            '<div class="messages__item messages__item--operator">' +
            item.message +
            "</div>";
        }
      });

    const chatMessage = chatBox.querySelector(".chatbox__messages");
    chatMessage.innerHTML = html;
  }

  display() {
    const { openButton, sendButton } = this.args;
    const chatBox = document.querySelector(".chatbox__support");

    openButton.addEventListener("click", () => this.toggleState(chatBox));

    sendButton.addEventListener("click", () => this.onSendButton(chatBox));

    const inputNode = chatBox.querySelector("input");
    inputNode.addEventListener("keyup", (event) => {
      if (event.key === "Enter") {
        this.onSendButton(chatBox);
      }
    });
  }
}

const chatbox = new Chatbox();
chatbox.display();

Furthermore, here’s the HTML code in my base.html file:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Chatbot</title>
    <link
      rel="stylesheet"
      href="{{ url_for('static', filename='style.css') }}"
    />
  </head>
  <body>
    <div class="container">
      <div class="chatbox">
        <div class="chatbox__support">
          <div class="chatbox__header">
            <div class="chatbox__image--header">
              <img
                src="https://img.icons8.com/color/48/000000/circled-user-female-skin-type-5--v1.png"
                alt="image"
              />
            </div>
            <div class="chatbox__content--header">
              <h4 class="chatbox__heading--header">Chat support</h4>
              <p class="chatbox__description--header">
                Hi. My name is Sam. How can I help you?
              </p>
            </div>
          </div>
          <div class="chatbox__messages">
            <div></div>
          </div>
          <div class="chatbox__footer">
            <input type="text" placeholder="Write a message..." />
            <button class="chatbox__send--footer send__button">Send</button>
          </div>
        </div>
        <div class="chatbox__button">
          <button>
            <img
              src="{{ url_for('static', filename='images/chatbox-icon.svg') }}"
            />
          </button>
        </div>
      </div>
    </div>

    <script>
      var $SCRIPT_ROOT = "{{ request.script_root|safe }};";
    </script>
    <script
      type="text/javascript"
      src="{{ url_for('static', filename='app.js') }}"
    ></script>
  </body>
</html>

and CSS code for style.css:

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

body {
  font-family: "Nunito", sans-serif;
  font-weight: 400;
  font-size: 100%;
  background: #f1f1f1;
}

*,
html {
  --primaryGradient: linear-gradient(93.12deg, #581b98 0.52%, #9c1de7 100%);
  --secondaryGradient: linear-gradient(
    268.91deg,
    #581b98 -2.14%,
    #9c1de7 99.69%
  );
  --primaryBoxShadow: 0px 10px 15px rgba(0, 0, 0, 0.1);
  --secondaryBoxShadow: 0px -10px 15px rgba(0, 0, 0, 0.1);
  --primary: #581b98;
}

/* CHATBOX
=============== */
.chatbox {
  position: absolute;
  bottom: 30px;
  right: 30px;
}

/* CONTENT IS CLOSE */
.chatbox__support {
  display: flex;
  flex-direction: column;
  background: #eee;
  width: 300px;
  height: 350px;
  z-index: -123456;
  opacity: 0;
  transition: all 0.5s ease-in-out;
}

/* CONTENT ISOPEN */
.chatbox--active {
  transform: translateY(-40px);
  z-index: 123456;
  opacity: 1;
}

/* BUTTON */
.chatbox__button {
  text-align: right;
}

.send__button {
  padding: 6px;
  background: transparent;
  border: none;
  outline: none;
  cursor: pointer;
}

/* HEADER */
.chatbox__header {
  position: sticky;
  top: 0;
  background: orange;
}

/* MESSAGES */
.chatbox__messages {
  margin-top: auto;
  display: flex;
  overflow-y: scroll;
  flex-direction: column-reverse;
}

.messages__item {
  background: orange;
  max-width: 60.6%;
  width: fit-content;
}

.messages__item--operator {
  margin-left: auto;
}

.messages__item--visitor {
  margin-right: auto;
}

/* FOOTER */
.chatbox__footer {
  position: sticky;
  bottom: 0;
}

.chatbox__support {
  background: #f9f9f9;
  height: 450px;
  width: 350px;
  box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.1);
  border-top-left-radius: 20px;
  border-top-right-radius: 20px;
}

/* HEADER */
.chatbox__header {
  background: var(--primaryGradient);
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
  padding: 15px 20px;
  border-top-left-radius: 20px;
  border-top-right-radius: 20px;
  box-shadow: var(--primaryBoxShadow);
}

.chatbox__image--header {
  margin-right: 10px;
}

.chatbox__heading--header {
  font-size: 1.2rem;
  color: white;
}

.chatbox__description--header {
  font-size: 0.9rem;
  color: white;
}

/* Messages */
.chatbox__messages {
  padding: 0 20px;
}

.messages__item {
  margin-top: 10px;
  background: #e0e0e0;
  padding: 8px 12px;
  max-width: 70%;
}

.messages__item--visitor,
.messages__item--typing {
  border-top-left-radius: 20px;
  border-top-right-radius: 20px;
  border-bottom-right-radius: 20px;
}

.messages__item--operator {
  border-top-left-radius: 20px;
  border-top-right-radius: 20px;
  border-bottom-left-radius: 20px;
  background: var(--primary);
  color: white;
}

/* FOOTER */
.chatbox__footer {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  padding: 20px 20px;
  background: var(--secondaryGradient);
  box-shadow: var(--secondaryBoxShadow);
  border-bottom-right-radius: 10px;
  border-bottom-left-radius: 10px;
  margin-top: 20px;
}

.chatbox__footer input {
  width: 80%;
  border: none;
  padding: 10px 10px;
  border-radius: 30px;
  text-align: left;
}

.chatbox__send--footer {
  color: white;
}

.chatbox__button button,
.chatbox__button button:focus,
.chatbox__button button:visited {
  padding: 10px;
  background: white;
  border: none;
  outline: none;
  border-top-left-radius: 50px;
  border-top-right-radius: 50px;
  border-bottom-left-radius: 50px;
  box-shadow: 0px 10px 15px rgba(0, 0, 0, 0.1);
  cursor: pointer;
}

and here’s app.py to run the application:

from flask import Flask, render_template, request, jsonify

from chat import get_response

app = Flask(__name__)

@app.get("/")
def index_get():
    return render_template("base.html")

@app.post("/predict")                   
def predict():
   text = request.get_json().get("message")
   # TO-DO: check if text is valid
   response = get_response(text)
   message = {"answer": response}
   return jsonify(message)

if __name__ == "__main__":
    app.run(debug=True, port=5001)

and here’s chat.py:

import random
import json


import torch

from model import NeuralNet
from nltk_utils import bag_of_words, tokenize

device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

with open('intents.json', 'r') as json_data:
    intents = json.load(json_data)

FILE = "data.pth"
data = torch.load(FILE)

input_size = data["input_size"]
hidden_size = data["hidden_size"]
output_size = data["output_size"]
all_words = data['all_words']
tags = data['tags']
model_state = data["model_state"]

model = NeuralNet(input_size, hidden_size, output_size).to(device)
model.load_state_dict(model_state)
model.eval()

bot_name = "Sam"

def get_response(msg):
    sentence = tokenize(msg)
    X = bag_of_words(sentence, all_words)
    X = X.reshape(1, X.shape[0])
    X = torch.from_numpy(X).to(device)

    output = model(X)
    _, predicted = torch.max(output, dim=1)

    tag = tags[predicted.item()]

    probs = torch.softmax(output, dim=1)
    prob = probs[0][predicted.item()]
    if prob.item() > 0.75:
        for intent in intents['intents']:
            if tag == intent["tag"]:
                return random.choice(intent['responses'])
    
    return "I do not understand..."


if __name__ == "__main__":
    print("Let's chat! (type 'quit' to exit)")
    while True:
        # sentence = "do you use credit cards?"
        sentence = input("You: ")
        if sentence == "quit":
            break

        resp = 'hello'
        print(resp)

I have thoroughly inspected my JavaScript code and the HTML structure, but I couldn’t find any issues. The code responsible for updating the chatbox seems to be functioning correctly, yet the chatbot’s replies don’t appear on the web page.

I suspect the issue might be related to the integration between Flask and JavaScript. The Flask server receives the user’s input, processes it, and returns a response, but for some reason, the response is not being displayed in the chatbox on the web page.

I would greatly appreciate any insights or suggestions on how to troubleshoot and resolve this issue. Thank you in advance for your help!

Why is the #reset button executing the Flow.reset() function even if #gameboard has no children elements?

When i click on resetBtn it always executes Flow.reset function, no matter if gameboard has children elements or not, am i using the hasChildNodes() incorrectly?

const resetBtn = document.querySelector('#reset');
resetBtn.addEventListener('click', () => {
  if (document.getElementById('gameboard').hasChildNodes()) {
    Flow.reset();
   } 
  else {
    return;
  }
});

Html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" type="text/css" href="styles.css">
    <script src="main.js" type="text/javascript"></script>
    <title>Tic Tac Toe</title>
</head>
<body>
    <div class="mainContainer">
        <h1 class="header">Tic Tac Toe</h1>
        <div class="gameBoard" id="gameboard">

        </div>
        <div class="names-container">
            <input type="text" id="player1" placeholder="Player 1 Name">
            <input type="text" id="player2" placeholder="Player 2 Name">
        </div>
        <div class="buttonsContainer">
            <button class="startBtn" id="start" class="name-field">Start</button>
            <button class="resetBtn" id="reset" class="name-field">Reset</button>
        </div>
        <div class="message"></div>
        <div class="resultsContainer"></div>
    </div>
</body>
</html>

Difference between Javascript property with and without underscore

I saw the below code at https://javascript.info/class

Its constructor is taking the name parameter and assigning it to this.name. Then in the getter named name it is returning this._name. Why does this code work even when the constructor is not using this._name to set the value?

class User {

  constructor(name) {
    // invokes the setter
    this.name = name;
  }

  get name() {
    return this._name;
  }

  set name(value) {
    if (value.length < 4) {
      alert("Name is too short.");
      return;
    }
    this._name = value;
  }

}

let user = new User("John");
alert(user.name); // John

user = new User(""); // Name is too short.

Next dynamic route & Link behaviour

Say I have a page pages/shop/[[...slug]].js

I visit this page pages/shop/beer where beer is the slug.

The first way, if I type this URL into the address bar and hit enter, from the middleware I get request.NextUrl.href === "http://localhost:3000/pages/shop/beer", no problem it works as expected.

The second way, I have a <Link href="pages/shop/beer" as="pages/shop/beer"> from the home page, however, this time it prints out like this "http://localhost:3000/pages/shop/beer?slug=beer" Where is the ?slug=beer from? I wasn’t expecting that.

If the route prints /[[...slug]].js?slug=beer I can understand why, okay we don’t know what slug is, so we attached it to the url query params. But since we already know it is beer, why does the route have to be beer?slug=beer? That looks unnecessary & inconstant, I can prove that with the First way, with slug it works anyway.

Is there a way to tell Next <Link> not to add that slug?

Change input quantity step value for variable product based on variable weight in WooCommerce

I would like the quantity selector value to dynamically change based on the weight we specify in the variation section for variable products. Referring to the provided image,

screen.png

when we set the weight of the product to 0.5 kg, the product quantity selector should begin at 0.5. Similarly, if we set the weight to 1 kg, the quantity selector should start from 1. Furthermore, the quantity selector should adjust accordingly for each weight value we define.
So based on my code when user click on 0.5 (only number less than 1), user can not add to cart and appeared an message ” please select a value that is no less than one.”

here is my code:

(function($) {
    // Store the initial quantity step
    var initialStep = parseFloat($('input[name="quantity"]').attr('step'));

    // Wait for the variation to be selected
    $(document).on('found_variation', function(event, variation) {
        // Get the selected variation weight
        var variationWeight = parseFloat(variation.weight);

        // Update the default quantity selector value based on the selected variation weight
        $('input[name="quantity"]').val(variationWeight);

        // Update the step value to match the selected variation weight
        $('input[name="quantity"]').attr('step', variationWeight);

        // Trigger a change event on the quantity selector to update the displayed value
        $('input[name="quantity"]').trigger('change');
    });

    // Reset the quantity step to its initial value when a different variation is selected
    $(document).on('reset_data', function() {
        $('input[name="quantity"]').attr('step', initialStep);
    });
})(jQuery);

Line Invisible on Continuous Update of End Coordinate

I’m attempting to draw a line using React and D3. Lines are being drawn but it is not visible/not updating itself continuously as the mouse pointer is being dragged (changes the end point of line) with the button down. On the mouse-button-up it does appear as expected. Made many attempts to correct but no solution yet. Source code is given below. Your help will be greatly appreciated.

import * as d3 from 'd3';
import { useEffect, useRef, useState } from 'react';

let x;
let y;
const point = { x, y };
const pairOfPoints = { start: point, stop: point };

export default function PointerDraw() {
  const [lineCoords, setLineCoords] = useState(pairOfPoints);
  const [lines, setLines] = useState([]);
  const [startingPoint, setStartingPoint] = useState(point);
  const isDrawingNow = useRef(false);

  function handleDownEvent(event) {
    isDrawingNow.current = true;
    const currentLocation = d3.pointer(event);
    setStartingPoint({ x: currentLocation[0], y: currentLocation[1] });
    const linesSvg = d3.select('.lines');

    linesSvg
      .append('line')
      .attr('x1', currentLocation[0])
      .attr('y1', currentLocation[1])
      .attr('x2', currentLocation[0])
      .attr('y2', currentLocation[1])
      .attr('id', 'lineInProgress');
  }

  function handleMoveEvent(event) {
    if (isDrawingNow.current) {
      const currentLocation = d3.pointer(event);
      setLineCoords({
        start: startingPoint,
        stop: {
          x: currentLocation[0],
          y: currentLocation[1],
        },
      });
    }
  }

  function handleUpEvent() {
    isDrawingNow.current = false; // stop drawing
    setLines([...lines, lineCoords]);
  }

  useEffect(() => {
    const linesSvg = d3.select('.lines');

    linesSvg
      .selectAll('line')
      .data(lines)
      .join('line')
      .attr('x1', (d) => d.start.x)
      .attr('y1', (d) => d.start.y)
      .attr('x2', (d) => d.stop.x)
      .attr('y2', (d) => d.stop.y)
      .attr('id', '')
      .attr('stroke', 'blue')
      .attr('stroke-width', '3px');
  }, [lines]);

  useEffect(() => {
    const liveLine = d3.select('#lineInProgress');
    if (liveLine) {
      // this is the line being drawn
      liveLine
        .attr('x1', lineCoords.start.x)
        .attr('y1', lineCoords.start.y)
        .attr('x2', lineCoords.stop.x)
        .attr('y2', lineCoords.stop.y);
    }
  }, [lineCoords]);

  return (
    <g
      className="lines"
      onMouseDown={handleDownEvent}
      onMouseUp={handleUpEvent}
      onMouseMove={handleMoveEvent}
    >
      <rect x={1} y={1} width={800} height={600} fill="lightgrey" />
    </g>
  );
}

Above component is used by the code below:

import './App.css';
import PointerDraw from './PointerDraw';

export default function App() {
  return (
    <div className="App">
      <h1>Click and drag to draw</h1>
      <svg width="1920" height="1080">
        <PointerDraw />
      </svg>
    </div>
  );
}

How to find a previous file based on the filename’s pattern with javascript

This seems like it would be a popular thing to do, but I’m not finding any examples.

Give a folder with a series of files, all of which start with a timestamp:

YYYY-MM-DD_SomeOtherText.md

In the app I’m running, Obsidian, when I create a note, I have it set up so it will automatically prepend the date to the filename. It also has the ability to run javascript code during the creation of a new file.

I want to find the previous note on that subject and have it saved as a variable so that I can reference it in my template.

For instance, if I create a note “Standup”, it will create a file called “2023-07-05_Standup.md”. I need to run some javascript to find the previously most recent “Standup” file. Usually, this would just be the previous day, but since 2023-07-04 was a holiday it wouldn’t exist and I would actually need “2023-07-03_Standup.md”. Same for any note created on a Monday will not have the Sunday or Saturday files.

I picture the flow would be something like:

  1. Get a glob of all notes in the folder with the “????-??-??_SomeOtherText” filename format.
  2. Sort the list in descending order.
  3. Get the name of the SECOND value on the list (Presumably, the first record would be the note I’m now creating).
  4. Save the title of previous version of the note as a variable.
  5. In the template for the file, create a link to the previous note.

I can’t use metadata, like date created or modified, as sometimes files will be created out of order, or in mass. And files will get modified all the time; like marking a task from three weeks ago complete.

how to reduce an array of objects in typescript

Im trying to turn this:

[{price: 30, date: 05}, {price: 60, date: 05}, {price: 40, date: 06}, {price: 70, date: 06}  ]

into this:

[{price: 90, date: 05}, {price 110, date: 06}]

Does anyone know how to do this in typescript? I keep getting the following error for trying to type the initial value:

  Types of parameters 'acc' and 'previousValue' are incompatible.

Here’s what I tried:

  const reducedExpenses = expenses.reduce(
    (acc, { date, price }, index) => {
      if (acc[index].date === date) {
        return { ...acc[index], price: (acc[index].price += price) };
      }
      return acc.push({
        date,
        price,
      });
    },
    [{ date: -1, price: 0 }]
  );

Javascript fails to re-add an animation class

Here with a new predicament. Basically, a function in my code, langpicker(), is supposed to remove and add a class responsible for animating it upon clicking an option. It does so flawlessly on the sectionlang2 element, though successive calls seem to fail on its sibling element (sectionlang1).
My investigation efforts so far:

  1. Checking the identifiers’ integrity (for misspellings, et cetera);
  2. Checking for logical errors;

So far, no good. My efforts have been unable to identify the issue.

HOW TO TRIGGER:

Trigger the onclick events by clicking on any options present (at least twice)*

var check = 0;

function startAnimation1(){
    animationForwarder('animelem1-1', 'animelem1-2', 'anim-1', 0)
    document.getElementById('animelem1-1').classList.remove('dp-none');
    document.getElementById('animelem1-2').classList.remove('dp-none');
    const animelem13 = document.getElementById('animelem1-3');
    animelem13.classList.add('anim-1');
    animelem13.classList.remove('dp-none');
    setTimeout(animationForwarder, 330, 'headertext', 'skip', 'headertexttransformed', 0);
}
function anFrwdPreset(){
    if(check == 0){
        check = 1;
        anFrwdDoubleExec('sectionlang1', 'sectionlang2', 'slash1-lang', 'slash2-lang', 'anim-fadeintb', 250, 'anim-blink', 100, 0)
    }
}
function langPicker(buttonid){
    const checkOpacity = document.getElementById('sectionlang1');
    const sectionlang2 = document.getElementById('sectionlang2');
    if(checkOpacity.classList.contains('op-0') == true){
        checkOpacity.classList.remove('op-0');
        sectionlang2.classList.remove('op-0');
    }
    if(sectionlang2.classList.contains('anim-switchByFadeIn-lang') == true){
        checkOpacity.classList.remove('anim-fadeintb');
        sectionlang2.classList.remove('anim-fadeintb');
        checkOpacity.classList.remove('anim-switchByFadeIn-lang');
        sectionlang2.classList.remove('anim-switchByFadeIn-lang');
    }
    animationForwarder('sectionlang1', 'sectionlang2', 'anim-switchByFadeIn-lang', 100)
    setTimeout(langTextChange, 510, buttonid);
}
function langTextChange(buttonid){
    const subject1 = document.getElementById('sectionlang1p2');
    const subject2 = document.getElementById('sectionlang2');
    if(buttonid == 'cpp'){
        subject1.textContent = 'C++';
        subject2.textContent = 'C++ is one of the most renowned programming languages in the world, used in making a wide variety of programs/apps, mostly direct executables. I want to be fully competent at it, even though it is not one of languages I`ll need as a webmaster.'
    }
    if(buttonid == 'js'){
        subject1.textContent = 'Javascript';
        subject2.textContent = 'Javascript is one of my recent `discoveries`. Immediately after I dived into it, determination appeared. Being very powerful, my persona constantly tries to learn something new about it to empower my web developer adventures with fancy techniques.'
    }
    if(buttonid == 'html'){
        subject1.textContent = 'HTML';
        subject2.textContent = 'HTML`s my personal favorite for obvious reasons: its potential for shaping something creative with its association, nesting style and overall not limiting my imagination. I just love doing stuff in it.'
    }
    if(buttonid == 'css'){
        subject1.textContent = 'CSS';
        subject2.textContent = "There's not much to say here except for that it's a critical extension of html, or at least I perceive it as. Being a key to expressing one's power, I admire the fact that I'll carry it between the palms of my hand till the very end."
    }
}
function anFrwdDoubleExec(identifier, identifier2, identifier3, identifier4 ,anim_id, wait_time, anim_id2, wait_time2, secondExecutionWaitTime){
    animationForwarder(identifier, identifier2, anim_id, wait_time);
    setTimeout(() => {animationForwarder(identifier3, identifier4, anim_id2, wait_time2)}, secondExecutionWaitTime);
}
function animationForwarder(identifier, identifier2, anim_id, wait_time){
    document.getElementById(identifier).classList.add(anim_id);
    if(identifier2 != 'skip'){
        setTimeout(() => {document.getElementById(identifier2).classList.add(anim_id)}, wait_time);
    }
}
.nonstandard-draft{
    max-width: 15vw;
    position: fixed;
    bottom: 1.7vw;
    right: 1.7vw;
}
.nonstandard-draft-head{
    font-size: 1.7vw;
    display: inline-block;
    font-family: Belanosima;
    margin: 0 0 1.2vw 0;
}
.nonstandard-draft-data{
    font-family: Belanosima;
    font-size: 1vw;
    white-space: pre-line;
    color: #0d8569;
    filter: drop-shadow(0.1vw 0.1vw 0.2vw black);
}
.grd-draft1{
    background-image: linear-gradient(to right bottom, #f269e0, #ff5d9a, #ff864b, #ffbd00, #a8eb12);
    filter: drop-shadow(0vw 0vw 0.1vw #ff864b);
}
.grd-draft2{
    background-image: linear-gradient(to right bottom, #8f00ff, #007aff, #0091ef, #009994, #009950);
    filter: drop-shadow(0vw 0vw 0.1vw #0091ef);
}

.w-100p{
    width: 100%;
}
.w-97p{
    width: 97%;
}
.w-14p75{
    width: 14.75vw;
}
.w-9p5{
    width: 9.5vw;
}
.h-100p{
    height: 100%;
}
.header{
    background-image: url('../RESOURCES/bgs/bg12.gif');
    height: 100vh;
}
.fx{
    display: flex;
}
.fx-jc-spb{
    justify-content: space-between;
}
.fx-ai-c{
    align-items: center;
}
.fx-jc-c{
    justify-content: center;
}
.fx-dir-c{
    flex-direction: column;
}
.mg-none{
    margin: 0;
}
#headertext{
    width: 0vw;
    overflow: hidden;
}
.headertexttransformed{
    width: 48.5709375vw !important;
    transition: all 0.5s ease-in;
}  
.b-sh-def{
    box-shadow: 0vw 0vw 0.3vw grey;
}
.bgs-full{
    background-size: 100% 100%, 100% 100%;
}
.headerimage{
    width: 12vw;
    margin-right: 3.25vw;
    image-rendering: pixelated;
}
.headergradienttext{
    white-space: nowrap;
    font-size: 3vw;
}
.ff-raleway{
    font-family: Raleway;
}
.grd-firsth1{
    background-image: linear-gradient(to right bottom, #000000, #202020, #3a3a3a, #565656, #747474);
    filter: drop-shadow(0.1vw 0.1vw 0.01vw #3a3a3a);
}
.grd-sech1{
    background-image: linear-gradient(to right bottom, #00e377, #00e395, #00e2af, #00e1c3, #00ded3);
    filter: drop-shadow(0.1vw 0.1vw 0.01vw #00e377);
}
.gradient-text{
    background-clip: text;

    -webkit-background-clip: text;
    -o-background-clip: text;
    -moz-background-clip: text;

    -webkit-text-fill-color: transparent;
    -o-text-fill-color: transparent;
    -moz-text-fill-color: transparent;
}
.fc-violet{
    color: blueviolet;
}
.nav{
    position: fixed;
    background-color: white;
    top: 1vw;
    left: 50%;
    transform: translateX(-50%);
    border-radius: 10vw;
    z-index: 1;
}
.dp-none{
    display: none;
}
.wh-animelem-1{
    width: 1.5vw;
    height: 1.5vw;
}
.wh-animelem-1-addr-1{
    top: -2vw;
    left: -2vw;
}
.wh-animelem-1-addr-2{
    bottom: -2vw;
    right: -2vw;
    transform: rotate(180deg);
}
.headercontainer{
    margin: 3.25vw;
}

.fsz-1{
    font-size: 1vw;
}

@keyframes anim1{
    0%{
        opacity: 0;
    }
    33.3%{
        opacity: 1;
    }
    66.6%{
        opacity: 0;
    }
    99.9%{
        opacity: 1;
    }
}

.abm-section{
    height: 74vh;
    padding: 10vh 0;
}
.bs-bb{
    box-sizing: border-box;
}

.anim-1{
    animation: anim1 0.25s ease-in-out;
}
.nest-subsection{
    width: 80vw;
    max-width: 1250px;
}
.general-subsection{
    max-height: calc(70vh - 80px);
}
.add-text-subsection{
    font-family: Belanosima;
}
.t-ss-heading{
    font-size: 48px;
    margin-bottom: 32px;
}
.t-ss-paragraph{
    font-size: 34px;
    filter: drop-shadow(0.2vw 0.2vw 0.1vw #a6a6a6);
}
.t-ss-paragraph-modabm{
    width: 671.438px;
}
@keyframes blink{
    0%{
        opacity: 0;
    }
    50%{
        opacity: 1;
    }
    100%{
        opacity: 0;
    }
}
.anim-blink{
    animation: blink 1.25s infinite;
}
.pfp{
    height: 340px;
    width: auto;
    border-radius: 15%;
    margin-left: 70px;
}
@keyframes fadeInTR{
    from{
        transform: translateX(-75px);
    }
    to{
        opacity: 1;
    }
}
.anim-fadeintr{
    animation: fadeInTR 0.75s ease-out forwards;
}
.scroll-alternative{
    overflow-y: scroll;
}
.scroll-alternative::-webkit-scrollbar{
    display: none;
}
.op-0{
    opacity: 0;
}
.border{
    height: 40px;
    background: url('../RESOURCES/SHWHP_RES/greeceborder.png'), white;
    background-size: contain;
}
.abm-bgp{
    background: url('../RESOURCES/SHWHP_RES/bg-content/bg-master.png'), white;
    background-size: auto 100%;
    background-repeat: no-repeat;
    background-position: bottom left;
}
.longsec-child-preset{
    height: 70vh;
    color: #f3f3f3;
}
.general-heading{
    font-size: 54px;
}
.add-lang-paragraph{
    text-align: left;
    font-size: 24px;
    margin-top: 30px;
}
@keyframes fadeInTB{
    from{
        transform: translateY(-30px);
    }
    to{
        opacity: 1;
    }
}
.anim-fadeintb{
    animation: fadeInTB 0.75s cubic-bezier(.2,1,1,1) forwards;
}
.bg-long{
    background-image: url('../RESOURCES/bgs/bg14.jpg');
}
.longer-subsection{
    max-height: 100%;
}
.lang-pick{
    width: 225px;
    height: 225px;
    padding:20px;
    flex-wrap: wrap;
    transform: rotate(45deg);
}
.lang-pick-opt{
    width: 94px;
    height: 94px;
    margin: 9.25px;
    border-radius: 12px;
    flex-shrink: 0;
}
.cpp{
    background-image: radial-gradient(circle, #5ffbf1, #64fcec, #69fde7, #6ffde1, #76fedc, #83fed7, #8fffd3, #9affcf, #abffcd, #baffcc, #c7ffcc, #d3ffce);
}
.cpp::before{
    content: '';
    width: 71.27659574468085%;
    height: 6px;
    transform: scaleX(0);
    transform-origin: left;
    position: absolute;
    top: -20px;
    left: -20px;
    background-color: #08fcbc;
    transition: transform 0.1s ease-in;
}
.lang-pick:hover .cpp::before{
    transform: scaleX(1);
    transform-origin: left;
    transition: transform 0.1s ease-out;
}
.cpp::after{
    content: '';
    width: 6px;
    height: 71.27659574468085%;
    transform: scaleY(0);
    transform-origin: top;
    position: absolute;
    top: -20px;
    left: -20px;
    background-color: #08fcbc;
    transition: transform 0.1s ease-in;
}
.lang-pick:hover .cpp::after{
    transform: scaleY(1);
    transform-origin: top;
    transition: transform 0.1s ease-out;
}
.html{
    background-image: radial-gradient(circle, #e28ef6, #d884f4, #ce7af2, #c470f0, #b867ef, #af66ef, #a564f0, #9b63f0, #9168f2, #876df4, #7d72f5, #7376f5);
}
.html::before{
    content: '';
    width: 71.27659574468085%;
    height: 6px;
    transform: scaleX(0);
    transform-origin: right;
    position: absolute;
    top: -20px;
    right: -20px;
    background-color: #08fcbc;
    transition: transform 0.1s ease-in;
}
.lang-pick:hover .html::before{
    transform: scaleX(1);
    transform-origin: right;
    transition: transform 0.1s ease-out;
}
.html::after{
    content: '';
    width: 6px;
    height: 71.27659574468085%;
    transform: scaleY(0);
    transform-origin: top;
    position: absolute;
    top: -20px;
    right: -20px;
    background-color: #08fcbc;
    transition: transform 0.1s ease-in;
}
.lang-pick:hover .html::after{
    transform: scaleY(1);
    transform-origin: top;
    transition: transform 0.1s ease-out;
}
.css{
    background-image: radial-gradient(circle, #5ffbf1, #46eefa, #41dfff, #52cffe, #69bff8, #79b3f4, #8aa7ec, #9a9ae1, #aa8fd8, #ba83ca, #c777b9, #d16ba5);
}
.css::before{
    content: '';
    width: 71.27659574468085%;
    height: 6px;
    transform: scaleX(0);
    transform-origin: left;
    position: absolute;
    bottom: -20px;
    left: -20px;
    background-color: #08fcbc;
    transition: transform 0.1s ease-in;
}
.lang-pick:hover .css::before{
    transform: scaleX(1);
    transform-origin: left;
    transition: transform 0.1s ease-out;
}
.css::after{
    content: '';
    width: 6px;
    height: 71.27659574468085%;
    transform: scaleY(0);
    transform-origin: bottom;
    position: absolute;
    bottom: -20px;
    left: -20px;
    background-color: #08fcbc;
    transition: transform 0.1s ease-in;
}
.lang-pick:hover .css::after{
    transform: scaleY(1);
    transform-origin: bottom;
    transition: transform 0.1s ease-out;
}
.js{
    background-image: radial-gradient(circle, #b1ff00, #90ff4e, #6eff76, #4dff97, #2affb4, #4bf6a6, #5eee98, #6ce58c, #8fcf5b, #a9b72f, #be9c05, #ce7e00);
}
.js::before{
    content: '';
    width: 71.27659574468085%;
    height: 6px;
    transform: scaleX(0);
    transform-origin: right;
    position: absolute;
    bottom: -20px;
    right: -20px;
    background-color: #08fcbc;
    transition: transform 0.1s ease-out;
}
.lang-pick:hover .js::before{
    transform: scaleX(1);
    transform-origin: right;
    transition: transform 0.1s ease-out;
}
.js::after{
    content: '';
    width: 6px;
    height: 71.27659574468085%;
    transform: scaleY(0);
    transform-origin: bottom;
    position: absolute;
    bottom: -20px;
    right: -20px;
    background-color: #08fcbc;
    transition: transform 0.1s ease-out;
}
.lang-pick:hover .js::after{
    transform: scaleY(1);
    transform-origin: bottom;
    transition: transform 0.1s ease-out;
}
.cur-sel{
    cursor: pointer;
}
.whp-icon{
    width: 74%;
    height: 74%;
    transition: all 0.15s;
}
.whp-icon:hover{
    width: 80%;
    height: 80%;
    transition: all 0.15s;
}
.pos-abs{
    position: absolute;
}
.p-ab-center{
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}
.pos-rel{
    position: relative;
}
.anim-switchByFadeIn-lang{
    animation: switchByFadeIn-lang 1s forwards;
}
@keyframes switchByFadeIn-lang{
    50%{
        transform: translateX(100px);
        opacity: 0;
    }
    50.001%{
        transform: translateX(-100px);
    }
    100%{
        transform: translateX(0px);
        opacity: 1;
    }
}
#sectionlang1p2{
    display: inline;
}
<section onmouseover="anFrwdPreset()" class="longsec-child-preset w-100p fx fx-jc-c fx-ai-c fx-dir-c">
                <div class="nest-subsection fx fx-jc-spb fx-ai-c">
                    <div class="longer-subsection add-text-subsection fx-dir-c bs-bb">
                        <div id="sectionlang1" class="op-0">
                            <span id="slash1-lang" class="mg-none general-heading">/</span><span id="slash2-lang" class="mg-none general-heading">/&nbsp;</span><h1 id="sectionlang1p2" class="mg-none general-heading">Languages: the keys</h1>
                        </div>
                        <p id="sectionlang2"class="t-ss-paragraph t-ss-paragraph-modabm add-lang-paragraph ff-raleway mg-none op-0">Languages serve as my trump cards: they are like limbs for every programmer. And I am no exception! Here you can review the aforementioned.</p>
                    </div>
                    
                    <div class="lang-pick fx">
                        <div id="cpp" onclick="langPicker('cpp')" class="lang-pick-opt bgs-full cpp pos-rel cur-sel pickermarker">
                            <img src="RESOURCES/SHWHP_RES/cpp_b&w.png" class="whp-icon pos-abs p-ab-center" alt="C++">
                        </div>
                        <div id="html" onclick="langPicker('html')" class="lang-pick-opt bgs-full html pos-rel cur-sel pickermarker">
                            <img src="RESOURCES/SHWHP_RES/html_b&w.png" class="whp-icon pos-abs p-ab-center" alt="HTML">
                        </div>
                        <div id="css" onclick="langPicker('css')" class="lang-pick-opt bgs-full css pos-rel cur-sel pickermarker">
                            <img src="RESOURCES/SHWHP_RES/css_b&w.png" class="whp-icon pos-abs p-ab-center" alt="CSS">
                        </div>
                        <div id="js" onclick="langPicker('js')" class="lang-pick-opt bgs-full js pos-rel cur-sel pickermarker">
                            <img src="RESOURCES/SHWHP_RES/js_b&w.png" class="whp-icon pos-abs p-ab-center" alt="JS">
                        </div>
                    </div>
                </div>
            </section>

How to find all posts that have an author who is already in the friends array?

I’m trying to grab all posts from the user’s friends list and populate them from newest to oldest.

Controller function to grab all friends posts:

async function indexByFriends(req, res) {
  const { handle } = req.user;
  console.log('handle:', handle);
  try {
    const userProfile = await Profile.findOne({ handle });
    console.log('friends handles:', userProfile.friends);

    const posts = await Post.find({ author: { $in: userProfile.friends } })
      .sort({ createdAt: -1 })
      .populate({
        path: 'author',
        select: '-_id -__v',
        match: { handle: { $in: userProfile.friends } }
      });

    res.json(posts);
  } catch (err) {
    console.error(err);
    res.status(500).json(err);
  }
}

Profile model:

import mongoose from 'mongoose'

const Schema = mongoose.Schema

const profileSchema = new Schema({
  name: String,
  photo: String,
  handle: String,
  friends: [{
    type: String,
    ref: 'Profile'
  }],
  friendRequests: [String],
  bio: String,
  followers: [String],
  following: [String],
  blocked: [String],
  verified: Boolean,
  posts: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Post'
  }],
  restaurants: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Restaurant'
  }]
}, {
  timestamps: true,
})

const Profile = mongoose.model('Profile', profileSchema)

export { Profile }

Post model:

import mongoose from 'mongoose'

const Schema = mongoose.Schema

const postSchema = new Schema({
  name: String,
  photo: String,
  meal: String,
  review: String,
  title: String,
  rating: Number,
  description: String,
  author: { 
    type: String, 
    ref: 'Profile',
    field: 'handle' 
  },
  resturant: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Post'
  }},{
  timestamps: true,
})

const Post = mongoose.model('Post', postSchema)

export { Post }

When I run that function I am retunred a 500 error:
GET /api/posts/friends 500 131.701 ms - 258 as well as CastError: Cast to ObjectId failed for value "SBBGameMaster" (type string) at path "_id" for model "Profile"

My json data look like this:

{
    "stringValue": ""SBBGameMaster"",
    "valueType": "string",
    "kind": "ObjectId",
    "value": "SBBGameMaster",
    "path": "_id",
    "reason": {},
    "name": "CastError",
    "message": "Cast to ObjectId failed for value "SBBGameMaster" (type string) at path "_id" for model "Profile""
}

I understand that the issue is around using the handle instead of the object_id. I want to use the handle to make it easier to look up users based on the handle, instead of having to type the users _id.

I’d expect to see a list of all posts who’s author is a handle of a user that is also in the user’s friend array.