Zapier Code by Zapier not counting quantities for bundled products in Shopify subscription orders

Problem Summary
I’m using Zapier to extract data from Shopify orders and send it to Google Sheets. Products within subscription bundles are showing quantity 0, when I run tests it counts fine. But, when I get live orders, all the product counts besides the bundle are marked 0.

Setup

  1. **Platform: Shopify + AWTOMIC for bundled subscription meal plans
  2. Site: https://cravemealprepco.com
  3. Google Sheet: Template link of live order, followed by test
  4. Zap Structure:

    a) 1 – Shopify – New Paid Order

    b) 2 – Filter by Zapier → Only continues if line items bundle name includes “Regular”

    c) 3 – Code by Zapier → Process meal data (code below)

    d) 4 – Google Sheets → Create Spreadsheet Row
/*
UPDATED VERSION - Handles both bundle items and individual items
*/

//////////////////// 1. master list - Updated with actual Shopify names ////////////////////
const meals = {
  // Add the actual product names from Shopify
  "Peanut Butter Power Balls": 0,
  "Regular Protein - Meal Plan": 0,
  "Overnight Oats": 0,
  "Protein Donuts ( 2 pack )": 0,
  
  // Keep your existing names in case they're used elsewhere
  "Chocolate Strawberry Protein Pancakes": 0,
  "Crave Breakfast Sandwich (Gluten Free)": 0,
  "BBQ Chicken Mac N' Cheese": 0,
  "French Onion Chicken - (Gluten Free)": 0,
  "Chipotle Chicken (Gluten Free)": 0,
  "Crave Fil A (Gluten Free)": 0,
  "Turkey Bolognese": 0,
  "Fiesta Turkey Bowl (Gluten Free)": 0,
  "Sweet Chili Turkey Bowl (Gluten Free)": 0,
  "Adobo Braised Beef Bowl - (Gluten Free)": 0,
  "Beef Empanada": 0,
  "Sesame Ginger Beef": 0,
  "Big Mac Bowl (Gluten free)": 0,
  "Asian Crunch Salad": 0,
  "Strawberry Walnut Chicken Salad (GF)": 0,
  "Mediterranean Chicken Hummus Wrap": 0,
  "Grilled Chicken Chimichurri Bowl (GF) (DF) (SF)": 0,
  "Grilled Chicken Caesar Wrap": 0,
  "Beef & Broccoli (GF) (DF) (SF)": 0,
  "Steak & Eggs (GF) (DF) (SF)": 0,
  "Pulled Chicken (GF) (DF) (SF)": 0,
  "Turkey Burger (GF) (DF) (SF)": 0,
  "Grilled Chicken (GF) (DF) (SF)": 0,
  "Chili Lime Turkey (GF) (DF) (SF)": 0
};

//////////////////// 2. normalise inputs ////////////////////
const names = Array.isArray(inputData.meal_name)
  ? inputData.meal_name
  : (inputData.meal_name || "").split(/s*,s*/).filter(Boolean);

const qtys = Array.isArray(inputData.meal_qty)
  ? inputData.meal_qty
  : (inputData.meal_qty || "").split(/s*,s*/).filter(Boolean);

//////////////////// 3. Handle bundle data from inputData object ////////////////////
// Check if there are bundle items directly in inputData
const bundleItems = [];
for (const key in inputData) {
  // Look for meal names that end with "- Regular" or "- Double"
  if (key.match(/s*-s*(regular|double)$/i) && typeof inputData[key] === 'string' && !isNaN(inputData[key])) {
    const qty = parseInt(inputData[key], 10);
    if (qty > 0) {
      bundleItems.push({
        name: key,
        qty: qty
      });
    }
  }
}

//////////////////// 4. build the counts ////////////////////
const unknown = [];

// Process regular meal_name/meal_qty arrays
for (let i = 0; i < names.length; i++) {
  let title = (names[i] || "").trim();

  // strip "- Regular" or "- Double" (any case, any spaces)
  title = title.replace(/s*-s*(regular|double)$/i, "").trim();

  const qty = parseInt(qtys[i] || "0", 10);

  if (meals.hasOwnProperty(title)) {
    meals[title] += qty;
  } else {
    unknown.push(title);
  }
}

