How can I convert existing data in Google Sheets into a Table from Google Apps Script?

New to Google sheets and Javascript after 3 years away from computers.

I have some data:

enter image description here

I want to make it look a little nicer, so I click Format, Convert to Table.

enter image description here

And it spits out a fancy table like this:

enter image description here

Is there a way to replicate this from within the Apps Script? Something like this would be really handy if it worked:

const sheet = SpreadsheetApp.getActiveSheet();
const lastRow = sheet.getLastRow();
const lastColumn = sheet.getLastColumn();
const dataRange = sheet.getRange(1, 1, lastRow, lastColumn);

dataRange.convertToTable();

Is there something like this I can do?

Nativewind classes being removed with Storybook

I am using nativewind for a UI library I am working on. I am using Storybook for React Native web with Vite as the bundler. The problem is my styles are not applying. Not the tailwind variables or the default ones. However, the same configuration works fine with expo even on the web.

Here is my tailwind.config.js file:

/** @type {import('tailwindcss').Config} */
export default {
  darkMode: "class",
  content: [
    "./.storybook/**/*.{js,jsx,ts,tsx}",
    "../registry/**/*.{js,jsx,ts,tsx}",
    "./.stories/**/*.{js,jsx,ts,tsx}",
  ],
  // eslint-disable-next-line
  presets: [require("nativewind/preset")],
  theme: {
    extend: {
      colors: {
        border: "var(--border)",
        input: "var(--input)",
        ring: "var(--ring)",
        background: "var(--background)",
        foreground: "var(--foreground)",
        primary: {
          DEFAULT: "var(--primary)",
          foreground: "var(--primary-foreground)",
        },
        secondary: {
          DEFAULT: "var(--secondary)",
          foreground: "var(--secondary-foreground)",
        },
        destructive: {
          DEFAULT: "var(--destructive)",
          foreground: "var(--destructive-foreground)",
        },
        success: {
          DEFAULT: "var(--success)",
          foreground: "var(--success-foreground)",
        },
        warning: {
          DEFAULT: "var(--warning)",
          foreground: "var(--warning-foreground)",
        },
        muted: {
          DEFAULT: "var(--muted)",
          foreground: "var(--muted-foreground)",
        },
        accent: {
          DEFAULT: "var(--accent)",
          foreground: "var(--accent-foreground)",
        },
        popover: {
          DEFAULT: "var(--popover)",
          foreground: "var(--popover-foreground)",
        },
        card: {
          DEFAULT: "var(--card)",
          foreground: "var(--card-foreground)",
        },
        sidebar: {
          DEFAULT: "var(--sidebar-background)",
          foreground: "var(--sidebar-foreground)",
          primary: "var(--sidebar-primary)",
          "primary-foreground": "var(--sidebar-primary-foreground)",
          accent: "var(--sidebar-accent)",
          "accent-foreground": "var(--sidebar-accent-foreground)",
          border: "var(--sidebar-border)",
          ring: "var(--sidebar-ring)",
        },
      },
      borderRadius: {
        xl: "calc(var(--radius) + 4px)",
        lg: "var(--radius)",
        md: "calc(var(--radius) - 2px)",
        sm: "calc(var(--radius) - 4px)",
        full: "100%",
      },
    },
  },
  plugins: [],
};

My global.css file:

