React.js and Material UI

First column should be checkbox, so i can select rows from the table
Pagination is required and i should be able to change the number of rows per page
Header should be sticky if number of rows is more than 30
By clicking on the header column, i should be able to sort the table
Provide a button “Load All”, on click of load all button, implement virtualisation (docs: https://mui.com/material-ui/react-table/#virtualized-table) and load all the rows

why my code make reference error when i import the three.js , three is not defined while sript tag type= “module” Why?

browser cannot find a function named loadSVG in the current scope when the button’s onclick event is triggered.

Load SVG

import * as THREE from ‘https://unpkg.com/[email protected]/build/three.module.js’;
import { SVGLoader } from ‘https://unpkg.com/[email protected]/examples/jsm/loaders/SVGLoader.js’;

function renderSVGInThreeJS(svgData) {
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const loader = new SVGLoader();
const svg = loader.parse(svgData); // Parse SVG into shapes

const material = new THREE.MeshStandardMaterial({ color: 0x999999 });

svg.paths.forEach(path => {
const shapes = path.toShapes(true);
shapes.forEach(shape => {
const extrudeSettings = { depth: 10, bevelEnabled: false };
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
});
});

camera.position.z = 50;
const animate = function () {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
animate();
}

async function loadSVG(planId) {
const response = await fetch(`/api/floorplan/${planId}/`);
const data = await response.json();
if (data.svg) {
renderSVGInThreeJS(data.svg);
} else {
console.error(“Failed to load SVG”);
}
}

// ✅ Fix: Make loadSVG available globally
window.loadSVG = loadSVG;

expected to convert svg file with 2d svg img to 3d model file ,

tried es6 js components without bundler

globally declared the function name with window.loadSVG = loadSVG

but the errors come that uncaught type error , reference error , loadSVG is not defined

HTML – replace png image to webp [closed]

I have this css file:

.breadcrumbs .page-header {
  padding: 120px 0 60px 0;
  min-height: 20vh;
  background: url(https://picsum.photos/500/200.webp) center bottom;
  background-size: cover;
  border-radius: 0px;
  overflow: hidden;
  position: relative;
}
<div class="breadcrumbs">
  <div class="page-header">
    ...
  </div>
</div>

but I don’t see the image page-header-bg.webp in the page, but with a PNG file is working filw

How to make the distance between the bottom label and the bottom the same as between the other labels

In ChartJs how to make the distance between the bottom label and the bottom the same as between the other labels. The first picture is what I have, the second is how it should be.

First image

Second image

const chartOptions = ref({
  responsive: true,
  maintainAspectRatio: false,
  events: [],
  plugins: {
    legend: { display: false },
    tooltip: { enabled: false },
    datalabels: {
      anchor: 'end',
      align: 'top',
      clip: false,
      color: (ctx) => (ctx.chart.data.labels[ctx.dataIndex] === today ? '#05F' : '#0C0055'),
      font: {
        family: 'Poppins',
        size: 11, // doesn't work
        weight: (ctx) => (ctx.chart.data.labels[ctx.dataIndex] === today ? '500' : 'normal'),
      },
      opacity: (ctx) => (ctx.chart.data.labels[ctx.dataIndex] === today ? 1 : 0.8),
      formatter: (value) => value,
    },
  },
  scales: {
    x: {
      position: 'bottom',
      grid: {
        borderDash: [9, 9],
        drawBorder: true,
      },
      ticks: {
        font: { family: 'Poppins', size: 11, weight: 500, style: 'normal' },
        color: 'rgba(12, 0, 85, 0.5)',
        lineHeight: 1.15,
      },
    },
    y: {
      beginAtZero: false,
      grace: '0%',
      min: newMinValue,
      max: newMaxValue,
      ticks: {
        stepSize: stepSize,
        callback: function (value) {
          return value.toFixed(1) + (isKg.value ? ' kg' : 'lbs')
        },
        font: { family: 'Poppins', size: 11, weight: 500, style: 'normal' },
        color: 'rgba(12, 0, 85, 0.5)',
        lineHeight: 1.15,
      },
      grid: { borderDash: [8, 8], drawBorder: false },
    },
  },
})

Maybe someone else knows how to change the size of the dots, because the labels and the y-axis change at once
Thanks a lot

vue js unable to get values from button if SVG is present

I am using Vue3. I have a button that get the Id of user once click.

The problem is that if I place an svg icon within the button it causes the vue not to pick up the values of the data-id.. I.e

the data-id now returns null.

Below is the Laravel template blade:

@php  $id = '999';   @endphp

 
<button id="profile"
         v-on:click="start" :data-id="{{$id}}"
         class="profile">
    <svg class="svgclass h-12 w-12 text-red-500"   fill="none" viewBox="0 0 24 24"
                                             stroke="currentColor">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
     d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"/>
     </svg>
</button> 


<script>
        Vue.createApp({
            data() {
                return {
                    state: null,
                };
            },
            mounted() {
                console.log(this);
            },
            methods: {
                start(event){
                    //  const { id } = event.target.dataset;
                    var id = event.target.getAttribute('data-id');
                    console.log(id);
                },
            }
        }).mount('#profile');

    </script>

Why should we not use , with + for string concatenation in js?

A container contains 4 buttons with text A, B , C and D.
This works

let container = document.querySelector(".container");
container.addEventListener('click', (evt)=> {
  if (evt.target.tagName == 'BUTTON') {
    console.log("Button" + " " + evt.target.textContent + " got clicked");
  }
})

output : Button A got clicked

This returns undefined

let container = document.querySelector(".container");
container.addEventListener('click', (evt)=> {
  if (evt.target.tagName == 'BUTTON') {
    console.log("Button" + " " + evt.target.textContent, + " got clicked");
  }
})

output: Button A NaN

call Javascript from code behind in asp.net web form

I need help calling this javascript function after gridview2 is updated with information from a sql query. The gridview updates from the database no problem, and I can use a button to manually call the javascript function but I want to automatically call the function when the gridview is updated. The Javascript has to run on the client side, it adds objects to a three.js scene, the data in the gridview only relates to the file pathway, a unique object name and coordinates. I know I can add a event listener to other controls but I have no idea how to use an addeventlistener to a gridview object.

How do I get the client side javascript to run after the gridview is updated from the code behind?

                function yourJavaScriptFunction(rowIndex) {

                    

                    var x = rowIndex;
                    var file_path = getCellValue(x, 7); // Get the value from the second row, third column
                    if (file_path) {
                        console.log("Cell value: " + file_path);
                    } else {
                        console.log("Row or cell not found, get filepath."+ ' '+file_path);
                        alert("Row or cell not found,get filepath.");
                    }

                    var object_name = getCellValue(rowIndex, 1);
                    if (object_name) {
                       // alert(object_name);
                        console.log("Cell value: " + object_name);
                    } else {
                        console.log("Row or cell not found, get object name.");
                        alert("Row or cell not found, get object name.");
                    }

                   // alert('Button in row ' + rowIndex + ' clicked!');
                    //alert(value);
                   
                    module.add_Object(file_path, object_name);

                   //add_Object();
                    // mytest.add_Object();
                    return false; // Prevent postback if needed
                }

          <asp:GridView ID="GridView2" runat="server" Height="100%" Width="100%" HorizontalAlign="Center" AutoGenerateColumns="False" DataSourceID="SqlDataSource3" >

              <Columns>
                  <asp:BoundField DataField="NAME" HeaderText="NAME" SortExpression="NAME" />
                  <asp:BoundField DataField="ID" HeaderText="ID" SortExpression="ID" />
                  <asp:BoundField DataField="SUBTYPE1" HeaderText="SUBTYPE1" SortExpression="SUBTYPE1" />
                  <asp:BoundField DataField="IMAGE_FILE_PATH" HeaderText="IMAGE_FILE_PATH" SortExpression="IMAGE_FILE_PATH" />
                  <asp:BoundField DataField="TYPE" HeaderText="TYPE" SortExpression="TYPE" />
                  <asp:BoundField DataField="COUNT" HeaderText="COUNT" SortExpression="COUNT" />
                  <asp:BoundField DataField="LOCATION" HeaderText="LOCATION" SortExpression="LOCATION" />
                  <asp:BoundField DataField="TRADE_SIZE" HeaderText="TRADE_SIZE" SortExpression="TRADE_SIZE" />
              </Columns>

              <EditRowStyle HorizontalAlign="Left" />

          </asp:GridView>
          </ContentTemplate>
      </asp:UpdatePanel>
  </div>

Is there any way to get the p5.js element to inherit styles without changing the draw function?

I’m working on a p5.js project and trying to apply styling rules to my canvas via CSS, but nothing seems to work,

the only option seems to set all the styling one at a time inside the draw() function, but I have so many draw functions in my app for different visuals I don’t want to spend forever on it, why doesn’t CSS affect a p5.js canvas?

NextJS 15 Build Error occurred prerendering page “/404”

when running NODE_ENV=developement npm run build in nextjs project, there is the following error,

Error: should not be import outside of pages/_document.

Read more: https://nextjs.org/docs/messages/no-document-import-in-page

at y (D:….nextserverchunks7627.js:6:1263)
An error occurred while pre-rendering the page “/404”. Read more: https://nextjs.org/docs/messages/prerender-error
Error: should not be import outside of pages/_document.

Read more: https://nextjs.org/docs/messages/no-document-import-in-page
in Y (D:…node_modulesnextdistcompilednext-serverpages.runtime.prod.js:16:5469)
in y (D:….nextserverchunks7627.js:6:1263)
in react-stack-bottom-frame (D:…node_modulesreact-domcjsreact-dom-server.edge.development.js:8798:18)
in renderWithHooks (D:…node_modulesreact-domcjsreact-dom-server.edge.development.js:4722:19)
in renderElement (D:…node_modulesreact-domcjsreact-dom-server.edge.development.js:5157:23)
in retryNode (D:…node_modulesreact-domcjsreact-dom-server.edge.development.js:5805:22)
in renderNodeDestructive (D:…node_modulesreact-domcjsreact-dom-server.edge.development.js:5631:11)
in renderElement (D:…node_modulesreact-domcjsreact-dom-server.edge.development.js:5143:11)
in retryNode (D:…node_modulesreact-domcjsreact-dom-server.edge.development.js:5805:22)
in renderNodeDestructive (D:…node_modulesreact-domcjsreact-dom-server.edge.development.js:5631:11)
Export encountered an error at /_error: /404, exiting the build process.
⨯ Next.js build worker exited with code: 1 and signal: null

I didn’t create a custom 404 page. Please help.

NextJS 15.1.7, React 19.0.0, tailwindcss 4.0.7, DaisyUI 5.0.0, Prisma 6.4.1

d3.js force simulation: Some edges not rendering on double-click expansion

I’m building a topology visualization using d3.js where nodes are connected by multiple edges. I implemented a feature to toggle an “expanded” state on a group of edges by double-clicking, which applies an offset to the edge paths (using d3.curveCatmullRom with an alpha of 0.3) so that multiple edges between the same node pair become visible as separate curves.

unvisible node pair

However, when I double-click on an edge group (for example, between “50A_1_EMS” and “3000A_1_EMS”), only one edge is visible while the other remains hidden, even though two edges exist.

Here’s what I’ve done so far:

In the double-click event handler, I call d3.select(event.currentTarget).raise() to bring the clicked edge group to the top.
In the tick function, after updating the edge positions, I filter for expanded edge groups and call .raise() on them:

linkGroup
  .selectAll("g.link-group")
  .filter((d) => linkGroupState.get(d.key))
  .raise();

and

function ticked() {
      // 엣지 그룹 업데이트: 선 경로 및 라벨 위치 계산
      linkGroup.selectAll("g.link-group").each(function (d) {
        const source = d.source;
        const target = d.target;
        const dx = target.x - source.x;
        const dy = target.y - source.y;

    // 기본 중간점(노드 중심)
    const centerX = (source.x + target.x) / 2;
    const centerY = (source.y + target.y) / 2;

    // expanded 상태라면 오프셋을 적용하여 곡선의 중간점 계산
    let midX = centerX;
    let midY = centerY;
    const expanded = linkGroupState.get(d.key) || false;
    if (expanded) {
      const offsetFactor = 20;
      const offset = (d.linkIndex - (d.totalLinks - 1) / 2) * offsetFactor;
      const norm = Math.sqrt(dx * dx + dy * dy);
      if (norm !== 0) {
        const px = -dy / norm;
        const py = dx / norm;
        midX += px * offset;
        midY += py * offset;
      }
    }

    const lineGenerator = d3.line().curve(d3.curveCatmullRom.alpha(0.3));
    const coords = [
      [source.x, source.y],
      [midX, midY],
      [target.x, target.y],
    ];
    const pathData = lineGenerator(coords);

    // 여기서 디버깅용으로 pathData 출력
    console.log(
      "DEBUG pathData:",
      d.key,
      "linkIndex:",
      d.linkIndex,
      pathData
    );

    d3.select(this).select("path.link-visible").attr("d", pathData);
    d3.select(this).select("path.link-hit").attr("d", pathData);
    d3.select(this)
      .select("g.link-label-group")
      .attr("transform", `translate(${centerX}, ${centerY})`);
  });

  // 노드 위치 업데이트
  nodeGroup
    .selectAll("g")
    .attr("transform", (d) => `translate(${d.x}, ${d.y})`);

  updateVisibleNodes();

  // 여기서 엣지 라벨 위치 업데이트 추가
  edgeLabelLayer.selectAll("g.link-label-group").each(function (d) {
    const centerX = (d.source.x + d.target.x) / 2;
    const centerY = (d.source.y + d.target.y) / 2;
    // linkGroupState에 저장된 상태(true: expanded, false: collapsed)
    const isExpanded = linkGroupState.get(d.key) || false;
    d3.select(this)
      .attr("transform", `translate(${centerX}, ${centerY})`)
      .style("display", isExpanded ? "none" : "block"); // expanded 상태면 숨김
  });

  // 4) 속도 라벨(각 링크별) 위치 + 표시 여부
  edgeSpeedLayer.selectAll("g.link-speed-label").each(function (d) {
    console.log(
      "Speed label:",
      d,
      "isExpanded:",
      linkGroupState.get(d.key)
    );
    const centerX = (d.source.x + d.target.x) / 2;
    const centerY = (d.source.y + d.target.y) / 2;
    const isExpanded = linkGroupState.get(d.key) || false;

    d3.select(this)
      .attr("transform", `translate(${centerX}, ${centerY})`)
      .style("display", isExpanded ? "block" : "none") // 펼쳐졌으면 보이기
      .select("text.speed-label-text")
      .text(d.eth_speed ? d.eth_speed + "G" : "");
    // eth_speed가 있다면 "1G", "10G" 등 표시
  });
  linkGroup.lower();
  nodeGroup.raise();
  linkGroup
    .selectAll("g.link-group")
    .filter((d) => linkGroupState.get(d.key))
    .raise();
  // 디버깅용: DOM 순서 로그 출력
  const order = linkGroup
    .selectAll("g.link-group")
    .nodes()
    .map((node) => node.__data__.key);
  console.log("현재 link-group 순서:", order);
}

I log the computed SVG path data (pathData), keys, and linkIndex values. The pathData outputs (e.g., M905.596,254.625C…) appear normal and there are no NaN or Infinity values.
Potential causes I suspect are:

The multiple edges between the same node pair are grouped using a key generated by [source, target].sort().join(“|”), so even if they have different linkIndex values, they share the same key. This might be causing the double-click toggle to affect all edges simultaneously.
The simulation’s tick or transition updates might be inadvertently reordering the DOM elements after the double-click event.
I’ve tried adjusting the offset factor and verifying the DOM order, but the issue persists.
Has anyone encountered a similar problem or can suggest a workaround (for instance, including the linkIndex in the key or another approach) to ensure that all edges become visible on double-click expansion?

Thanks in advance!

How to check new line character in javascript

I need want to check the new line in given string

the original value

the image contain original string value in profile text

when i read the profile text value

var index = jQuery.inArray(dyanmic profile id, profilename);

namevalues[index] = $find(id).get_text();

i got like this

“MEWUJUDKAN JAWATAN PEMANDUGRED R10 KONTRAK BAGI TIMBALAN PENGERUSI MAJLIS AGAMA ISLAMMELAKA DAN PERLANTIKAN ENCIK ADIBIN ALI, PEMANDU GRED R10 SECARA KONTRAK MAJLIS AGAMA ISLAMMELAKA.”

the new line remove the space and join the two string.

The image has wrong displays on Iphone

I am creating a website with parallax scroll effects but on Iphone the parallax images do not seems good it seems like they are of bad quality and incorrect cropping I tried to use <figure> but the issue stills the same result, also I tried to overwrite the images on mobiles to more adaptability this also do not works.
Here is my current code:

    document.addEventListener("scroll", function() {
      const sections = document.querySelectorAll('.section-background');
      sections.forEach(section => {
        const img = section.querySelector('.parallax-img');
        const rect = section.getBoundingClientRect();
        const sectionHeight = section.offsetHeight;
        // Calculate progress (0 when the section is fully below, 1 when it's fully above)
        let progress = Math.min(Math.max(-rect.top / sectionHeight, 0), 1);
        // Map progress to translation from 0 to -sectionHeight
        const translateY = -progress * sectionHeight;
        img.style.transform = `translateY(${translateY}px)`;
      });
    });
.ah2 {
background: rgba(128,128,128,.5);
  font-size: 125px;  
      padding-top: 0.25em;
    font-size: 28pt !important;
    width: 60%
}
    .section-background {
      position: relative;
      height: 100vh;
      width: 100%;
      overflow: hidden;
      display: flex;
      justify-content: center;
      align-items: center;
      text-align: center;
    }
    .parallax-img {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 200%; /* twice the section height ensures full coverage */
      object-fit: cover;
      z-index: -1;
      will-change: transform;
      margin: 0;
    }
      figure > img {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%; /* twice the section height ensures full coverage */
      object-fit: cover;
      z-index: -1;
      will-change: transform;
    }
    <div class="section-background">
 <figure class="parallax-img">
   <img src="https://a.travel-assets.com/findyours-php/viewfinder/images/res70/363000/363235-Nice.jpg"></img>
 </figure>
        <div class="content">
            <h2 class="ah2">WHERE EXCEPTIONAL ENTERTAINMENT — MUSIC, THEATRE, THE ARTS AND INSPIRING LECTURES — ENRICHES OUR CULTURE.</h2>
        </div>
    </div>

    <div class="section-background">
 <figure class="parallax-img">
   <img className="parallax-img" src="https://static.vecteezy.com/system/resources/thumbnails/040/890/255/small_2x/ai-generated-empty-wooden-table-on-the-natural-background-for-product-display-free-photo.jpg"></img>
 </figure>
        <div class="content">
            <h2 class="ah2">WHERE SHARING A GOOD LAUGH HELPS BUILD FRIENDSHIPS THAT LAST A LIFETIME.</h2>
        </div>
    </div>

    <div class="section-background">
 <figure class="parallax-img">
   <img src="https://smart-tourism-capital.ec.europa.eu/sites/default/files/styles/embed_large/public/2021-09/Nice2.jpg?itok=3GEJXlT6"></img>
 </figure>
        <div class="content">
            <h2 class="ah2">WHERE MUSIC UNDER THE REDWOODS CREATES A MAGIC SHARED WITH FRIENDS AND FAMILY.</h2>
        </div>
    </div>

Expected result

:Expected result

Current result

:Current result

Firebase realtimedatabase sort to time

I’m writing in the realtime database using the push() function

The push() function orders the input data in chronological order, but it does not, and it orders the data in numerical order

I want the data to be entered in chronological order
The userName is not based on timestamp, but the data is recorded in the order of 0107 after 0104

"0104": {
  "-OKQu5uz3y6aq_exRrwA": {
    "contents": "1",
    "timestamp": "2025-03-03T12:08:34.229Z",
    "userName": "0104"
  }
},
"0107": {
  "-OHJRj4ye8DOxw0UbEo1": {
    "contents": "1",
    "timestamp": "2025-01-23T18:35:48.476Z",
    "userName": "0107"
  }
},
"0107": {
  "-OJRQ1IGgYYgmk-ri7Ye": {
    "contents": "1 ",
    "timestamp": "2025-02-19T04:16:44.235Z",
    "userName": "0107"
  },

Realtime database

<script type="module">  
    import { initializeApp } from "https://www.gstatic.com/firebasejs/11.1.0/firebase-app.js";
    import { getDatabase, ref, set, get, child, push } from "https://www.gstatic.com/firebasejs/11.1.0/firebase-database.js";
    import { getAnalytics } from "https://www.gstatic.com/firebasejs/11.1.0/firebase-analytics.js";
    
    const firebaseConfig = {My Firebase Info};

    // Initialize Firebase
    const app = initializeApp(firebaseConfig);
    const analytics = getAnalytics(app);
    const db = getDatabase(app);

    document.getElementById("submit").addEventListener('click', function (e) {
        e.preventDefault();
        const currentTime = new Date().toISOString(); // Get current time in ISO format

        push(ref(db, 'user/' + document.getElementById("userName").value), {
            userName: document.getElementById("userName").value,
            contents: document.getElementById("contents").value,
            timestamp: currentTime // Add timestamp
        });

        alert("Success");
    });
</script>

How to insert a note node in the DOM at the top of any web page?

I try to add a text box if the REQUEST_URI have an entry note like this:

enter image description here

By example: https://stackoverflow.com/?note=TEST don’t produce any new node from my code.

// ==UserScript==
// @name         Simple note from QUERY_STRING
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Affiche la valeur de 'note' de la query string de l'URL.
// @author       Mévatlavé
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    function addNoteHeader(noteValue) {
        const header = document.createElement('div');
        header.className = 'query-string-notifier';
        header.textContent = noteValue;
        header.style.backgroundColor = 'yellow';
        header.style.color = 'black';
        header.style.fontSize = '24px';
        header.style.fontWeight = 'bold';
        header.style.textAlign = 'center';
        header.style.padding = '10px';
        header.style.position = 'fixed';
        header.style.top = '0';
        header.style.left = '0';
        header.style.right = '0';
        header.style.zIndex = '1000';

        document.body.insertBefore(header, document.body.firstChild);
    }

    const urlParams = new URLSearchParams(window.location.search);
    const noteValue = urlParams.get('note');

    if (noteValue) {
        addNoteHeader(noteValue);
    }
})();

It works on some sites, but not others. What’s the cause?
Is my issue is CSP or DOM loading related?
How to fix or workaround to work on any website?

Submit Button wont work on my submission form

I have a googlesheet to help track my expenses for my Turo Business. I created a form that I upload a receipt and fill in the info related to that receipt.
Everything works great however when I fill out my form and press submit nothing happens. I can see that it creates the Folders needed in my GoogleDrive to store and save the receipts, but it stops there and doesn’t actually save the receipt and it doesn’t take the data I entered on the form and save it in the Submission Data sheet. It just allows me to keep pressing the Submit Button.

Here is a link to a sample sheet: https://docs.google.com/spreadsheets/d/1qR_EmTpHMhS6fycu47WnWO-3tGHkoAphzXbHxvCX96c/edit?usp=drive_link

Code.gs:

        function onOpen() {
      const ui = SpreadsheetApp.getUi();
      ui.createMenu('Turo Tools')
        .addItem('Upload Receipt', 'showDialog')
        .addToUi();
    }
    
    function showDialog() {
      const html = HtmlService.createHtmlOutputFromFile('Dialog')
        .setWidth(1800)
        .setHeight(1600);
      SpreadsheetApp.getUi().showModalDialog(html, 'Upload Receipt');
    }
    
    function getDropdownData() {
      const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Dropdowns');
      const ranges = ['Category', 'Payment Method', 'Tax-Deductible', 'Account'];
      const data = {};
    
      ranges.forEach(range => {
        const headerRow = sheet.getRange("1:1").getValues()[0]; // Get header row
        const columnIndex = headerRow.indexOf(range) + 1; // Find column index for the range name
    
        if (columnIndex > 0) {
          const rangeValues = sheet.getRange(2, columnIndex, sheet.getLastRow() - 1).getValues().flat().filter(cell => cell !== '');
          data[range] = rangeValues;
        }
      });
    
      return data;
    }
    
    // Pull Vehicle, Guest Name, and Trip ID from 'CSV Data' sheet
    function getCSVData() {
      const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('CSV Data');
      const data = sheet.getDataRange().getValues();
    
      const vehicles = [];
      const guestNames = [];
      const tripIds = [];
      const vehicleGuestMap = {}; // Maps vehicles to guest names
      const tripIDGuestMap = {}; // Maps vehicle -> guest -> trip ID
    
      const vehicleColIndex = 4; // Column E (index 4)
      const guestNameColIndex = 2; // Column C (index 2)
      const tripIdColIndex = 1; // Column B (index 1)
    
      for (let i = 2; i < data.length; i++) {
        const vehicle = data[i][vehicleColIndex];
        const guestName = data[i][guestNameColIndex];
        const tripId = String(data[i][tripIdColIndex]); // Ensure trip ID is a string
    
        if (vehicle && !vehicles.includes(vehicle)) {
          vehicles.push(vehicle);
        }
        if (guestName && !guestNames.includes(guestName)) {
          guestNames.push(guestName);
        }
        if (tripId && !tripIds.includes(tripId)) {
          tripIds.push(tripId);
        }
    
        // Map vehicles to guest names
        if (vehicle) {
          if (!vehicleGuestMap[vehicle]) {
            vehicleGuestMap[vehicle] = new Set();
          }
          vehicleGuestMap[vehicle].add(guestName);
        }
    
        // Map vehicles and guest names to trip IDs
        if (vehicle && guestName && tripId) {
          if (!tripIDGuestMap[vehicle]) {
            tripIDGuestMap[vehicle] = {};
          }
          if (!tripIDGuestMap[vehicle][guestName]) {
            tripIDGuestMap[vehicle][guestName] = [];
          }
          tripIDGuestMap[vehicle][guestName].push(tripId);
        }
      }
    
      // Convert Sets to arrays for JSON compatibility
      for (let vehicle in vehicleGuestMap) {
        vehicleGuestMap[vehicle] = Array.from(vehicleGuestMap[vehicle]);
      }
    
      // Ensure the tripIDGuestMap is returned with the correct structure
      Logger.log({ vehicles, guestNames, tripIds, vehicleGuestMap, tripIDGuestMap });
    
      return {
        vehicles,
        guestNames,
        tripIds,
        vehicleGuestMap,
        tripIDGuestMap
      };
    } 
    
        
    function saveReceipt(formData, base64File) {
      try {
        const driveFolderId = '1pN7V57tRqhf1LU_M5rZlB8YJMNGSwAPu'; // Turo folder ID
        const driveFolder = DriveApp.getFolderById(driveFolderId);
    
        const date = new Date(formData.date);
        const year = date.getFullYear();
        const month = Utilities.formatDate(date, Session.getScriptTimeZone(), 'MMMM');
    
        let receiptsFolder = driveFolder.getFoldersByName('Receipts');
        receiptsFolder = receiptsFolder.hasNext() ? receiptsFolder.next() : driveFolder.createFolder('Receipts');
    
        let yearFolder = receiptsFolder.getFoldersByName(year.toString());
        yearFolder = yearFolder.hasNext() ? yearFolder.next() : receiptsFolder.createFolder(year.toString());
    
        let monthFolder = yearFolder.getFoldersByName(month);
        monthFolder = monthFolder.hasNext() ? monthFolder.next() : yearFolder.createFolder(month);
    
        let categoryFolder = monthFolder.getFoldersByName(formData.category);
        categoryFolder = categoryFolder.hasNext() ? categoryFolder.next() : monthFolder.createFolder(formData.category);
    
        // Handle Base64 file properly
        const base64Parts = base64File.split(',');
        if (base64Parts.length < 2) {
          throw new Error("Invalid base64 file format");
        }
        
        const contentType = base64Parts[0].match(/:(.*?);/)[1];
        const decodedFile = Utilities.base64Decode(base64Parts[1]);
        
        const uniqueId = Utilities.getUuid();
        const fileName = `${formData.vendor}_${formData.date}_${uniqueId}`;
        const fileBlob = Utilities.newBlob(decodedFile, contentType, fileName);
        const savedFile = categoryFolder.createFile(fileBlob);
        const fileUrl = savedFile.getUrl();
    
        console.log("File successfully saved:", fileUrl);
    
        // Save submission data
        const submissionSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Submission Data');
        const lastRow = submissionSheet.getLastRow();
        const firstEmptyRow = lastRow + 1; // Ensures data is written in the next available row
    
        submissionSheet.getRange(firstEmptyRow, 1, 1, 19).setValues([[ 
          formData.date,
          formData.category,
          formData.description,
          formData.vendor,
          formData.paymentMethod,
          formData.account,
          formData.amount.subtotal,
          formData.amount.salesTax,
          formData.amount.useTax,
          formData.amount.liquorTax,
          formData.amount.fee,
          formData.amount.tip,
          formData.amount.total,
          formData.taxDeductible,
          formData.vehicle,
          formData.guestName,
          formData.tripId,
          fileUrl,
          formData.notes
        ]]);
    
        console.log("Form data successfully recorded in the spreadsheet.");
    
        return 'File saved and data recorded successfully!';
      } catch (error) {
        console.error("Error in saveReceipt:", error);
        return `Error: ${error.message}`;
      }


Dialog.html
    

<!DOCTYPE html>
    <html>
      <head>
        <title>Upload Receipt</title>
        <style>
          body {
            font-family: Arial, sans-serif;
            color: #333;
            margin: 0;
            padding: 20px;
            background-color: #f9f9f9;
          }
          .container {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            background: #fff;
            border-radius: 10px;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
            overflow: hidden;
          }
          .preview {
            flex: 1;
            padding: 20px;
            background-color: #f4f4f4;
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            align-items: center;
          }
          .preview h3 {
            margin-bottom: 15px;
          }
          .form {
            flex: 2;
            padding: 10px;
            display: grid;
            grid-template-columns: repeat(1, 1fr);
            gap: 10px;
          }
          input, select, textarea {
            width: 150px;
            margin-bottom: 15px;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 5px;
            font-size: 14px;
            font-family: Arial, sans-serif;
            color: #333;
            background-color: #fff;
          }
          button {
            padding: 12px 20px;
            font-size: 16px;
            color: #fff;
            background-color: #4CAF50;
            border: none;
            border-radius: 5px;
            cursor: pointer;
          }
          button:hover {
            background-color: #45a049;
          }
          iframe {
            border: 1px solid #ddd;
            border-radius: 5px;
            width: 400px;
            height: 800px;
          }
          .amount-grid {
            display: grid;
            grid-template-columns: repeat(2, 10fr);
            gap: 10px;
          }
          .trip-grid {
            display: grid;
            grid-template-columns: repeat(3, 10fr);
            gap: 10px;
          }
          .receipt-grid {
            display: grid;
            grid-template-columns: repeat(2, 10fr);
            gap: 10px;
          }
          .notes-grid {
            display: grid;
            grid-template-columns: repeat(1, 10fr);
            gap: 10px;
          }
        </style>
      </head>
      <body>
        <div class="container">
          <div class="preview">
            <h3>Preview</h3>
            <input type="file" id="fileInput" accept=".pdf,.jpg,.jpeg" />
            <img id="filePreview" style="width: 100%; height: 800px; object-fit: contain; border: 1px solid #ddd; border-radius: 5px;" />
          </div>
          <div class="form">
            <div>
              <h4>Trip Details</h4>
              <div class="trip-grid">
                <select id="vehicle" onchange="updateGuestNameDropdown()" required></select>
                <select id="guestName" onchange="updateTripIdDropdown()" required></select>
                <select id="tripId" required></select>
              </div>
            </div>
            <div>
              <h4>Receipt Details</h4>
              <div class="receipt-grid">
                <input type="date" id="date" placeholder="Date" required />
                <select id="category" required></select>
                <input type="text" id="description" placeholder="Description" />
                <input type="text" id="vendor" placeholder="Vendor" required />
                <select id="paymentMethod" required></select>
                <select id="account" required></select>
                <select id="taxDeductible" required></select>
              </div>
            </div>
            <div>
              <h4>Amount</h4>
              <div class="amount-grid">
                <input type="number" id="subtotal" placeholder="Subtotal" oninput="updateTotal()" />
                <input type="number" id="salesTax" placeholder="Sales Tax" oninput="updateTotal()" />
                <input type="number" id="useTax" placeholder="Use Tax" oninput="updateTotal()" />
                <input type="number" id="liquorTax" placeholder="Liquor Tax" oninput="updateTotal()" />
                <input type="number" id="fee" placeholder="Fee" oninput="updateTotal()" />
                <input type="number" id="tip" placeholder="Tip" oninput="updateTotal()" />
              </div>
              <input type="number" id="total" placeholder="Total" readonly />
            </div>
            <div>
              <div class="notes-grid">
                <textarea id="notes" placeholder="Notes"></textarea>
              </div>
            </div>
            <button onclick="submitForm()">Submit</button>
          </div>
        </div>
    
        <script>
          document.addEventListener('DOMContentLoaded', function () {
            google.script.run
              .withSuccessHandler(populateDropdowns)
              .getDropdownData();
    
            const fileInput = document.getElementById('fileInput');
            fileInput.addEventListener('change', function () {
              const file = fileInput.files[0];
              if (file) {
                const reader = new FileReader();
                reader.onload = function (e) {
                  const preview = document.getElementById('filePreview');
                  preview.src = e.target.result;
                  preview.alt = 'File Preview';
                };
                reader.readAsDataURL(file);
              }
            });
          });
    
          function populateDropdowns(data) {
            const dropdownMapping = {
              category: { key: 'Category', placeholder: 'Category' },
              paymentMethod: { key: 'Payment Method', placeholder: 'Payment Method' },
              taxDeductible: { key: 'Tax-Deductible', placeholder: 'Tax Deductible' },
              account: { key: 'Account', placeholder: 'Account' }
            };
    
            Object.entries(dropdownMapping).forEach(([dropdownId, { key, placeholder }]) => {
              const dropdown = document.getElementById(dropdownId);
              if (data[key]) {
                dropdown.innerHTML = ''; // Clear existing options
                const defaultOption = document.createElement('option');
                defaultOption.value = '';
                defaultOption.textContent = placeholder;
                defaultOption.disabled = true;
                defaultOption.selected = true;
                dropdown.appendChild(defaultOption);
    
                // Add actual options
                data[key].forEach(item => {
                  const option = document.createElement('option');
                  option.value = item;
                  option.textContent = item;
                  dropdown.appendChild(option);
                });
              }
            });
    
            // Update the dropdowns for vehicle, guest, and trip ID from CSV data
            updateVehicleDropdown();
          }
    
          function updateVehicleDropdown() {
      const vehicleDropdown = document.getElementById('vehicle');
    
      google.script.run.withSuccessHandler(function(csvData) {
        if (!csvData.vehicles || csvData.vehicles.length === 0) {
          console.log("No vehicles found.");
          return;
        }
        
        vehicleDropdown.innerHTML = '<option value="">Select Vehicle</option>';
        csvData.vehicles.forEach(vehicle => {
          const option = document.createElement('option');
          option.value = vehicle;
          option.textContent = vehicle;
          vehicleDropdown.appendChild(option);
        });
      }).getCSVData();
    }
    
    
        function updateGuestNameDropdown() {
      const vehicle = document.getElementById('vehicle').value;
      const guestNameDropdown = document.getElementById('guestName');
    
      google.script.run.withSuccessHandler(function(csvData) {
        // Check if vehicleGuestMap exists and has data
        if (!csvData.vehicleGuestMap || !csvData.vehicleGuestMap[vehicle]) {
          console.log("No guests found for the selected vehicle.");
          return;
        }
    
        // Get the guests for the selected vehicle from the vehicleGuestMap
        const guestsForVehicle = csvData.vehicleGuestMap[vehicle];
    
        // Clear and repopulate the dropdown
        guestNameDropdown.innerHTML = '<option value="">Select Guest</option>';
        guestsForVehicle.forEach(guest => {
          const option = document.createElement('option');
          option.value = guest;
          option.textContent = guest;
          guestNameDropdown.appendChild(option);
        });
    
        // Log guests for debugging
        console.log("Guests for selected vehicle:", guestsForVehicle);
      }).getCSVData();
    }
    
    
    function updateTripIdDropdown() {
      const vehicle = document.getElementById('vehicle').value;
      const guestName = document.getElementById('guestName').value;
      const tripIdDropdown = document.getElementById('tripId');
    
      google.script.run.withSuccessHandler(function(csvData) {
        if (!csvData.tripIDGuestMap || Object.keys(csvData.tripIDGuestMap).length === 0) {
          console.log("No trip IDs found.");
          return;
        }
    
        const tripsForVehicleGuest = csvData.tripIDGuestMap[vehicle] && csvData.tripIDGuestMap[vehicle][guestName] || [];
    
        tripIdDropdown.innerHTML = '<option value="">Select Trip ID</option>';
        tripsForVehicleGuest.forEach(tripId => {
          const option = document.createElement('option');
          option.value = tripId;
          option.textContent = tripId;
          tripIdDropdown.appendChild(option);
        });
    
        if (tripsForVehicleGuest.length === 0) {
          console.log(`No trip IDs found for vehicle: ${vehicle} and guest: ${guestName}`);
        }
      }).getCSVData();
    }
    
    
    
          function submitForm() {
            const formData = {
              date: document.getElementById('date').value,
              category: document.getElementById('category').value,
              description: document.getElementById('description').value,
              vendor: document.getElementById('vendor').value,
              paymentMethod: document.getElementById('paymentMethod').value,
              account: document.getElementById('account').value,
              taxDeductible: document.getElementById('taxDeductible').value,
              vehicle: document.getElementById('vehicle').value,
              guestName: document.getElementById('guestName').value,
              tripId: document.getElementById('tripId').value,
              amount: {
                subtotal: document.getElementById('subtotal').value,
                salesTax: document.getElementById('salesTax').value,
                useTax: document.getElementById('useTax').value,
                liquorTax: document.getElementById('liquorTax').value,
                fee: document.getElementById('fee').value,
                tip: document.getElementById('tip').value,
                total: document.getElementById('total').value,
              },
              notes: document.getElementById('notes').value,
            };
    
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];
            const reader = new FileReader();
            reader.onload = function (e) {
              const base64File = e.target.result.split(',')[1];
              google.script.run.saveReceipt(formData, base64File);
            };
            reader.readAsDataURL(file);
          }
    
          function updateTotal() {
            const subtotal = parseFloat(document.getElementById('subtotal').value) || 0;
            const salesTax = parseFloat(document.getElementById('salesTax').value) || 0;
            const useTax = parseFloat(document.getElementById('useTax').value) || 0;
            const liquorTax = parseFloat(document.getElementById('liquorTax').value) || 0;
            const fee = parseFloat(document.getElementById('fee').value) || 0;
            const tip = parseFloat(document.getElementById('tip').value) || 0;
    
            const total = subtotal + salesTax + useTax + liquorTax + fee + tip;
            document.getElementById('total').value = total.toFixed(2);
          }
        </script>
      </body>
    </html>