// Process bundle items found in inputData
for (const item of bundleItems) {
  let title = item.name.trim();
  
  // strip "- Regular" or "- Double" (any case, any spaces)
  title = title.replace(/s*-s*(regular|double)$/i, "").trim();

  if (meals.hasOwnProperty(title)) {
    meals[title] += item.qty;
  } else {
    unknown.push(title);
  }
}

//////////////////// 5. return the row ////////////////////
return {
  name: inputData.billing_name || "",
  phone: inputData.billing_phone || "",
  email: inputData.email || "",
  unknown_titles: unknown,
  ...meals
};

Input Data Structure
The Code by Zapeir step receies:

  1. billing_name: Billing Address Name
  2. billing_phone: Customer Phone
  3. email: Email
  4. meal_qty: Line Items Quantity
  5. meal_name: Line Items Title

Expected Vs Actual Behavior

Expected: When a new order is placed the product quantities should be updated correctly in to the spreadsheet row.

Actual: When a new order is placed the product quantities all show as ‘0’ besides the bundle item. When I run same order as a test it works fine.

Question
How can I make it so that I receive same results (Track the quantity for each meal for new orders and add them to the sheet correctly). I was thinking the issue might be with the JavaScript code.

MutationObserver не работает, когда я нахожусь на другой вкладке Chrome [closed]

Наблюдатель работает, когда я нахожусь на вкладке Chrome, где используется MutationObserver. Я получаю сообщения, когда происходят изменения, но когда я нахожусь на другой вкладке или в другом окне, ничего не происходит, пока я не вернусь на вкладку, где использую MutationObserver.

Например, предположим, что я использую stackoverflow.com на вкладке, где наблюдаю за мутациями. Всё в порядке, я вижу изменения. Затем я оставляю вкладку открытой и переключаюсь на другую вкладку. Мутации перестают работать, как будто DOM не обновляется, пока я не вернусь на предыдущую вкладку.
На сайте идёт обратный отсчёт таймера. Я слежу за этим таймером. Через минуту обновляю страницу. Но когда вкладка неактивна, скрипт перестаёт следить!

вот часть кода:

window.alert = function ( text ) { console.log( 'tried to alert: ' + text ); return true; };
const $span = document.getElementById("countdown");
const options = { subtree: true, childList: true };
const observer = new MutationObserver((mutationsList, observer) => {
for (let mutation of mutationsList){
if (mutation.type === 'childList') {
    if ($span.textContent.split(":")[2]=="00") {
        document.querySelector("#dashboard-sidebar > nav > ul > li.nav-item.active > a").click();
        console.log('%c '+$span.textContent, 'color:red');
    }
}
}
});

if($span)observer.observe($span, options);
console.log(new Date().toLocaleTimeString());

setInterval, который следит за тем же таймером, работает исправно, но через некоторое время засыпает

                setInterval(() => {
                    console.log('%c '+$span.textContent, 'color:red');
                }, 60 * 1000); // 5 minutes (5 * 60 seconds * 1000 ms)

How to interact with a Livewire webpage from an external browser extension?

I’m (very newly) getting into browser extension development so would appreciate any input here on whether or not what I’m trying to do is even possible.

I’m trying to write a browser extension for an external website (not owned by me). The site uses Livewire for a very simple display – think a list of options that can either be selected or unselected. The goal of the extension is to grab a list of the items that are unselected – the respective HTML elements have a particular “badge” (HTML class called “selected” and “unselected”) as part of their child elements when I look at the DOM manually.

I first tried to get the list of selected elements by manually filtering through the DOM with JS but have been running into a lot of issues. When viewing the elements on the page they seem to be divs with wire:id classification, which I can look at child elements of, but sometimes this changes to a wire:snapshot which I can’t get further into.

I’m wondering if there is a good way to do this, tried looking to see if there’s a way to import Livewire library into a JS script and use it to interact with the page, but haven’t been able to find any documentation.

Any advice/suggestions/explanations if I’m misunderstanding how the whole things works would be much appreciated 🙂

Repeat for-loop until condition is false

const [start, setStart] = React.useState(false)
let myArray = [1,2,3,4]

I already have a button that calls setStart to change start between true and false. How to repeatedly iterate through myArray to output its content, such as 1, 2, 3, 4, 1, 2, 3, 4... and stops whenever start is flagged false (upon a button click)? Vice versa when start is true.