@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --radius: 0.625rem;
    --background: #ffffff;
    --foreground: #252525;
    --card: #ffffff;
    --card-foreground: #252525;
    --popover: #ffffff;
    --popover-foreground: #252525;
    --primary: #343434;
    --primary-foreground: #fbfbfb;
    --secondary: #f7f7f7;
    --secondary-foreground: #343434;
    --success: #22c55e;
    --warning: #eab308;
    --muted: #f7f7f7;
    --muted-foreground: #8e8e8e;
    --accent: #f7f7f7;
    --accent-foreground: #343434;
    --destructive: #ef4444;
    --border: #ebebeb;
    --input: #ebebeb;
    --ring: #b5b5b5;
    --chart-1: #f97316;
    --chart-2: #06b6d4;
    --chart-3: #3b82f6;
    --chart-4: #84cc16;
    --chart-5: #f59e0b;
    --sidebar: #fbfbfb;
    --sidebar-foreground: #252525;
    --sidebar-primary: #343434;
    --sidebar-primary-foreground: #fbfbfb;
    --sidebar-accent: #f7f7f7;
    --sidebar-accent-foreground: #343434;
    --sidebar-border: #ebebeb;
    --sidebar-ring: #b5b5b5;
  }

  .dark:root {
    --background: #252525;
    --foreground: #fbfbfb;
    --card: #343434;
    --card-foreground: #fbfbfb;
    --popover: #444444;
    --popover-foreground: #fbfbfb;
    --primary: #ebebeb;
    --primary-foreground: #343434;
    --secondary: #444444;
    --secondary-foreground: #fbfbfb;
    --muted: #444444;
    --muted-foreground: #b5b5b5;
    --accent: #5f5f5f;
    --accent-foreground: #fbfbfb;
    --destructive: #dc2626;
    --success: #16a34a;
    --warning: #ca8a04;
    --border: rgba(255, 255, 255, 0.1);
    --input: rgba(255, 255, 255, 0.15);
    --ring: #8e8e8e;
    --chart-1: #8b5cf6;
    --chart-2: #10b981;
    --chart-3: #f59e0b;
    --chart-4: #ec4899;
    --chart-5: #dc2626;
    --sidebar: #343434;
    --sidebar-foreground: #fbfbfb;
    --sidebar-primary: #8b5cf6;
    --sidebar-primary-foreground: #fbfbfb;
    --sidebar-accent: #444444;
    --sidebar-accent-foreground: #fbfbfb;
    --sidebar-border: rgba(255, 255, 255, 0.1);
    --sidebar-ring: #707070;
  }
}

Then for storybook I have the following config:


import {join, dirname} from "path";
import type {StorybookConfig} from "@storybook/react-native-web-vite";
import {createRequire} from "module";
import {createBuildStoriesPlugin} from "../plugins/buildStories";

const require = createRequire(import.meta.url);

/**
 * This function is used to resolve the absolute path of a package.
 * It is needed in projects that use Yarn PnP or are set up within a monorepo.
 */
function getAbsolutePath(value) {
  return dirname(require.resolve(join(value, "package.json")));
}

const config: StorybookConfig = {
  stories: [
    "../.stories/**/*.mdx",
    "../.stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
  ],
  addons: ["@storybook/addon-docs"],
  framework: {
    name: getAbsolutePath("@storybook/react-native-web-vite"),
    options: {
      modulesToTranspile: [
        "react-native",
        "react-native-web",
        "solito",
        "moti",
        "react-native-reanimated",
        "react-native-css-interop",
        "nativewind",
        "react-native-gesture-handler",
      ],
      pluginReactOptions: {
        jsxImportSource: "nativewind",
        babel: {
          presets: [
            ["nativewind/babel", {mode: "transformOnly", postcss: true}],
          ],
          plugins: ["react-native-reanimated/plugin"],
        },
      },
    },
  },
  async viteFinal(viteConfig) {
    viteConfig.plugins = viteConfig.plugins || [];
    viteConfig.plugins.push(createBuildStoriesPlugin());

    // Configure CSS processing for Tailwind
    viteConfig.css = {
      ...viteConfig.css,
      postcss: {
        plugins: [
          require("tailwindcss")({
            config: "./tailwind.config.js",
          }),
        ],
      },
    };

    if (!viteConfig.optimizeDeps) {
      viteConfig.optimizeDeps = {};
    }
    if (!viteConfig.optimizeDeps.esbuildOptions) {
      viteConfig.optimizeDeps.esbuildOptions = {};
    }
    if (!viteConfig.optimizeDeps.esbuildOptions.loader) {
      viteConfig.optimizeDeps.esbuildOptions.loader = {};
    }
    viteConfig.optimizeDeps.esbuildOptions.loader[".js"] = "jsx";
    viteConfig.optimizeDeps.esbuildOptions.loader[".mjs"] = "jsx";

    return viteConfig;
  },
};
export default config;

Finally my preview.ts:

import type {Preview} from "@storybook/react-native-web-vite";
import "../output.css";
import React from "react";
import {NavigationContainer} from "@react-navigation/native";


