TypeScript equivalent of Kotlin Array.any()

I’m still very new to TypeScript. Working on a Next.js project I am displaying a list of grouped items with titles. If there are no items in the group meeting a specific criteria, I don’t want to display the title or the items. I’ve used the Kotlin Array.any() function before as a way of saying “if any item matches this criteria, include it”. I haven’t found a way to do this in TypeScript directly. In the example below, I’d like to keep the Item list if any of it’s items have a someProp = null. The only way I’ve found to do this, so far, is using !Array.every() but the double negative “not every item.someProp not null” is really heavy on the cognitive complexity. Is there a better way to accomplish this?

{Object.entries(groupedListData!)
  .filter(
    ([key, itemsGroupedByKey]) =>
      !itemsGroupedByKey.every(
        (item) => item.someProp !== null
      )
    )
  ...

I’ve read over all of the Array functions and none of them seem to really fit. I’ve also read about extending the built-in Array class with my own any() but that seems a bit heavy-handed. I won’t need this for more than a few spots in the code.

how to create edit function in my todo app vanilla js? [closed]

I have created a todo list with title , description, due dates and priority. But when i click on edit button it waits for 2nd click for editing. How to correctly write edit function for editing form and displays the updated (edited) task.

https://jsfiddle.net/n9ghxbqs/

function addTask(){
  let title = document.querySelector('#title').value
  let des = document.querySelector('#des').value
  let duedate = document.querySelector('#due-date').value
  let priority = document.querySelector('#priority').value

  let newlist = new Todo(title,des,duedate,priority)

  myTodoList.push(newlist)
  render()
  todoForm.style.display = 'none'
}

function render(){
 let list = document.querySelector('.list')
 list.innerHTML = ''

 for(let i = 0; i < myTodoList.length;i++){
  let task = myTodoList[i]
  
  let todoEl = document.createElement('div')
    todoEl.innerHTML = `<div class="book-grid">
      <h3>${task.title}</h3>
      <p>${task.des}</p>
      <p>${task.duedate}due </p>
      <p>${task.priority} priority</p> 

      <div class="f-btn">
      <button class="status" onclick="edit()">edit
      </button>
      <button class="delete" >
        Remove
      </button>
      </div>`;

      list.appendChild(todoEl)

 }

}

function edit(){
  let edit = document.querySelectorAll('.status')

  edit.forEach((editbtn)=>{
    editbtn.addEventListener('click',(e)=>{
      if(e.currentTarget){
        addTodo()
       
      }
    })
  })
 
} ```


 

How do I use a variable as a js object key when searching? [duplicate]

I have a simple program I’m testing where I take a user’s input ID and use that to gather data from a js object; however, I can’t seem to figure out how to use a variable as search for this.

Here’s the current code:
index.html

...
<body>
  <p id="displayTest">...</p>
  <input id="inputTextbox" type="text"></input>
  <input type="submit" onclick="submitID()"></input>
  <script src="script.js"></script>
</body>
...

script.js

var root = {
             data:{
               7292:{
                 ...
               },
               8323:{
                 ...
               }
            }
var display = document.getElementByID("displayTest");
var input = document.getElementByID("inputTextbox");
function submitID() {
  if ((input.value) in (root.data)) {
    display.innerText = root.data/*.the user's input.somedata*/;
    } else {
    display.innerText = "failed :(";
    }
}

It’s important that I am able to get the key in a search like this (root.data.User’s Input.somedata) so that I am able to access data under the input ID; for example, name.

I’ve searched around and still couldn’t find it, so I started trail and error, here’s a few of things I’ve tried:

...
var inputID = input.value;
function submitID() {
  ...
  display.innerText = root.data.inputID.somedata; /*Doesn't return anything, I believe it's looking for a key called 'inputID'*/
  ...
  }
...
...
function submitID() {
  ...
  display.innerText = root.data.${inputID}.somedata; /*Identifier Expected syntax error*/
  ...
  }
...
...
function submitID() {
  ...
  display.innerText = root.data.(input.value).somedata; /*Identifier Expected syntax error*/
  ...
  }
...

I can’t think of any other way to do this, and searching the web has been to no avail, so I turn to you, stackoverflow community, to help me.

Thanks in advance!

TDLR: I am trying to use a variable input as the key for a search in a js object

The code I wrote here is handwritten (not copy/pasted, so it may differ from the actual project), and is written on a device I am unfamiliar with. Assume misspellings & common syntax errors are fixed in project.

Google Gantt Chart in Razor Pages with SQL [closed]

Google Gantt charts… you’ve probably heard of them and maybe even used them. They are easy to toss into a Razor Page when you provide static data for the rows. But, I’m wanting to pull the data from a database table on our SQL server and populate the Gantt chart. I have read countless articles about using PHP and Json files, none have worked for me. I can get the data to populate an HTML table, so I know the data is there. I just can’t format it properly to show in the Gantt Chart. I won’t post any of my code because it’s really just a bunch of snippets of the stuff I’ve found online, including using Ajax calls to get data. Can someone please point me to a modern working example (with source code) of a Google Gantt chart pulling data from a database to successfully populate the chart? This can be in either Razor pages or Blazor. Any help would be greatly appreciated.

Also, I know there are a lot of commercial charts I can use for Razor and Blazor, but we can’t spend any money on this project, which is why I’m trying to use the Google Gantt chart.

Thank you for your time!

UPDATE:
I know I said I wouldn’t post code, but here are the generic example that works, and then the one I can’t get to work…

This one with static data in the rows works:

<h5 style="text-align: center"><u>Written Report Chart</u></h5>

<script type="text/javascript">
    google.charts.load('current', {'packages':['gantt']});
    google.charts.setOnLoadCallback(drawChart);

    function daysToMilliseconds(days) {
      return days * 24 * 60 * 60 * 1000;
    }

    function drawChart() {

      var data = new google.visualization.DataTable();
      data.addColumn('string', 'Task ID');
      data.addColumn('string', 'Task Name');
      data.addColumn('date', 'Start Date');
      data.addColumn('date', 'End Date');
      data.addColumn('number', 'Duration');
      data.addColumn('number', 'Percent Complete');
      data.addColumn('string', 'Dependencies');

      data.addRows([
        ['Research', 'Find sources',
         new Date(2015, 0, 1), new Date(2015, 0, 5), null,  100,  null],
        ['Write', 'Write paper',
         null, new Date(2015, 0, 9), daysToMilliseconds(3), 25, 'Research,Outline'],
        ['Cite', 'Create bibliography',
         null, new Date(2015, 0, 7), daysToMilliseconds(1), 20, 'Research'],
        ['Complete', 'Hand in paper',
         null, new Date(2015, 0, 10), daysToMilliseconds(1), 0, 'Cite,Write'],
        ['Outline', 'Outline paper',
         null, new Date(2015, 0, 6), daysToMilliseconds(1), 100, 'Research']
      ]);

      var options = {
        height: 275
      };

      var chart = new google.visualization.Gantt(document.getElementById('WrittenReport'));

      chart.draw(data, options);
    }
</script>

<div id="WrittenReport" class="chart-container"></div>
<div class="page-divider"></div>

And here is the code for the one that doesn’t work:
In my ‘CSHTML’…

@page
@model ProdSchedPortal.Pages._Testing.AnotherTestModel
@{
}

<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
<script type="text/javascript">
    google.charts.load('current', { 'packages': ['gantt'] });
    google.charts.setOnLoadCallback(drawChart);
    function drawChart() {
        $.ajax({
            type: "POST",
            url: "?handler=ListFilter",
            data: {},
            contentType: "application/json;charset=utf-8",
            dataType: "json",
            success: function (response) {
                // var data = new google.visualization.DataTable(JSON.parse(response));

                var data = new google.visualization.DataTable();
                data.addColumn('string', 'Task ID');
                data.addColumn('string', 'Task Name');
                data.addColumn('date', 'Start Date');
                data.addColumn('date', 'End Date');
                data.addColumn('number', 'Duration');
                data.addColumn('number', 'Percent Complete');
                data.addColumn('string', 'Dependencies');



                const formattedRows = response.map((row, index) => {
                    return [
                        String(index + 1),
                        `${row["OpDesc"]} (${row["JobNum"]})`,
                        new Date(row["StartDate"]),
                        new Date(row["JobOper_DueDate"]),
                        null,
                        parseFloat(row["EstProdHours"]) || 0,
                        null
                    ];
                });
                data.addRows(formattedRows);



                // for (var i = 0; i < response.d.length; i++) {
                //     var taskId = response.d[i][0].toString();
                //     var taskname = response.d[i][1].toString();
                //     var resource = response.d[i][2].toString();
                //     var startDate = new Date(response.d[i][3]).toString();
                //     var enddate = new Date(response.d[i][4]).toString();
                //     var duration = parseInt(response.d[i][5]);
                //     var percentageComplete = parseInt(response.d[i][6]);
                //     var dependencies = response.d[i][7].toString();
                //     data.addRows([[taskId, taskname, resource, startDate, enddate, duration, percentageComplete, dependencies]]);
                // }

                var options = {
                    height: 400, width: 500, gantt: { trackHeight: 30 }
                };

                var chart = new google.visualization.Gantt(document.getElementById('chart_div'));
                chart.draw(data, options);
            }, error: function (response) {
                alert(response.responseText);
            }
        });
    }
</script>

<div id="chart_div" style="height: 180px; margin-top: 50px; border: 1px solid dimgray;"></div>

And in my ‘HTML’, which I made static row data for this example…

public JsonResult? OnPostListFilter()
{
    // Get the DataTable from Database.
    DataTable dt = new DataTable();
    dt.Columns.Add("Task ID", typeof(string));
    dt.Columns.Add("Task Name", typeof(string));
    dt.Columns.Add("Resource", typeof(string));
    dt.Columns.Add("Start Date", typeof(string));
    dt.Columns.Add("End Date", typeof(string));
    dt.Columns.Add("Duration", typeof(int));
    dt.Columns.Add("Percent Complete", typeof(int));
    dt.Columns.Add("Dependencies", typeof(string));

    dt.Rows.Add("2014Spring", "Spring 2014", "spring", new DateTime(2014, 2, 22, 0, 30, 0), new DateTime(2014, 5, 20, 6, 30, 0), null, 100, null);
    dt.Rows.Add("2014Summer", "Summer 2014", "summer", new DateTime(2014, 5, 21, 6, 45, 0), new DateTime(2014, 8, 20, 7, 0, 0), null, 100, null);
    dt.Rows.Add("2014Autumn", "Autumn 2014", "autumn", new DateTime(2014, 8, 21, 7, 4, 0), new DateTime(2014, 11, 20, 7, 30, 0), null, 100, null);
    dt.Rows.Add("2014Winter", "Winter 2014", "winter", new DateTime(2014, 11, 21, 7, 30, 0), new DateTime(2015, 2, 21, 8, 30, 0), null, 100, null);
    dt.Rows.Add("2015Spring", "Spring 2015", "spring", new DateTime(2015, 2, 22), new DateTime(2015, 5, 20), null, 50, null);
    dt.Rows.Add("2015Summer", "Summer 2015", "summer", new DateTime(2015, 5, 21), new DateTime(2015, 8, 20), null, 0, null);
    dt.Rows.Add("2015Autumn", "Autumn 2015", "autumn", new DateTime(2015, 8, 21), new DateTime(2015, 11, 20), null, 0, null);
    dt.Rows.Add("2015Winter", "Winter 2015", "winter", new DateTime(2015, 11, 21), new DateTime(2016, 2, 21), null, 0, null);
    dt.Rows.Add("Football", "Football Season", "sports", new DateTime(2014, 8, 4), new DateTime(2015, 1, 1), null, 100, null);
    dt.Rows.Add("Baseball", "Baseball Season", "sports", new DateTime(2015, 2, 28), new DateTime(2015, 9, 20), null, 14, null);
    dt.Rows.Add("Basketball", "Basketball Season", "sports", new DateTime(2014, 9, 28), new DateTime(2015, 5, 20), null, 86, null);
    dt.Rows.Add("Hockey", "Hockey Season", "sports", new DateTime(2014, 9, 8), new DateTime(2015, 5, 21), null, 89, null);

    List<object> chartData = new List<object>();
    for (int i = 0; i < dt.Rows.Count; i++)
    {
        chartData.Add(new object[] {
    dt.Rows[i][0], dt.Rows[i][1], dt.Rows[i][2], dt.Rows[i][3],dt.Rows[i][4],
    dt.Rows[i][5]==DBNull.Value?0:dt.Rows[i][5], dt.Rows[i][6], dt.Rows[i][7]==DBNull.Value?"":dt.Rows[i][7]
});
    }

    return new JsonResult(chartData);
}

All I get is a blank alert and screen…

enter image description here

How to make Divs to appear side-by-side without changing the HTML structure, only using CSS

I want to pair each .dropdown-select div with its corresponding .phone div, such that:

  • The first .dropdown-select and the first .phone appear on the same
    line,
  • The second .dropdown-select and the second .phone appear on the
    next line, and so on.

The .dropdown-select will occupy 25% space and .phone will occupy 75% space.

The divs are generated dynamically, hence I cannot change the positioning or modify the HTML structure. Need to use CSS entirely for positioning. Tried different techniques, not successful.

And the divs with class as text should not be affected with flex(should not be changed) only divs with class dropdown-select and phone.

/* Main container that holds all the input fields */
.contactFormContainer .field__group {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 20px;
}

/* Ensure each .dropdown-select and .phone div takes up 50% of the container width */
.contactFormContainer .dropdown-select,
.contactFormContainer .phone {
  display: flex;
  flex-direction: column;
  width: calc(50% - 5px);
  /* Make sure each takes up 50% of the space with 5px margin */
}

/* Ensure input fields inside dropdown-select and phone take full width */
.contactFormContainer .dropdown-select__input,
.contactFormContainer .phone__container input {
  width: 100%;
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 4px;
}

/* The .text divs remain as block-level elements */
.contactFormContainer .text {
  display: block;
  margin-bottom: 10px;
}

/* Media Query for smaller screens */
@media (max-width: 768px) {
  .contactFormContainer .field__group {
    flex-direction: column;
    /* Stack everything vertically on small screens */
  }

  .contactFormContainer .dropdown-select,
  .contactFormContainer .phone {
    width: 100%;
    /* Full width for small screens */
  }
}
<div class="contactFormContainer">
  <div class="field__group">
    <div class="text">
      <label for="per_email_1">
            Personal eMail 1
          </label>
      <div class="text__count   ">
        <input type="Text" name="per_email_1" aria-label="per_email_2" id="per_email_1_a_1"
            placeholder="Personal eMail 2" minlength="8" maxlength="70" pattern="^[a-zA-Z0-9_%+-]+([a-zA-Z0-9_%+.-]+)@(?!.*?..)([a-zA-Z0-9_-.]+).([a-zA-Z]{2,5})"
            value="">
      </div>
    </div>
    <div class="text">
      <label for="per_email_2">
            Personal eMail 2
          </label>
      <div class="text__count   ">
        <input type="Text" name="per_email_2" aria-label="office_email_1" id="per_email_2_a_1"
            placeholder="Office Email1" minlength="8" maxlength="70" pattern="^[a-zA-Z0-9_%+-]+([a-zA-Z0-9_%+.-]+)@(?!.*?..)([a-zA-Z0-9_-.]+).([a-zA-Z]{2,5})"
            value="">
      </div>
    </div>
    <div class="text">
      <label for="per_email_3">
            Personal eMail 3
          </label>
      <div class="text__count   ">
        <input type="Text" name="per_email_3" aria-label="office_email_2" id="per_email_3_a_1"
            placeholder="Office Email 2" minlength="8" maxlength="70" pattern="^[a-zA-Z0-9_%+-]+([a-zA-Z0-9_%+.-]+)@(?!.*?..)([a-zA-Z0-9_-.]+).([a-zA-Z]{2,5})"
            value="">
      </div>
    </div>
    <div class="dropdown-select " id="mobile_1">
      <label for="mobile_1">
            Mobile No. 1
          </label>
      <div class="dropdown-container">
        <div class="dropdown-select__field ">
          <input type="text" class="dropdown-select__input" id="mobile_1_a_1" placeholder="">
        </div>
      </div>
      <span class="">
          </span>
    </div>
    <div class="dropdown-select " id="mobile_2">
      <label for="mobile_2">
            Mobile No. 2
          </label>
      <div class="dropdown-container">
        <div class="dropdown-select__field ">
          <input type="text" class="dropdown-select__input" id="mobile_2_a_1" placeholder="">
        </div>
      </div>
      <span class="">
          </span>
    </div>
    <div class="dropdown-select " id="mobile_3">
      <label for="mobile_3">
            Mobile No. 3
          </label>
      <div class="dropdown-container">
        <div class="dropdown-select__field ">
          <input type="text" class="dropdown-select__input" id="mobile_3_a_1"
              placeholder="">
        </div>
      </div>
      <span class="">
          </span>
    </div>
    <div class="dropdown-select " id="mobile_4">
      <label for="mobile_4">
            Mobile No. 4
          </label>
      <div class="dropdown-container">
        <div class="dropdown-select__field ">
          <input type="text" class="dropdown-select__input" id="mobile_4_a_1"
              placeholder="">
        </div>
      </div>
      <span class="">
          </span>
    </div>
    <div class="dropdown-select " id="mobile_5">
      <label for="mobile_5">
            Mobile No. 5
          </label>
      <div class="dropdown-container">
        <div class="dropdown-select__field ">
          <input type="text" class="dropdown-select__input" id="mobile_5_a_1"
              placeholder="">
        </div>
      </div>
      <span class="">
          </span>
    </div>
    <div class="dropdown-select " id="mobile_6">
      <label for="mobile_6">
            Mobile No. 6
          </label>
      <div class="dropdown-container">
        <div class="dropdown-select__field ">
          <input type="text" class="dropdown-select__input" id="mobile_6_a_1"
              placeholder="">
        </div>
      </div>
      <span class="">
          </span>
    </div>
    <div class="phone" id="mobile_1_add">
      <label for="mobile_1_add">
          </label>
      <div class="phone__container ">
        <input type="text" name="mobile_1_add" id="mobile_1_add_a_1" placeholder=""
            minlength="8" maxlength="8" pattern="^[89]d{7}$" value="">
      </div>
    </div>
    <div class="phone" id="mobile_2_add">
      <label for="mobile_2_add">
          </label>
      <div class="phone__container ">
        <input type="text" name="mobile_2_add" id="mobile_2_add_a_1" placeholder=""
            minlength="8" maxlength="8" pattern="^[89]d{7}$" value="">
      </div>
    </div>
    <div class="phone" id="mobile_3_add">
      <label for="mobile_3_add">
          </label>
      <div class="phone__container ">
        <input type="text" name="mobile_3_add" id="mobile_3_add_a_1" placeholder=""
            minlength="8" maxlength="8" pattern="^[89]d{7}$" value="">
      </div>
    </div>
    <div class="phone" id="mobile_4_add">
      <label for="mobile_4_add">
          </label>
      <div class="phone__container ">
        <input type="text" name="mobile_4_add" id="mobile_4_add_a_1" placeholder=""
            minlength="8" maxlength="8" pattern="^[89]d{7}$" value="">
      </div>
    </div>
    <div class="phone" id="mobile_5_add">
      <label for="mobile_5_add">
          </label>
      <div class="phone__container ">
        <input type="text" name="mobile_5_add" id="mobile_5_add_a_1" placeholder=""
            minlength="8" maxlength="8" pattern="^[89]d{7}$" value="">
      </div>
    </div>
    <div class="phone" id="mobile_6_add">
      <label for="mobile_6_add">
          </label>
      <div class="phone__container ">
        <input type="text" name="mobile_6_add" id="mobile_6_add_a_1" placeholder=""
            minlength="8" maxlength="8" pattern="^[89]d{7}$" value="">
      </div>
    </div>
  </div>
</div>

How do I use a variable as a js object key when searching?

I have a simple program I’m testing where I take a user’s input ID and use that to gather data from a js object; however, I can’t seem to figure out how to use a variable as search for this.

Here’s the current code:
index.html

...
<body>
  <p id="displayTest">...</p>
  <input id="inputTextbox" type="text"></input>
  <input type="submit" onclick="submitID()"></input>
  <script src="script.js"></script>
</body>
...

script.js

var root = {
             data:{
               7292:{
                 ...
               },
               8323:{
                 ...
               }
            }
var display = document.getElementByID("displayTest");
var input = document.getElementByID("inputTextbox");
function submitID() {
  if ((input.value) in (root.data)) {
    display.innerText = root.data/*.the user's input.somedata*/;
    } else {
    display.innerText = "failed :(";
    }
}

It’s important that I am able to get the key in a search like this (root.data.User’s Input.somedata) so that I am able to access data under the input ID; for example, name.

I’ve searched around and still couldn’t find it, so I started trail and error, here’s a few of things I’ve tried:

...
var inputID = input.value;
function submitID() {
  ...
  display.innerText = root.data.inputID.somedata; /*Doesn't return anything, I believe it's looking for a key called 'inputID'*/
  ...
  }
...
...
function submitID() {
  ...
  display.innerText = root.data.${inputID}.somedata; /*Identifier Expected syntax error*/
  ...
  }
...
...
function submitID() {
  ...
  display.innerText = root.data.(input.value).somedata; /*Identifier Expected syntax error*/
  ...
  }
...

I can’t think of any other way to do this, and searching the web has been to no avail, so I turn to you, stackoverflow community, to help me.

Thanks in advance!

TDLR: I am trying to use a variable input as the key for a search in a js object

The code I wrote here is handwritten (not copy/pasted, so it may differ from the actual project), and is written on a device I am unfamiliar with. Assume misspellings & common syntax errors are fixed in project.

Google Gantt Chart in Razor Pages with SQL

Google Gantt charts… you’ve probably heard of them and maybe even used them. They are easy to toss into a Razor Page when you provide static data for the rows. But, I’m wanting to pull the data from a database table on our SQL server and populate the Gantt chart. I have read countless articles about using PHP and Json files, none have worked for me. I can get the data to populate an HTML table, so I know the data is there. I just can’t format it properly to show in the Gantt Chart. I won’t post any of my code because it’s really just a bunch of snippets of the stuff I’ve found online, including using Ajax calls to get data. Can someone please point me to a modern working example (with source code) of a Google Gantt chart pulling data from a database to successfully populate the chart? This can be in either Razor pages or Blazor. Any help would be greatly appreciated.

Also, I know there are a lot of commercial charts I can use for Razor and Blazor, but we can’t spend any money on this project, which is why I’m trying to use the Google Gantt chart.

Thank you for your time!

How to implement a custom Add to Cart button to Shopify’s Dawn Theme?

I have attempted to create a custom Add to Cart button for the Dawn theme (using AJAX functionality) based on the dev notes for Cart API (https://shopify.dev/docs/api/ajax/reference/cart).

I have been able to apply JS that updates the cart drawer, but the item only shows in the cart drawer once the page has been refreshed. After this point I have not been able to find a solution to eliminate the need to refresh the page.

I imagine there are specific cart functions within the Dawn theme that I am missing, however, can someone with experience in applying this type of code to the Dawn theme help guide me through the next steps?

Current liquid file for custom buy button:

<button class="custom-buy-button{% if product.available == false %} unavailable{% endif %}" 
        id="c-buy-button-{{ product.id }}" 
        data-variant-id="{{ product.selected_or_first_available_variant.id }}">
  <p>Add to Cart</p>
  <span class="loading-spinner hidden">⏳</span>
</button>

<style>
  .custom-buy-button {
    width: 100%;
    border: 1px solid #1f1d24;
    background: white;
    color: #1f1d24;
    cursor: pointer;
    position: relative;
  }
  .custom-buy-button.unavailable {
    pointer-events: none;
    border: 1px solid gray;
    color: gray;
  }
  .loading-spinner {
    display: inline-block;
    margin-left: 10px;
  }
  .hidden {
    display: none;
  }
  .error-message {
    color: red;
    font-size: 14px;
    margin-top: 5px;
    display: none;
  }
  .cart-success-message {
    color: green;
    font-size: 14px;
    margin-top: 5px;
    display: none;
  }
</style>

<p class="error-message"></p>
<p class="cart-success-message"></p>

<script>
  document.addEventListener("DOMContentLoaded", function () {
    let buyButton = document.querySelector('.custom-buy-button');
    let loadingSpinner = buyButton.querySelector('.loading-spinner');
    let cartDrawer = document.querySelector('cart-drawer') || document.querySelector('.cart-notification');

    if (!buyButton) {
      console.error("Buy button not found");
      return;
    }

    buyButton.addEventListener("click", function () {
      function updateCartDrawer() {
        fetch(`${window.Shopify.routes.root}?sections=cart-drawer`)
          .then(response => response.json())
          .then(data => {
            console.log("Update Cart Drawer");
          })
          .catch(error => {
            console.error('Error updating cart drawer:', error);
          });
      }
    });
  });
</script>

Safe PowerBI iFrame implementation using a proxy service

My main goal is to display a PowerBI report within an older website built with the .NET Framework 4.0. There are a few considerations:
The PowerBI report needs to be visible and interactive, but we need to hide the original link URL.
The PowerBI report was shared using the public URL method (so anyone with the link can access the project). It was created this way, rather than using the secure sharing method, as it requires a PowerBI account to view it. Therefore, we want to display the information but restrict full access to the report.

It currently works with the public URL, but it presents a serious security issue: the URL is exposed in the HTML src attribute.
The main issue is that the src URL displayed in the web development tools cannot be hidden or cleared, so the next step was to implement a proxy to hide the actual URL.

I created another project to properly test it and decided to create a proxy service that would redirect the original request to my target site to avoid displaying the actual PowerBI URL, which contains sensitive data.

    <!DOCTYPE html>
    <html lang="es">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Amazing Title</title>
        <link rel="stylesheet" href="styles.css">
        <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    </head>
    <body>
        <header style="height: 3em;">
            <h1>PowerBI Report</h1>
        </header>
        <div class="iframe-container" style="width: 100%; height: 52em;">
            <iframe
            id="inlineFrameExample"
            title="Inline Frame Example"
            width="100%"
            height="100%"
            src="">
            </iframe>
        </div>

        <footer>
            <p>&copy; 2023 CLF.</p>
        </footer>

        <script src="script.js"></script>
    </body>
    </html>
    $(document).ready(function () {
      var url = 'https://localhost:7103/api/powerbi/report';
      $('#inlineFrameExample').attr('src', url);
    });

And this is my new Proxy Service:

    [ApiController]
    [Route("api/powerbi")]
    public class ProxyController : ControllerBase
    {
        private readonly IHttpClientFactory _httpClientFactory;

        public ProxyController(IHttpClientFactory httpClientFactory)
        {
            _httpClientFactory = httpClientFactory;
        }

        [HttpGet("report")]
        public async Task<IActionResult> GetPowerBIReport()
        {
            var client = _httpClientFactory.CreateClient();
            var powerBIUrl = "https://app.powerbi.com/view?r=TheUrlThatIWantToHide";
            var response = await client.GetAsync(powerBIUrl);

            if (!response.IsSuccessStatusCode)
            {
                return StatusCode((int)response.StatusCode, "Error");
            }

            var contentType = response.Content.Headers.ContentType?.ToString() ?? "text/html";
            var content = await response.Content.ReadAsStreamAsync();
            return File(content, contentType);
        }
    }

So now I’m facing the following issues:
#First: Millions of failed requests trying to search hash-manifest.js. At first, I thought this is happening because it’s trying to load resources from the website and can’t due to the proxy service implementation.
This makes a lot of sense, although I have my doubts about emulating that behavior, considering the security risk that doing so can present.
image-MultipleErrorLogs

#Second: When I visit the URL https://localhost:7103/api/powerbi/report (the URL of my service that should return the content of the actual PowerBI URL), it starts reloading infinitely, and the URL changes to https://localhost:7103/api/powerbi/report?disablecdnExpiration=1744249594, with the disablednExpiration number constantly changing.
I thought it was an anti-proxy or something similar, but I’m not sure. What I do see is that the service is making so many requests per second that it even crashes on some frames, and the PowerBI page says Too Many Requests.
image-DirectProxyServiceUrl

I’m trying to fix this, but I’m stuck.

Best practices for dependency management in a monorepo using npm workspaces and Nx [closed]

I’m working with a monorepo in JavaScript/TypeScript that uses npm workspaces and Nx. The setup includes:

  • 2 frontend apps

  • 2 backend apps

  • Several shared utility packages

I’m trying to establish a clean and maintainable dependency management strategy, but there seem to be conflicting recommendations:

  • Some suggest putting all dependencies in the root package.json

  • Others recommend including only shared dependencies (used by 2+ apps) at the root

  • Some prefer putting all dev dependencies at the root

  • while others recommend scoping everything (including devDeps) to each app’s package.json

I’d love to hear from folks who’ve tried these different approaches. Specifically:

  • What are the pros and cons you’ve experienced with each strategy?

  • How does the choice affect maintenance, especially as the number of apps grows?

  • Any impact on CI build performance or caching when dependencies are hoisted vs scoped locally?

  • Would it make sense to put tools like react, vite, or even framework-specific dev dependencies at the root?

For context, I’ve currently only placed common tools like typescript, eslint, and the testing suite in the root package.json.

Thanks in advance for your insights!

Uploading a file in Cypress in Salesforce and there is no type = file

I’m trying to attach a .csv file in Salesforce application and there is no type = file. How can we attach file? None of the solutions worked for me. Any help.

Tried:

const filePath = 'shelf_upload_template.csv';
cy.xpath("//span[text()='Upload Files']").eq(1).invoke('show').attachFile(filePath)
cy.wait(5000);
cy.xpath("//span[text()='Add']").click()
AND
cy.xpath("//a[@data-label='Attachments']").click({force:true})

Here is the Console:

<lightning-icon icon-name="utility:upload" class="slds-icon-utility-upload slds-button__icon slds-button__icon--left slds-icon_container forceIcon" data-data-rendering-service-uid="1157" data-aura-rendered-by="5616:0" lwc-4897l11qtae-host="" data-aura-class="forceIcon"><span lwc-4897l11qtae="" style="--sds-c-icon-color-background: var(--slds-c-icon-color-background, transparent)" part="boundary"><lightning-primitive-icon lwc-4897l11qtae="" exportparts="icon" size="xx-small" variant="inverse" lwc-55d10h1nfm1-host=""><svg focusable="false" aria-hidden="true" viewBox="0 0 520 520" part="icon" lwc-55d10h1nfm1="" data-key="upload" class="slds-icon slds-icon_xx-small"><g lwc-55d10h1nfm1=""><path d="M485 310h-30c-8 0-15 8-15 15v100c0 8-7 15-15 15H95c-8 0-15-7-15-15V325c0-7-7-15-15-15H35c-8 0-15 8-15 15v135a40 40 0 0040 40h400a40 40 0 0040-40V325c0-7-7-15-15-15zM270 24c-6-6-15-6-21 0L114 159c-6 6-6 15 0 21l21 21c6 6 15 6 21 0l56-56c6-6 18-2 18 7v212c0 8 6 15 14 15h30c8 0 16-8 16-15V153c0-9 10-13 17-7l56 56c6 6 15 6 21 0l21-21c6-6 6-15 0-21z" lwc-55d10h1nfm1=""></path></g></svg></lightning-primitive-icon></span></lightning-icon>

CORS issue in Unity WebGL app when using mobile network (but works on office Wi-Fi)

I’m building a Unity WebGL app hosted at mydomain.com that dynamically loads images from another domain (api.example.in). These images are used as elements for an AR experience.

Everything works perfectly on office Wi-Fi, but when I access the same app using mobile data, the image loading fails due to CORS (Cross-Origin Resource Sharing) errors in the browser console.

Here’s the relevant part of my JavaScript code in the WebGL index.html:

function createImageTargets(data, folderPath) {
  const files = data.files;
  files.forEach((file) => {
    if (!file.match(/.(jpg|jpeg|png|gif)$/i)) return;

    const imagetarget = document.createElement("imagetarget");
    const id = file.replace(/.[^/.]+$/, "");
    imagetarget.id = id;

    const imageUrl = `${folderPath}/ARBuildsImages/${file}`;
    imagetarget.setAttribute("src", imageUrl);
    imagetarget.setAttribute("crossorigin", "anonymous");

    document.body.appendChild(imagetarget);
  });
}
  1. Accessing the image URLs directly works fine from office Wi-Fi, but from mobile data I get CORS errors in the browser console.
    2.Removing crossorigin=”anonymous” doesn’t help—images still fail to load on mobile network.
    3.The CORS error happens only on image loading, not API calls.
    4.Accesing images directly in browser through link works fine.

What I’m expecting
To solve the cors issue and access to the image

Want to know a better way to do bulk insertion in typeorm without save()

I’m doing a bulk insert using repository.insert(...) in TypeORM, and I’m trying to figure out the best way to associate inserted records with their related metadata for follow-up inserts in other tables.
Here’s the situation:
I’m inserting a list of Product entities where each product has a unique name. I’m also maintaining a Map<string, { product, tags, locations }> where the key is the product’s name, and the value includes all the related data that will be inserted into other tables (like product_tags, product_locations, etc.) that depend on the generated productId.
I use this code:

 const productsToInsert = [...productMap.values()].map(entry => entry.product);
const insertResult = await dataSource.getRepository(Product).insert(productsToInsert);

 insertResult.generatedMaps.forEach((p, idx) => {
   // Trying to get the name to map back to productMap
   const productName = p.name; // not available here
   const id = p.id;
 });

The issue is that generatedMaps only contains columns that were generated (e.g., id), and not columns like name that were explicitly set. So I can’t match back the inserted ID with the original product using name, which was my key.>
Is there a better way to track which inserted ID belongs to which original entity when using .insert()?

  • I want to get the ids after insertion so i can map the ids with name and then insert in other tables too where we need this id for foreign key.

Syncing audio with score scrolling

I’m buiding a webpage that will be used by drummers to practise patterns.

In order to do so, I must have:

  1. An adjustable metronome (functional)
  2. A score including automatic scrolling synced to the metronome created with HTML canvas (not fully functional)

The problem lies in syncing the score scrolling to the metronome. For the moment, I use purely empirical values because when I’ve tried to calculate the distances and used settimeout to scroll the score I couldn’t get a satisfying result.

Please note:

  1. the space between the notes is 30 pixels.
  2. The canvas will probably be 100% page width as in the exemple.
  3. The vertical bar in the middle of the canvas is the reference point. When a note reaches this bar, a sound will be played so that the user can hear what drum element should be played and how (snare max vol or min vol in this example).

The script does the job but is quite approximate. In order for the script to be usable, there should be a very good synchronisation between the metronome and the notes reaching the reference bar.

I’ve tried calculating the distance that should be offset each millisecond but settimeout won’t allow for less than 15 to 17 ms and it varying is not accurate enough.

Below is the full code for the project.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DrumChallenge.com</title>
    <style>
        #div_patterns {
            display: inline-block;
            margin: 20px;
        }
        #div_tempo {
            display: inline-block;
            margin: 20px;
        }
        #drumscore {
            position: relative;
            width: 100%;
            height: 200px;
        }
        #drumscore canvas {
            position: absolute;
            top: 0px;
            left: 0px;
        }
        canvas {
            border: 1px solid black;
            width: 100%;
            height: 200px;
        }
    </style>
</head>
<body>
    <div id="params">
        <div id="div_patterns"></div>
        <div id="div_tempo">
            <span>Tempo :</span>
            <input type="text" id="value_tempo" value="60" />
            
            <span>Delai :</span>
            <span id="value_delai"></span>
            
            <span>Timer :</span>
            <span id="value_timer"></span>
            
        </div>
        <button id="launch" class="launch" type="button">Lancer challenge</button>
    </div>
    <div id="drumscore">
        <canvas id="score"></canvas>
        <canvas id="scoreCanvas"></canvas>
    </div>
    <script src="metronome.js"></script>

    <script>
        
        
        
    document.addEventListener('DOMContentLoaded', () => {
        const audioContext = new (window.AudioContext || window.webkitAudioContext)();
        const metronome = new Metronome(audioContext);
        
        var Current_Audio = false;
        var Challenge_Launched = false;
        var Current_Animation = false;
        var Timer_Challenge = false;
        var Timer_General = false;
        var Tempo = 60;
        var Delai;
        var Distance_Between_Notes = 30;
        var General_Timer = 0;
        var NextNoteTime = 0;
        //
        //
        //
        const LaunchButton = document.getElementById('launch');
        LaunchButton.addEventListener('click', function(){
            if (Challenge_Launched == false){
                CreerChallenge();
                Challenge_Launched = true;
                
                const bpm = parseInt(InputTempo.value, 10);
                metronome.setTempo(bpm);
                metronome.start();
                
                Timer_General = setInterval(SetGeneralTimer, 1000);
                NextNoteTime = performance.now();
                //drawNotes();
                requestAnimationFrame(drawNotes);
            } else {
                Challenge_Launched = false;
                clearTimeout(Timer_Challenge);
                clearInterval(Timer_General);
                metronome.stop();
                //cancelAnimationFrame(Current_Animation);
            }
        });
        //
        //
        //
        function SetTempo(){
            Tempo = InputTempo.value;
            Delai = (60000 / Tempo).toFixed(2);
            document.getElementById('value_tempo').innerHTML = Tempo;
            document.getElementById('value_delai').innerHTML = Delai + 'ms';
            metronome.setTempo(Tempo);
            /*if (Challenge_Launched){
                clearTimeout(Timer_Challenge);
                //Timer_Challenge = setTimeout(drawNotes, Delai);
            }*/
        }
        //
        //
        //
        const InputTempo = document.getElementById('value_tempo');
        InputTempo.addEventListener('blur', function(){
            SetTempo()
        });
        SetTempo()
        //
        //
        //
        const drumscore = document.getElementById('drumscore');
        //
        // Canvas et contexte de la partition
        //
        const score = document.getElementById('score');
        const scorectx = score.getContext('2d');
        scorectx.canvas.width  = drumscore.offsetWidth;
        scorectx.canvas.height = drumscore.offsetHeight;
        //
        // Canvas et contexte des notes
        //
        const canvas = document.getElementById('scoreCanvas');
        const ctx = canvas.getContext('2d');
        ctx.canvas.width  = drumscore.offsetWidth;
        ctx.canvas.height = drumscore.offsetHeight;
        //
        // Lignes de la partition
        //
        const ScoreLines = [60,80,100,120,140];
        //
        //
        //
        const Elements = [
             {Name: 'Snare', Line: ScoreLines[2]}
        ];
        //
        // Patterns
        //
        const Patterns = [
            {
                Name:       'Rll',
                Element:    'Snare',
                Notes:      ['R', 'l', 'l'],
                Checkbox:   false,
                Label:      false,
                Checked:    false,
            },
            {
                Name:       'rrL',
                Element:    'Snare',
                Notes:      ['r', 'r', 'L'],
                Checkbox:   false,
                Label:      false,
                Checked:    false,
            }
        ];
        //
        // Affichage patterns
        //
        const DivPatterns = document.getElementById('div_patterns');
        Patterns.forEach(pattern => {
            pattern.Checkbox = document.createElement('input');
            pattern.Checkbox.type = "checkbox";

            pattern.Label = document.createElement('label')
            pattern.Label.htmlFor = pattern.Name;
            pattern.Label.appendChild(document.createTextNode(pattern.Name));
            
            DivPatterns.appendChild(pattern.Checkbox);
            DivPatterns.appendChild(pattern.Label);
        });
        //
        // Sounds
        //
        const Sounds = [
            {
                Element:    'Snare',
                Type:       'Normal',
                URL:        '',
                Audio:      new Audio('snare_normal.wav')
            },
            {
                Element:    'Snare',
                Type:       'Ghost',
                Audio:      new Audio('snare_ghost.wav')
            }
            
        ];
        //
        // Notes à afficher
        //
        
        const measures = 20;
        const noteWidth = 10;
        const noteHeight = 10;
        const scrollSpeed = 3;
        //
        // Main Droite ou Gauche
        //
        const isAccented = str => str === str.toUpperCase();
        const isRightHand = str => str.toUpperCase() === 'R';
        
        //
        // Créer le challenge
        //
        var notes = [];
        var current_pattern;
        //
        // Dessiner la partition
        //
        function CreerChallenge() {
            notes = [];
            for (var i=0 ; i<measures ; i++){
                current_pattern = Patterns[Math.floor(Math.random() * 2)];
                
                for (var j=0 ; j<current_pattern.Notes.length ; j++){
                    notes.push({
                        x:      canvas.width / 2 + 180 + (notes.length * Distance_Between_Notes) + 1,
                        y:      isRightHand(current_pattern.Notes[j]) ? ScoreLines[2] - 5 : ScoreLines[2] + 5,
                        w:      isAccented(current_pattern.Notes[j]) ? 7 : 4,
                        h:      isAccented(current_pattern.Notes[j]) ? 5 : 3,
                        Audio:  isAccented(current_pattern.Notes[j]) ? new Audio('snare_normal.wav') : new Audio('snare_ghost.wav')
                    })
                }
            }
            console.log(notes);
        }
        //
        // Dessiner la partition
        //
        function drawScore() {
            scorectx.clearRect(0, 0, canvas.width, canvas.height);
            scorectx.strokeStyle = "#A0A0A0";
            
            ScoreLines.forEach(Line => {
                scorectx.beginPath();
                scorectx.moveTo(0, Line);
                scorectx.lineTo(canvas.width, Line);
                scorectx.stroke();
            });
            
            scorectx.beginPath();
            scorectx.moveTo(canvas.width / 2, ScoreLines[0]);
            scorectx.lineTo(canvas.width / 2, ScoreLines[ScoreLines.length-1]);
            scorectx.stroke();
        }
        //
        //
        //
        function nextNote() {
            const secondsPerBeat = 60.0 / Tempo;
            NextNoteTime += 1000 / Distance_Between_Notes;
        }

        //
        // Dessiner et animer les notes
        //
        function drawNotes() {
            
            NextNoteTime = performance.now();
            
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            notes.forEach(note => {
            
                //ctx.fillRect(note.x, note.y, note.w, note.w);
                ctx.beginPath();
                ctx.ellipse(note.x, note.y, note.w, note.h, Math.PI, 0, 2 * Math.PI);
                ctx.fill();
                
                if (note.x > canvas.width / 2 - 5 && note.x <= canvas.width / 2){
                    Current_Audio = note.Audio;
                    note.Audio.play();
                }
                
                //note.x -= scrollSpeed;
                note.x -= Tempo / 15;
            });

            
            //const endTime = performance.now()

            //console.log(`Call to doSomething took ${endTime - startTime} milliseconds ` + endTime)
            //Current_Animation = requestAnimationFrame(drawNotes);
            if (Challenge_Launched){
                //Timer_Challenge = setTimeout(drawNotes, 1);
                Timer_Challenge = setTimeout(() => requestAnimationFrame(drawNotes), 1);
            }
        }
        
        function SetGeneralTimer(){
            const startTime = performance.now()
            General_Timer++;
            document.getElementById('value_timer').innerHTML = General_Timer
            const endTime = performance.now()
            //console.log(`Started ` + startTime + ' | ' + (endTime - startTime) + ' | ' + General_Timer)
        }
        
        
        drawScore();
    });
    </script>
</body>
</html>

And the metronome class:

class Metronome {
    constructor(context) {
        this.context = context;
        this.isPlaying = false;
        this.current16thNote = 0;
        this.tempo = 60;
        this.lookahead = 25.0;
        this.scheduleAheadTime = 0.1;
        this.nextNoteTime = 0.0;
        this.timerID = null;
    }

    nextNote() {
        const secondsPerBeat = 60.0 / this.tempo;
        this.nextNoteTime += 0.25 * secondsPerBeat;
        this.current16thNote++;
        if (this.current16thNote === 16) {
            this.current16thNote = 0;
        }
    }

    scheduleNote(beatNumber, time) {
        const osc = this.context.createOscillator();
        const envelope = this.context.createGain();
        osc.frequency.value = (beatNumber % 4 === 0) ? 1000 : 800;
        envelope.gain.value = 1;
        envelope.gain.exponentialRampToValueAtTime(0.001, time + 0.2);
        osc.connect(envelope);
        envelope.connect(this.context.destination);
        osc.start(time);
        osc.stop(time + 0.2);
    }

    scheduler() {
        console.log(this.nextNoteTime);
        console.log(this.context.currentTime);
        console.log(this.scheduleAheadTime);
        while (this.nextNoteTime < this.context.currentTime + this.scheduleAheadTime) {
            this.scheduleNote(this.current16thNote, this.nextNoteTime);
            this.nextNote();
        }
        this.timerID = setTimeout(this.scheduler.bind(this), this.lookahead);
    }

    start() {
        if (!this.isPlaying) {
            this.isPlaying = true;
            this.current16thNote = 0;
            this.nextNoteTime = this.context.currentTime;
            this.scheduler();
        }
    }

    stop() {
        this.isPlaying = false;
        clearTimeout(this.timerID);
    }

    setTempo(newTempo) {
        this.tempo = newTempo;
    }
}