For example: (click button, loop starts) 1, 2, 3, 4, 1, 2, 3, (click button, loop terminates)

My attempt at implementing a while-loop:

while(start){

  for(let i of myArray){

    if(!start) break;
    setTimeout(console.log(i), 500)
    
  }
}

however it runs into an infinite loop.

Really appreciate any help!

How to send model and some other parameters from javascript to controller using jquery.ajax?

I have an ASP.NET Core MVC project and I am trying to send a model and some other parameter from JavaScript to a controller action in the manner.

const urlAction = window.location.origin + "/UserAccount/ConfirmContact";
const jqXHR = $.ajax({
    method: "POST",
    url: urlAction,
    data: { model: model, contactType: contactType },
    contentType: "application/json; charset=utf-8"
});
await jqXHR?.then(
    async function(data, textStatus, jqxhr)
    {
        alert(`request to ${urlAction} is successed`);
    },
    function(jqxhr, textStatus, errorThrown)
    {
        alert(`request to ${urlAction} is failed; status: ${textStatus}; error: ${errorThrown}`);
    });

My model:

public enum ContactType : int
{
    EMail = 0,
    PhoneNumber
}
public class AccountViewModel : ModelBase
{
    public string? UserId { get; set; }
    public string? UserName { get; set; }
    public string Password { get; set; }
    public string PasswordConfirmation { get; set; }
    public bool TwoFactorEnabled { get; set; }
    public TwoFactorMethod TwoFactorMethod { get; set; }
    public string? Email { get; set; }
    public string? PhoneNumber { get; set; }
    public ContactConfirmationState EmailConfirmationState { get; set; }
    public ContactConfirmationState PhoneConfirmationState { get; set; }
    public string Name { get; set; }
    public string? EsiaLogin { get; set; }
    public string? EsiaPassword { get; set; }
    public string EsiaPasswordConfirmation { get; set; }
    public AccountViewModel()
    {
    }
}

On the controller side:

[HttpPost]
public async Task<IActionResult> ConfirmContact(AccountViewModel model, ContactType contactType)
{
    if(model != null)
    {
        if(contactType == ContactType.PhoneNumber)
        {
            if(await PhoneConfirmationService.SendVerificationCode(model))
            {
                model.PhoneConfirmationState = ContactConfirmationState.AwaitingConfirmation;
            }
        }
        else
        {
            if(await EmailConfirmationService.SendVerificationCode(HttpContext, model))
            {
                model.EmailConfirmationState = ContactConfirmationState.AwaitingConfirmation;
            }
        }
    }
    return View("Register", model ?? new AccountViewModel());
}

But the action parameters are not initialized from the request, but with a default value.
How to send the data correctly?

How to get an empty NodeList?

Is there a way to get an empty NodeList? We could get away with something like

document.querySelectorAll('#a>#b>#c')

and even put it in a wrapper function that iterates as long as it gets an empty NodeList to make it safe. But I’m wondering if there is an “elegant” way of getting an instanceof NodeList which happens to have length of 0. Fiddled around with new NodeList() but without any success.

How to get groupedColumnDefs in sidebar and flat columnDefs in the grid?

AGgrid cdocumentaion here shows custom colum layout but on a click of a button https://www.ag-grid.com/react-data-grid/tool-panel-columns/#custom-column-layout. How can I get different columLayouts in sideBar and Grid. I have exploring with chatGPT and copilot but no luck. This is the code am trying