const preview: Preview = {
  parameters: {
    backgrounds: {
      options: {
        light: {
          name: "Light",
          value: "#ffffff",
        },
        dark: {
          name: "Dark",
          value: "#0a0a0a",
        },
      },
    },
    controls: {
      matchers: {
        color: /(background|color)$/i,
        date: /Date$/i,
      },
    },
  },
  initialGlobals: {
    backgrounds: {value: "dark"},
  },
  decorators: [
    (Story) => (
      <>
        <NavigationContainer>
          <Story />
        </NavigationContainer>
      </>
    ),
  ],
};

export default preview;

The issue is not tailwind generating the classes. As my output file does have the correct classes in it my I did a test. Additionally, the stylesheet does load in the head with the correct styles yet they do not apply.

[![Styles load][1]][1]

However, the elements themselves do not seem to have the correct classes. So it seems babel is stripping away my tailwind classes. How do I prevent this?

Invalid left-hand side in assignment of x.style =

Background: I have an ordinary HTML DIV box. which contains some text and a table.
It’s a wide table, too wide for cell phones, so there is an option to hide some less significant columns to reduce the width.

    CSS  dv {margin:0 auto; background-color:#ffffff; max-width:100%; padding:8px 5px 3px 9px; border:1px solid #eb4; border-radius:12px; display:table;}    
    HTML <div id="dv1" class="dv" style="padding:0 2px 4px 0; max-width:850px; overflow:hidden;">

I have Javascript to service the option to alter the div max-width (trying to match the div to the table width):

    function show_all_columns(show) {   //show or hide first 5 years of columns
        let x = Obj.tbl1.rows;
        for (let row = Obj.rowcnt; row >= 0; row--) {   
            let cells = x[row].cells;
            for (let col = 1; col <= 5; col++) {
                cells[col].style.display = show ? "table-cell" : "none";
            }                           //all this works
        }
        Obj.all_cols_are_shown = show;

        x = document.getElementById("dv1");             //more  detail than necessary,
        x.style.max-width = (show)? "850px" : "600px";  // <----<<  but this x is the problem

        x = 0;   //just to stop debugger here so I can read x
    }

But the Chrome debugger reports this error about x: Invalid left-hand side in assignment

In the debugger, if I comment out the troubled line (x.style.max-width which halts everything), then I can drill into the x variable, which shows:

    style: CSSStyleDeclaration 
    0: "padding-top"
    1: "padding-right"
    2: "padding-bottom"
    3: "padding-left"
    4: "max-width"
    5: "overflow-x"
    6: "overflow-y"

My diligence sees nothing wrong with the format of the max-width, but something blows up, and I have no clue. Any help appreciated.

I assume 600 px width is appropriate for cell phones, but am not sure if I just made that up or not?

Using JavaScript for Qualtrics: make options for question 2 dependent on question 1 selection

So I have been assigned with making a survey in Qualtrics.

  • The first question is “Which county do you live in”
  • The second question is “which zip code do you use”

Right now, I am trying to make all of the options in the dropdown menu for question 2 (Q2) dependent on the answer to question 1 (Q1). For example, if someone selects “Smith County” for Q1, I only want zip codes in Smith County to appear as options for Q2.

After making several different Q2s that display based on conditional logic, I have decided that there must be a better way, and I have resorted to looking into Qualtrics’ Java Encoding capabilities.

Would anybody know how to code something like this? I am aware that it would likely require uploading embedded data- that is a non-issue.

Thanks!

How to add FPS-like controls (WASD movement + mouse rotation) to a Plotly 3D scatter plot in HTML?

I need to create a Plotly 3D scatter plot in Python, save it as a self-contained HTML file (e.g., via fig.write_html), and open it in any browser for offline viewing. The plot should have FPS-like controls: WASD keys for camera movement (forward/back/left/right) and mouse for rotation (around the camera).

This would allow “flying” through the data points like in an FPS game, rather than just orbiting a fixed center.

I’m using Python to generate the plot and export to HTML, but I’m open to JavaScript modifications since Plotly.js handles the rendering. Here’s a minimal example of my current setup using Plotly Express:

import plotly.express as px

# Sample data (Iris dataset for 3D scatter)
df = px.data.iris()

# Create 3D scatter plot
fig = px.scatter_3d(df, x='sepal_length', y='sepal_width', z='petal_length',
                    color='species', title='Interactive 3D Scatter Plot')

# Save as interactive HTML
fig.write_html("3d_scatter.html")

This generates an HTML file with basic interactivity, but no FPS controls. I’ve seen hints in Plotly docs about customizing the camera (e.g., via scene.camera.eye, center, and up in the layout), and I know Plotly supports relayout for updates.

However, I’m stuck on how to:

  1. Capture keyboard (WASD) and mouse events in the browser.
  2. Update the camera position/rotation dynamically without breaking the plot.
  3. Handle pointer lock for mouse rotation to mimic FPS mouse-look.
    Is there a way to achieve this by extending the generated HTML with JavaScript (e.g., event listeners and Plotly.react/relayout)? Or do I need to use Plotly.js directly? Any code examples or tutorials would be greatly appreciated—bonus if it works with large datasets without performance issues.

Environment: Python 3.10, Plotly 5.17, testing in Chrome.

How to prevent text highlight while keeping drag functional

I’m following this tutorial:

https://learnvue.co/articles/vue-drag-and-drop

The final result is that I can drag the elements once I’ve highlighted the text that they contain. (e.g. if I highlight the text “Item A”, I can then drag it to the other drop zone)

I want to disable text highlighting and make the text unselectable. Several answers here recommend the CSS tag user-select: none; but this makes it so the user cannot drag the element either.

I’ve created a sandbox showing both the original code (have to highlight the text to drag the element) and the code with user-select: none;. I’m at a loss for what to try next.

Sandbox Link

How do I avoid adding the same product to the cart database?

I am working on my store project using JS. When I click on the add-to-cart button, an array with objects – image, name, price, is added to my storage. But, I can add such arrays, products, infinitely to the cart storage. How can I prevent adding the same product to the cart?

addToCard = (e) => {
    if (e.target.closest(".add-to-cart")) {
      
      let price = e.target.parentElement.parentElement.dataset.price;
      let name = e.target.parentElement.parentElement.parentElement.dataset.name;
      let img = e.target.parentElement.parentElement.parentElement.dataset.img;
      
      const cartItems = { price, name, img };

      apiService.post("/order", cartItems).then(() => {
       this.setState({
          ...this.state,
          orderCart: this.state.orderCart?.concat(cartItems),
        })
      })
      
      useToastNotification({
        message: "Product in the cart!",
        type: TOAST_TYPE.success,
      });
    }
  };

I want to add an item to the cart, only once. My items have different names. How can I check the variable name in the array and not add the product with that name to the storage?

How to run Node.js code in the browser when it doesn’t access files or system APIs?

I have some JavaScript code that runs perfectly in Node.js, for example:

const someModule = require("some-module");

const result = someModule.doSomething();
console.log(result);

This code does not access files, the network, or other Node-specific resources, and the module itself is purely a logic module that works offline. So in theory, it should work in a browser since Node.js and browser JavaScript use the same language.

I am not looking for a Node server solution. I want a solution that works with pure HTML, JS, and CSS, so the page can remain completely static, like a page hosted on GitHub Pages.

The problem is that when I try to run the same logic in the browser (for example, by importing the module via a CDN), I often encounter errors related to Node-specific APIs that don’t exist in browsers.

I am looking for a general approach to make this kind of Node.js code run in a browser.


Example of my current issue:

Importantly, I am not asking for a fix only for BIP39. I am trying to understand the general approach to this kind of issue, so that whenever the same problem happens with other Node.js modules that are purely logic-based and offline, I can apply the same solution.

Node.js code (index.js):

// index.js
// npm install bip39

const bip39 = require("bip39");

const mnemonic = bip39.generateMnemonic();
console.log(mnemonic);

Attempt in the browser (index.html):

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>BIP39 Demo</title>
  <style>
    body {
      font-family: sans-serif;
      text-align: center;
      margin-top: 50px;
    }
    button {
      padding: 10px 20px;
      font-size: 16px;
      cursor: pointer;
    }
    #output {
      margin-top: 20px;
      font-weight: bold;
    }
  </style>
</head>
<body>
  <h1>BIP39 Random Seed Words</h1>
  <button id="generate">Generate Mnemonic</button>
  <div id="output"></div>

  <script type="module">
    import bip39 from "https://cdn.skypack.dev/bip39";

    const button = document.getElementById("generate");
    const output = document.getElementById("output");

    button.addEventListener("click", () => {
      const mnemonic = bip39.generateMnemonic();
      output.textContent = mnemonic;
    });
  </script>
</body>
</html>

NestJS Cron Scheduler Stops Running Unexpectedly

I have a NestJS application running on a Windows Server, where the app is installed as a Windows Service (via nssm).The issue: the 1-minute and 5-minute schedulers stop running unexpectedly.

Inside the app I use @nestjs/schedule with 5 cron jobs:

  • one runs every 1 minute

  • one runs every 5 minutes

  • others run every 10 min, 15 min, 1 hour, etc.

The issue: the 1-minute and 5-minute jobs stop firing after some time, and not restarted, but the rest continue normally. The service itself remains running, and there are no error logs.

Example code for the 5-minute job:

import { Cron } from '@nestjs/schedule';
import { Injectable, Logger } from '@nestjs/common';

@Injectable()
export class TasksService {
  private readonly logger = new Logger(TasksService.name);

  @Cron('*/5 * * * *')
  async scheduledHandleCronOld() {
    this.logger.log('Old File Processing Job >> Starting scheduled handleCronOld...');
    try {
      if (this.config.get('SFTP_FILE_FORMAT') === 'OLD') {
        await this.handleCronOld(null);
      } else {
        this.logger.log('Old File Processing Job >> Ended due to config is NEW File Format...');
      }
    } catch (error) {
      this.logger.error(`Old File Processing Job >> Scheduler error: ${error.message}`);
    }
  }
}