function AgIndex() {
  //const [isEditing, setIsEditing] = useState(true);
  const gridRef = useRef(null);
  const columnDefs = [
    { field: "salary", colId: "salary" },
    { field: "bonus", colId: "bonus" },
    { field: "name", colId: "name" },
    { field: "department", colId: "department" },
  ];

  const groupedColumnLayout = [
    {
      headerName: "Salary Info",
      groupId: "salary_info",
      children: [{ colId: "salary" }, { colId: "bonus" }],
    },
    {
      headerName: "Personal Info",
      groupId: "personal_info",
      children: [{ colId: "name" }, { colId: "department" }],
    },
  ];

  const rowData = [
    { salary: 50000, bonus: 5000, name: "Alice", department: "Engineering" },
    { salary: 60000, bonus: 7000, name: "Bob", department: "Marketing" },
    { salary: 55000, bonus: 6000, name: "Charlie", department: "HR" },
  ];
  const apiRef = useRef(null);
  const sideBar = {
    toolPanels: [
      {
        id: "columns",
        labelDefault: "Columns",
        iconKey: "columns",
        toolPanel: "agColumnsToolPanel",
        toolPanelParams: {
          suppressSyncLayoutWithGrid: true,
          columnLayout: groupedColumnLayout,
          suppressRowGroups: true,
          suppressValues: true,
          suppressPivots: true,
          suppressPivotMode: true,
          suppressColumnMove: true,
        },
      },
    ],
    defaultToolPanel: "columns",
  };

  const setCustomGroupLayout = useCallback(() => {
    const columnToolPanel = gridRef.current.api.getToolPanelInstance("columns");
    if (columnToolPanel) {
      columnToolPanel.setColumnLayout(groupedColumnLayout);
    }
  }, [groupedColumnLayout]);

  // useEffect(() => {
  //   setCustomGroupLayout();
  // }, []);

  const onGridReady = useCallback((params) => {
    apiRef.current = params.api;
    params.api.openToolPanel("columns");
    requestAnimationFrame(() => {
      setCustomGroupLayout();
    });

  }, []);


  return (
    <div>
      <div className="ag-theme-alpine" style={{ height: 500, width: "100%" }}>
        <AgGridReact
          ref={gridRef}
          rowData={rowData}
          columnDefs={columnDefs}
          defaultColDef={{ resizable: true, sortable: true, filter: true }}
          sideBar={sideBar}
          onGridReady={onGridReady}
          //onFirstDataRendered={onFirstDataRendered}
        />
      </div>
    </div>
  );
}````

UnknownDependenciesException [Error]: Nest can’t resolve dependencies of the MysqlAgendaRepository (?)

Im getting “[ExceptionHandler] UnknownDependenciesException [Error]: Nest can’t resolve dependencies of the MysqlAgendaRepository (?). Please make sure that the argument “AgendaRepository” at index [0] is available in the AgendaModule context.” everytime i start my application. I use MysqlAgendaRepository as concrete implementation and AgendaRepository as a abstration implementation.

agenda.module.ts:

import { Module } from '@nestjs/common';
import { AgendaService } from './agenda.service';
import { AgendaController } from './agenda.controller';
import { Agenda } from './entities/agenda.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
import { MysqlAgendaRepository } from './repositories/agenda.mysql.repository';

@Module({
  imports:[TypeOrmModule.forFeature([Agenda])],
  providers: [AgendaService, { provide: "AgendaRepository", useClass: MysqlAgendaRepository}
  ],
  controllers: [AgendaController]
})
export class AgendaModule {}

How to Create a Nested Array from 2 Tables in Angular

I have 2 tables that I need to display in the following manner:
Nested Table
I’m populating 2 different arrays

storeTableData: any=[];
employeeTableData: any=[];

The storeTableData has the following fields: StoreID, Name, Address
The employeeTableData has the following fields: StoredID, Name, Role

I’m not sure if I have to turn my data into an array like this first (and if so, how?):