How to not trigger Firefox’s strict Content Security Policy by my extension

I am currently contributing on an extension, and the main problem is that on Firefox, the CSP is being triggered, which causes quite a few issues. Mainly it seems to block the injection of dynamic CSS, which I thought I removed, although it still gets triggered. Just for reference, the website I am targeting is modrinth. This is the repo if you want a full look at the code (it’s probably really bad code) blueRinth, however, the main problem I suspect is in my injected.js, where the code is:

document.onreadystatechange = function () {
  const projectRegex =
    /^https?://modrinth.com/(?:project|mod|resourcepack|shader|datapack|modpack)/([^/]+)/;
  const projectIdTest = projectRegex.exec(window.location.href);
  const projectId =
    projectIdTest != null && projectIdTest.length > 1 ? projectIdTest[1] : null;
  if (projectId !== null) {
    runBanner(projectId);
    return;
  }
  pageSupportsBanners = false;
};

useNuxtApp()._middleware.global.push(function handleRoute(to, from) {
  if (to.name.startsWith("type-id")) {
    const { id } = to.params;
    console.log("entering project", id);
    runBanner(id);
  } else if (from.name.startsWith("type-id")) {
    const { id } = from.params;
    //console.log("leaving project", id);
    const containers = document.getElementsByClassName("banner-container");
    for (let container of containers) {
      document.body.removeChild(container);
    }
    pageSupportsBanners = false;
  }
});

let pageSupportsBanners = false;
const fallbackimage =
  "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAnSURBVHhe7cEBDQAAAMKg909tDjcgAAAAAAAAAAAAAAAAAAAA4FwNQEAAAQtzTh0AAAAASUVORK5CYII=";
let banner = fallbackimage;

async function runBanner(id) {
  console.log("runBanner called with id:", id);
  pageSupportsBanners = true;
  const apibase = "https://api.modrinth.com/v3/project/";

  try {
    const response = await fetch(apibase + id);
    if (!response.ok) {
      console.error("blueRinth: Response failed during banner loading.");
      return;
    }
    const data = await response.json();
    console.log("Project API data:", data);
    console.log("gallery:", data.gallery);
    data.gallery.forEach((entry) => {
      if (entry.featured === true) {
        banner = entry.url;
        applyBanners();
      }
    });
  } catch {
    console.error(
      "blueRinth: Something failed during banner loading. Please contact WorldWidePixel."
    );
  }
}

function applyBanners() {
  console.log("applyBanners called, banner:", banner);
  if (pageSupportsBanners) {
    const bannerContainer = document.createElement("div");
    const bannerStyles = document.createElement("style");
    bannerStyles.innerHTML = `
  .banner-image {
    background-image:
    linear-gradient(0deg, var(--color-bg) 0%, rgba(0, 0, 0, 0) 200%),
    url(${banner}) !important;
  }
  `;
    document.head.appendChild(bannerStyles);
    bannerContainer.classList.add("banner-container");
    const bannerImage = document.createElement("div");
    bannerImage.classList.add("banner-image");
    bannerContainer.appendChild(bannerImage);
    const bannerBg = document.createElement("div");
    bannerBg.classList.add("banner-bg");
    document.body.appendChild(bannerBg);
    document.body.appendChild(bannerContainer);
    bannerContainer.style.opacity = "100%";
  }
}

Here is the text of the CSP errors i face, I have little idea what they really mean, hence why I’m here:

Content-Security-Policy: (Report-Only policy) The page’s settings would block an inline script (script-src-elem) from being executed because it violates the following directive: “script-src ‘self’”. Consider using a hash (‘sha256-JBmyBqGA3TPyBg9fUy2esK5fafHmvdYH2GunnhLcvUw=’) or a nonce.

Content-Security-Policy: The page’s settings blocked an inline style (style-src-elem) from being applied because it violates the following directive: “style-src https://m.stripe.network”. Consider using a hash (‘sha256-dd4J3UnQShsOmqcYi4vN5BT3mGZB/0fOwBA72rsguKc=’, requires ‘unsafe-hashes’ for style attributes) or a nonce.

I have tried moving all my css to separate files and declaring it in my manifest.json, then in my content.js, switching themes baed on those files, however this does not work. The problem may also arise in my content.js, where the main theme switching is handled.

How can I implement syntax highlighting across the entire project files in VS Code?

In my case, while developing a Blazor web app, I found that some files have syntax highlighting and others do not. It primarily consists of HTML, C#, and JavaScript, which can occasionally be combined into a single file.

Here’s the screenshot of it:

VS Code files comparison

It appears off, especially if the JavaScript code expands in size.


And here’s the VS Code relevant information (VS Code > Help > About):

Vs Code About

Also, note that I am on Windows. Yet, I have no idea why it occurred.

Why am I getting “This attempt to set a cookie via a Set-Cookie header was blocked due to user preference” in my Next.js + Express app?

I’m building a blogging app with Next.js (frontend) and Express.js (backend).
The backend is deployed on Render, and the frontend runs locally on http://localhost:3000.

I’m trying to set cookies from my Express backend using Set-Cookie, but in the browser console I always get this error:

This attempt to set a cookie via a Set-Cookie header was blocked due to user preference

Here’s what I already tried:

  • Added CORS config on backend with credentials: true

  • On frontend, I’m using fetch with credentials: “include”

  • Tried setting cookie options like:

res.cookie("token", token, {
  httpOnly: true,
  secure: true,
  sameSite: "none",
});
  • Verified that my backend is actually sending the Set-Cookie header in the response

Checked on Chrome/Edge — still blocked

My backend response headers look correct, but the cookie is never stored.

From what I understand, this error can be related to:

  • Cross-site cookies being blocked by default

  • Third-party cookies disabled in the browser

  • SameSite/Secure misconfiguration when frontend and backend are on different domains

What I need help with:

Is this purely a browser setting issue (e.g., Chrome blocking cross-site cookies), or something I should fix in my backend config?

If it’s backend related, what is the correct way to set cookies so they are not blocked?

Do I need to change cookie options (SameSite, Secure, domain) when frontend = localhost and backend = Render domain?

How do I make sure this will also work in production (with custom domain on both frontend and backend)?

Excel Office.js displayDialogAsync fails with “Internal Error” on Desktop version 2507

I am developing an Excel add-in that needs to authenticate the user using OAuth. To do this I am calling Office.context.ui.displayDialogAsync to open the authorization page in a dialog.

The code works fine when I run the add-in in Excel Online and also previous versions on or before 2506, but when I test the exact same add-in in Excel Desktop (latest build 2507) it fails immediately with an internal error.

Here is the code I am using to open the dialog:

function openAuthDialog(authUrl) {
    app.log("auth url : " + authUrl);
    var codeChallenge = sessionStorage.getItem("code_challenge");

    Office.context.ui.displayDialogAsync(
        authUrl + "?scope=openid&client_id=" + clientId +
        "&response_type=code&redirect_uri=" + panelURL +
        "&code_challenge=" + codeChallenge +
        "&code_challenge_method=S256",
        { height: app.optimizeHeight(), width: app.optimizeWidth() },
        function (asyncResult) {
            if (asyncResult.status === Office.AsyncResultStatus.Failed) {
                console.error("Error from dialog => " + asyncResult.error.code + ": " + asyncResult.error.message);
                window.location.assign(app.host_url + "excel/AppCommon/connect.html");
            } else {
                dialog = asyncResult.value;
                dialog.addEventHandler(Office.EventType.DialogMessageReceived, processMessage);
                dialog.addEventHandler(Office.EventType.DialogEventReceived, dialogCallback);
            }
        }
    );
}

When I run this on Excel Desktop (version 2507), I get this error:

OSF.DDA.AsyncResulterror: OSF.DDA.Errorcode: -2147024809
message: “An internal error has occurred.”
status: “failed”

How to display WooCommerce orders in a custom frontend dashboard with filters and pagination? [closed]

I’m trying to build a custom frontend dashboard (for shop managers) where they can view and filter WooCommerce orders.
Here’s what I need:

  • Display orders in a table (order number, date, status, total, customer, actions).

  • Add filters by date range and order status.

  • Add pagination (so not all orders load at once).

  • Make sure the query is optimized and doesn’t slow down the site when there are thousands of orders.

I’m currently using WC_Order_Query with paginate => true, which works, but I’m not sure if this is the best way to handle performance and scalability.

My questions are:

  • What is the most efficient way to query and display WooCommerce orders in the frontend with filters and pagination?

  • Should I use WC_Order_Query, WP_Query, or maybe a custom SQL query for better performance?

  • Any best practices to avoid slowing down the dashboard when there are many orders?

<?php
defined('ABSPATH') || exit;

if (! is_user_logged_in() || ! current_user_can('manage_woocommerce')) {
    echo '<p>Accesso negato.</p>';
    return;
}

$start_date   = isset($_GET['start_date'])   ? esc_attr($_GET['start_date'])   : '';
$end_date     = isset($_GET['end_date'])     ? esc_attr($_GET['end_date'])     : '';
$status       = isset($_GET['order_status']) ? sanitize_text_field($_GET['order_status']) : '';

$per_page     = 10;
$current_page = max(1, get_query_var('paged', isset($_GET['paged']) ? intval($_GET['paged']) : 1));

$query_args = [
    'paginate'     => true,
    'limit'        => $per_page,
    'paged'        => $current_page,
    'orderby'      => 'date',
    'order'        => 'DESC',
    'status'       => array_keys(wc_get_order_statuses()),
];

// Date filter
if ($start_date && ! $end_date) {
    $query_args['date_created'] = '>=' . $start_date;
} elseif (! $start_date && $end_date) {
    $query_args['date_created'] = '<=' . $end_date;
} elseif ($start_date && $end_date) {
    $query_args['date_created'] = $start_date . '...' . $end_date;
}

// Status filter
if (! empty($status)) {
    $query_args['status'] = $status;
}

// Run query
$orders_data = (new WC_Order_Query($query_args))->get_orders();
$orders      = $orders_data->orders;
$max_pages   = $orders_data->max_num_pages;
$has_orders  = ! empty($orders);
?>

<!-- FILTER FORM -->
<form action="#" method="get" class="woocommerce-order-filter-form form-data-filter">
    <input type="hidden" name="tab" value="orders" />
    
    From
    <input class="date-input" type="date" name="start_date" value="<?php echo esc_attr($start_date); ?>">
    To
    <input class="date-input" type="date" name="end_date"   value="<?php echo esc_attr($end_date); ?>">

    <select name="order_status" class="status-select">
        <option value=""><?php esc_html_e('All statuses', 'woocommerce'); ?></option>
        <?php foreach (wc_get_order_statuses() as $slug => $label): ?>
            <?php $pure_slug = str_replace('wc-', '', $slug); ?>
            <option value="<?php echo esc_attr($pure_slug); ?>" <?php selected($status, $pure_slug); ?>>
                <?php echo esc_html($label); ?>
            </option>
        <?php endforeach; ?>
    </select>

    <input class="date-submit" type="submit" value="<?php esc_attr_e('Filter', 'woocommerce'); ?>">
    <?php if ($start_date || $end_date || $status): ?>
        <a class="button reset" href="?tab=orders"><?php esc_html_e('Reset', 'woocommerce'); ?></a>
    <?php endif; ?>