Store[] = [
{
  StoreID: "1",
  Store: "Staples",
  address: "123 Main Street, San Diego CA 12345",
  employees: [
    {
      StoreID: "1",
      Name: "John Doe",
      Role: "Manager"
    },
    {
      StoreID: "1",
      Name: "John Smith",
      Role: "Cashier"
    },
    {
      StoreID: "1",
      Name: "Jane Doe",
      Role: "Shipping"
    },
},
{
  StoreID: "2",
  Store: "Best Buy",
  address: "456 Main Street, San Diego CA 12345",
  employees: [
    {
      StoreID: "2",
      Name: "John Smith",
      Role: "Manager"
    },
    {
      StoreID: "2",
      Name: "Jane Doe",
      Role: "Cashier"
    },
    {
      StoreID: "2",
      Name: "John Doe",
      Role: "Shipping"
    },
},
]

Or is there a way I can just do it via a nested ngif in the html or is there some other way?

Firebase SignInWithRedirect with Apple login fails on a nuxtjs / js app

I believe a Nuxt.js (Vue.js) web app with Firebase is correctly set up, as I’m able to log in with Firebase/Apple using signInWithPopup.

I have properly set my custom domain in the Auth domain settings. The problem only occurs when signing in with redirect.

Here is my implementation:
I click on the “Sign in with Apple” button, which triggers the corresponding function. I am correctly redirected to Apple’s sign-in page. I enter my credentials and am redirected back to the sign-in page. Absolutely nothing happens—no error message, nothing.

The result from getRedirectResult is always null.

I tried moving the getRedirectResult block into onMounted, but it changed nothing.

So, how can I get getRedirectResult to correctly pick up the sign-in process?


<script setup lang="ts">
// Define page meta to use appropriate layout
definePageMeta({
  layout: 'web-no-tab-bar'
});

useSeoMeta({
  title: 'Sign-In'
});

import { httpsCallable } from 'firebase/functions'
import { GoogleAuthProvider, OAuthProvider, signInWithPopup, updateProfile, getAdditionalUserInfo, signInWithRedirect, getRedirectResult, getAuth } from 'firebase/auth'
import { doc, getDoc, Firestore } from 'firebase/firestore'


// Store instances
const authStore = useAuthStore()
const flashStore = useFlashStore()


// Firebase instances
const { $auth, $firestoreDb, $functionsInstance } = useNuxtApp()
const auth = getAuth();

getRedirectResult(auth)
  .then((result) => { //-----------> ALWAYS NULL
   
    // Check if result is not null before processing
    if (result) {
      alert('result')
      const credential = OAuthProvider.credentialFromResult(result);
      if (credential) {
        // You can also get the Apple OAuth Access and ID Tokens.
        const accessToken = credential.accessToken;
        const idToken = credential.idToken;
      }
      // The signed-in user info.
      const user = result.user;
    }
  })
  .catch((error) => {
    // Handle Errors here.
    const errorCode = error.code;
    const errorMessage = error.message;
    // The email of the user's account used.
    const email = error.customData.email;
    // The credential that was used.
    const credential = OAuthProvider.credentialFromError(error);

    // ...
  });





/**
* ================================
*  Apple Sign-In
* ================================
*/
const signInWithApple = async () => {
  try {
    const provider = new OAuthProvider('apple.com')
    // Configure Apple provider with required scopes
    provider.addScope('email')
    provider.addScope('name')

    const result = await signInWithRedirect($auth, provider)

  } catch (err: any) {
    // eslint-disable-next-line no-console
    console.error('Apple sign-in failed:', err)
    flashStore.show(err?.message || 'Apple sign-in failed. Please try again.')
  }
}


/**
* ================================
*  Providers Sign-In
* ================================
*/

// Verify user profile after authentication. Not for email sign-in. For providers.
const checkUserProfileAfterProvidersSignIn = async (userId: string) => {
  try {
    const userDoc = await getDoc(doc($firestoreDb as Firestore, 'users', userId))

    // If missing username or country, treat as new user
    if (!userDoc.exists() || !userDoc.data()?.username || !userDoc.data()?.country) {
      navigateTo('/authentication/user-info-collect')
    } else {
      // Persist user country in localStorage for later use
      if (utils.isLocalStorageAvailable()) {
        const key = `selectedCountry_${userId}`
        window.localStorage.setItem(key, userDoc.data()?.country.toLowerCase())
      }

      // Redirect back to the page the visitor came from (or home)
      const target = utils.isLocalStorageAvailable()
        ? window.localStorage.getItem('previousUrlForAfterSignIn') || '/'
        : '/'
      navigateTo(target)
    }
  } catch (err) {
    // eslint-disable-next-line no-console
    console.error('Error verifying user profile after Google sign-in:', err)
    navigateTo('/')
  }
}


// Store previous URL in localStorage for future redirect handling
onMounted(() => {
  if (utils.isLocalStorageAvailable()) {
    const previousUrl = (window.history.state && window.history.state.back) || document.referrer || '/';
    localStorage.setItem('previousUrlForAfterSignIn', previousUrl);
  }

});
</script>

Create a Regex for a ‘+’ delimited file for JS

I have a file delimited by + signs. I want to convert those to comma delimited so I can create a csv.
At first I was using simply this regex:
/+/ but then I found out that the file contains data like this:
km aaa+aaa so I created created this regex for such cases /(?<![Kk]m[^+]*)+/g. This last one worked pretty well, but it didn’t work in cases like this:

AA +BBB  +km 12,    +ccccccc
AA +BBB  +km 530,   +ccccccc

Since it returns something like this:

"AA ","BBB  ","km 12,    +ccccccc"
"AA ","BBB  ","km 530,    +ccccccc"

Instead of:

"AA ","BBB  ","km 12,    ","ccccccc"
"AA ","BBB  ","km 530,   ","ccccccc"

Any idea what regex pattern could help?

Firefox on iOS won’t open fetched blob files

In a react website, I invoke the following function to open a blob file (PDF or a few image types) which I have fetched from the server after the user clicks a button.

function viewDocument(userId, documentId, accessToken) {
    const url = new URL("https://my-api/my-controller/Document");
    url.searchParams.set("UserId", userId);
    url.searchParams.set("DocumentId", documentId);
    
    fetch(url.toString(), {
        method: 'get',
        mode: 'cors',
        credentials: 'include',
        headers: {
            authorization: "Bearer " + accessToken
        },
        body: null
    })
    .then((response) => {
        if (!response.ok) { throw response }
        response.blob().then((blob) => {
            const objUrl = window.URL.createObjectURL(new Blob([blob], {type: blob.type}));
            window.open(objUrl);
        });
    })
    .catch( error => {
        // handle error
    });
};

My content security policy includes the following:

default-src 'self';
frame-src 'self' blob:;
img-src 'self' blob:;
object-src 'self' blob:;

The .NET Core API code to fetch the document from the server looks like this:

[HttpGet("api/[controller]/Document")]
[Authorize]
public async Task<IActionResult> GetDocument(string userId, long documentId)
{
    var fileExtensions = new Dictionary<string, string>()
    {
        {".gif", "image/gif"},
        {".jpg", "image/jpg"},
        {".jpeg", "image/jpeg"},
        {".pdf", "application/pdf"},
        {".png", "image/png"},
        {".tif", "image/tif"},
        {".tiff", "image/tiff"}
    };
    var filePath = "some-file-path";
    var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    var contentType = fileExtensions[Path.GetExtension(filePath).ToLower()];
    return File(fileStream, contentType);
}

With this code and configuration, my website works as intended on the latest versions of Chrome (Android & iOS), Safari (iOS), Edge (Android & iOS), and Firefox (Android) by opening a new tab with the blob contents. In some instances, I had to give permissions or unblock popups in settings, and that is to be expected.

For some reason, it simply doesn’t open a new tab or show the fetched blob on Firefox (iOS) even though I have unblocked popups. I have connected the phone to a Mac and debugged the page … the blob is fetched without errors but the next command to open the blob URL is just being ignored for some reason. I have tested other websites on that browser and it seems to open new tabs just fine for them. For my website, it simply does nothing when I click the button. This behavior is similar to what I saw in Safari (iOS) before I unblocked popups in Safar settings, but unblocking popups in Firefox (iOS) settings has not helped at all … a new tab is not opened when I click the button. I am hoping someone has seen this behavior and knows what is going on.

Javascript: Pass an existing variable to a redirected url via Form submit(); [duplicate]

I need to swap out old html files to new asp files, these old files are currently live and in use.

Old URL: www.mysite.com/page1.html?variable1

New URL: www.mysite.com/page1.asp?variable1

I have this so far but it does not capture and pass the variable to the new URL. Oddly, it does pass along the “?” somehow:

<form id="myForm" action="page1.asp" method="get"></form>

<script language="javascript">
function myFunction() {
  document.getElementById("myForm").submit();
}
myFunction();
</script> 

How can I capture the variable from the old URL and pass it along to the new URL?

Is this even possible? I can’t find any examples of this process.

Thanks!

onClick and onclick in react.js

I have a button and two functions closeOnClick and openOnClick. The onClick property is set as openOnClick in which after carrying out the desired process the onclick property is set to closeOnClick. After I click the button again both onClick and onclick is taking place. After testing I found out that onclick and onClick are different.

function closeOnClick() { 
  let sidebar: HTMLElement = document.getElementsByClassName("list-items")[0] as HTMLElement;
  let btn: HTMLElement = document.getElementsByClassName("open-sidebar-btn")[0] as HTMLElement;

  sidebar.classList.remove('show');
  btn.onclick = openOnClick;
  console.log('closed menu and changed onclick to close')
}

function openOnClick() { 
  let sidebar: HTMLElement = document.getElementsByClassName("list-items")[0] as HTMLElement;
  let btn: HTMLElement = document.getElementsByClassName("open-sidebar-btn")[0] as HTMLElement;

  sidebar.classList.add('show');
  btn.onclick = closeOnClick;
  console.log('opened menu and changed onclick to close')
}
<button className="open-sidebar-btn" onClick={openOnClick}>
  click me       
</button>

When I click the button again closeOnClick and openOnClick runs.

  • I tried to change onClick but that isn’t a property of the element.
  • I also tried to set an if statement inside openOnClick.
let closed = true;

function closeOnClick() { 
  let sidebar: HTMLElement = document.getElementsByClassName("list-items")[0] as HTMLElement;
  let btn: HTMLElement = document.getElementsByClassName("open-sidebar-btn")[0] as HTMLElement;

  sidebar.classList.remove('show');
  // btn.onclick = openOnClick;
  console.log('closed menu and changed onclick to close')
  closed = true
}

function openOnClick() { 
  let sidebar: HTMLElement = document.getElementsByClassName("list-items")[0] as HTMLElement;
  let btn: HTMLElement = document.getElementsByClassName("open-sidebar-btn")[0] as HTMLElement;
  if (closed) {
    sidebar.classList.add('show');
  btn.onclick = closeOnClick;
  console.log('opened menu and changed onclick to close')
  closed = false;
  }
}

It looks like the onClick will run after onclick so I used useState, considering that the state is changed after a delay and after all process are done if I am correct.

But this is messing up even more.

Stimulus not working and getting MIME type error

I’m learning Ruby on Rails and wanted to start using its features together with Stimulus. Unfortunately, I’ve encountered a barrier that I don’t know the cause of. Specifically, my files are being detected as text/plain, and I’m getting a MIME type error.

I’m using Rails v8.0.2 and Bootstrap in the project. I’ve already tried many things, including reinstalling Stimulus and checking the imports—everything seems to match up and doesn’t appear to have any issues.

If anyone has had this problem themselves and knows how to solve it, I’d really appreciate the help in advance.

application.js

```// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails           

import “@hotwired/turbo-rails”
import “controllers”
import * as bootstrap from “bootstrap”

import “trix”
import “@rails/actiontext”“`

controllers/application.js

import { Application } from "@hotwired/stimulus"
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
export const application = Application.start()

// Configure Stimulus development experience
window.Stimulus  = application
application.debug = false

eagerLoadControllersFrom("controllers", application)

hello_controller.js

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  connect() {
    console.log("TEST")
  }
}

index

<div data-controller="hello">
  Test Stimulus
</div>

application.html.erb

<%# Includes all stylesheet files in app/assets/stylesheets %>
    <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>

console

Uncaught TypeError: Specyfikator „bootstrap” był samym specyfikatorem, ale nie był do niczego przypisany. Względne specyfikatory modułów muszą zaczynać się od „./”, „../” lub „/”. application-b280f13e.js:4:28
GET
http://localhost:3000/assets/controllers/application
NS_ERROR_CORRUPTED_CONTENT

GET
http://localhost:3000/assets/controllers/hello_controller
NS_ERROR_CORRUPTED_CONTENT

GET
http://localhost:3000/assets/controllers/previews_controller
NS_ERROR_CORRUPTED_CONTENT

Wczytanie modułu z adresu „http://localhost:3000/assets/controllers/application” zostało zablokowane z powodu niedozwolonego typu MIME („text/plain”).
new
Wczytanie modułu z adresu „http://localhost:3000/assets/controllers/hello_controller” zostało zablokowane z powodu niedozwolonego typu MIME („text/plain”).
new
Wczytanie modułu z adresu „http://localhost:3000/assets/controllers/previews_controller” zostało zablokowane z powodu niedozwolonego typu MIME („text/plain”).
new
Stimulus
Uncaught ReferenceError: Stimulus is not defined
    <anonymous> debugger eval code:1

server

Started GET "/assets/controllers/application" for at 2025-07-10 13:18:48 +0200
Started GET "/assets/controllers/hello_controller" for  at 2025-07-10 13:18:48 +0200
Started GET "/assets/controllers/previews_controller" for at 2025-07-10 13:18:48 +0200