</form>

<!-- ORDERS TABLE -->
<?php if ($has_orders): ?>
    <table class="gestore-tabella-ordini cpa-dasboard-table">
        <thead>
            <tr>
                <th>Order</th>
                <th>Date</th>
                <th>Status</th>
                <th>Total</th>
                <th>Customer</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody>
            <?php foreach ($orders as $order): ?>
                <?php
                if (! is_a($order, 'WC_Order')) {
                    continue;
                }

                $status_code  = $order->get_status();
                $status_map   = [
                    'pending'        => ['label' => 'Pending',      'icon' => 'fa-clock'],
                    'processing'     => ['label' => 'Processing',   'icon' => 'fa-gear'],
                    'on-hold'        => ['label' => 'On hold',      'icon' => 'fa-spinner'],
                    'completed'      => ['label' => 'Completed',    'icon' => 'fa-circle-check'],
                    'cancelled'      => ['label' => 'Cancelled',    'icon' => 'fa-circle-xmark'],
                    'refunded'       => ['label' => 'Refunded',     'icon' => 'fa-arrow-rotate-left'],
                    'failed'         => ['label' => 'Failed',       'icon' => 'fa-triangle-exclamation'],
                    'checkout-draft' => ['label' => 'Draft',        'icon' => 'fa-spinner'],
                ];
                $status_data = $status_map[ $status_code ] ?? [
                    'label' => ucfirst($status_code),
                    'icon'  => 'fa-question-circle',
                ];
                ?>
                <tr class="woocommerce-orders-table__row status-<?php echo esc_attr($status_code); ?>">
                    <th scope="row">
                        <span class="order-number">#<?php echo esc_html($order->get_order_number()); ?></span>
                    </th>
                    <td>
                        <time datetime="<?php echo esc_attr($order->get_date_created()->date('c')); ?>">
                            <?php echo esc_html(wc_format_datetime($order->get_date_created())); ?>
                        </time>
                    </td>
                    <td>
                        <span class="status-text status-<?php echo esc_attr($status_code); ?>">
                            <i class="fa-solid <?php echo esc_attr($status_data['icon']); ?>"></i>
                            <?php echo esc_html($status_data['label']); ?>
                        </span>
                    </td>
                    <td>
                        <?php echo $order->get_formatted_order_total(); ?>
                    </td>
                    <td>
                        <?php echo esc_html($order->get_billing_first_name() . ' ' . $order->get_billing_last_name()); ?>
                    </td>
                    <td>
                        <a href="/dashboard/?dettaglio_ordine=<?php echo esc_attr($order->get_id()); ?>" class="cpa-btn-secondary">
                            <i class="fa-solid fa-magnifying-glass"></i>
                        </a>
                    </td>
                </tr>
            <?php endforeach; ?>
        </tbody>
    </table>

    <!-- PAGINATION -->
    <?php if ($max_pages > 1): ?>
        <div class="woocommerce-pagination woocommerce-pagination--numeric woocommerce-Pagination">
            <?php
            $base_url = remove_query_arg('paged');
            ?>
            <?php if ($current_page > 1): ?>
                <a class="woocommerce-button woocommerce-button--previous button"
                   href="<?php echo esc_url(add_query_arg('paged', $current_page - 1, $base_url)); ?>">
                    &larr; Previous
                </a>
            <?php endif; ?>

            <?php for ($i = 1; $i <= $max_pages; $i++): ?>
                <?php if ($i === $current_page): ?>
                    <span class="page-number current"><?php echo $i; ?></span>
                <?php else: ?>
                    <a class="page-number" href="<?php echo esc_url(add_query_arg('paged', $i, $base_url)); ?>">
                        <?php echo $i; ?>
                    </a>
                <?php endif; ?>
            <?php endfor; ?>

            <?php if ($current_page < $max_pages): ?>
                <a class="woocommerce-button woocommerce-button--next button"
                   href="<?php echo esc_url(add_query_arg('paged', $current_page + 1, $base_url)); ?>">
                    Next &rarr;
                </a>
            <?php endif; ?>
        </div>
    <?php endif; ?>

<?php else: ?>
    <?php wc_print_notice('No orders found.', 'notice'); ?>
<?php endif; ?>