Why are my stripe webhook session details saying they are undefined?

I am creating a stripe checkout session in my development mode, that is successfully sending information to my webhook, and when I print out my session details, it successfully prints out all the information from the session. The weird part is though, that when I try to access an individual piece of information so I can save it in my backend with prisma, it says the information is undefined when i console.log it out. It’s so weird I can see the whole thing, but not the individual information. What am I doing wrong and how can I access the individual lines in my Next.js program?

Here is my webhook code

import Stripe from "stripe";
import { stripe } from "@/lib/stripe";
import { headers } from "next/headers";
import { NextResponse } from "next/server";
import prisma from "@/lib/db/prisma";
import { OrderItem } from "@prisma/client";
import { createCart, getCart } from "@/lib/db/cart";
import { revalidatePath } from "next/cache";

export async function POST(req: Request) {
  const cart = (await getCart()) ?? (await createCart());
  const body = await req.text();
  const signature = headers().get("Stripe-Signature") as string;

  let event: Stripe.Event;

  try {
    event = stripe.webhooks.constructEvent(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!,
    );
  } catch (error) {
    return new NextResponse("Invalid signature", { status: 400 });
  }

  const session = event.data.object as Stripe.Checkout.Session;

  if (event.type === "charge.succeeded") {
    console.log("*********************************************************");
    console.log(session.shipping_details?.address?.state);
    console.log("*********************************************************");
    const paymentIntent = event.data.object;

    const userId = paymentIntent.metadata.userId;
    const {
      name,
      address,
      aptNumber,
      city,
      state,
      zipCode,
      country,
      paymentMethod,
      totalInCents,
      taxInCents,
      cartItems,
    } = paymentIntent.metadata;

    try {
      await prisma.$transaction(async (prisma) => {
        // Create the order
        const order = await prisma.order.create({
          data: {
            userId,
            name,
            address,
            aptNumber,
            city,
            state,
            zipCode,
            country,
            paymentMethod,
            totalInCents: parseInt(totalInCents),
            taxInCents: parseInt(taxInCents),
          },
        });

        // Create order items
        const orderItems = JSON.parse(cartItems).map((item: OrderItem) => ({
          productId: item.productId,
          productName: item.productName,
          price: item.price,
          quantity: item.quantity,
          orderId: order.id,
        }));

        await prisma.orderItem.createMany({
          data: orderItems,
        });

        // Empty the user's cart
        await prisma.cartItem.deleteMany({
          where: {
            cart: {
              userId: userId,
            },
          },
        });

        await prisma.cart.update({
          where: { id: cart.id },
          data: {},
        });
        revalidatePath("/", "layout");
      });
    } catch (error) {
      console.error("Error handling checkout session:", error);
    }
  }

  return new NextResponse("ok", { status: 200 });
}

and here is what is printed out when I console.log the whole session.

{
  id: *the id is here*,
  object: 'charge',
  amount: 1499,
  amount_captured: 1499,
  amount_refunded: 0,
  application: null,
  application_fee: null,
  application_fee_amount: null,
  balance_transaction: null,
  billing_details: {
    address: {
      city: 'Austin',
      country: 'US',
      line1: '3186 Brentwood Drive',
      line2: null,
      postal_code: '78746',
      state: 'TX'
    },
    email: '[email protected]',
    name: 'John Doe',
    phone: null
  },
  calculated_statement_descriptor: 'JOHN DOE',
  captured: true,
  created: 1722718453,
  currency: 'usd',
  customer: null,
  description: null,
  destination: null,
  dispute: null,
  disputed: false,
  failure_balance_transaction: null,
  failure_code: null,
  failure_message: null,
  fraud_details: {},
  invoice: null,
  livemode: false,
  metadata: {},
  on_behalf_of: null,
  order: null,
  outcome: {
    network_status: 'approved_by_network',
    reason: null,
    risk_level: 'normal',
    risk_score: 35,
    seller_message: 'Payment complete.',
    type: 'authorized'
  },
  paid: true,
  payment_intent: 'pi_3PjoxtRu5CVeMm6U0PoyXjnC',
  payment_method: 'pm_1PjoxsRu5CVeMm6UvlsVXLbb',
  payment_method_details: {
    card: {
      amount_authorized: 1499,
      authorization_code: null,
      brand: 'visa',
      checks: [Object],
      country: 'US',
      exp_month: 11,
      exp_year: 2042,
      extended_authorization: [Object],
      fingerprint: '08eKoYQiOE8pTlLD',
      funding: 'credit',
      incremental_authorization: [Object],
      installments: null,
      last4: '4242',
      mandate: null,
      multicapture: [Object],
      network: 'visa',
      network_token: [Object],
      overcapture: [Object],
      three_d_secure: null,
      wallet: null
    },
    type: 'card'
  },
  radar_options: {},
  receipt_email: null,
  receipt_number: null,
  receipt_url: 'https://pay.stripe.com/receipts/payment/CAcaFwoVYWNjdF8xUGZDT1pSdTVDVmVNbTZVKPaxurUGMgYiOLShtJk6LBZH4IQkJ4DUUBK9TzBeAOdnf8adJI3SNgJhlihMuQHs9e8IDacRGL5vePD12-',
  refunded: false,
  review: null,
  shipping: {
    address: {
      city: 'AUSTIN',
      country: 'US',
      line1: '3186 Brentwood Drive',
      line2: null,
      postal_code: '78746',
      state: 'TX'
    },
    carrier: null,
    name: 'John Doe',
    phone: null,
    tracking_number: null
  },
  source: null,
  source_transfer: null,
  statement_descriptor: null,
  statement_descriptor_suffix: null,
  status: 'succeeded',
  transfer_data: null,
  transfer_group: null
}

and when I check an individual item such as

console.log(session.shipping_details?.address?.state);

it just prints out undefined, which is weird.

ALSO, you may have noticed, and I find it really weird too, but when I am trying to access the information in session, and try to do a session.”autocomplete” to get the available keys to choose from, it does not give me just basic shipping. only shipping_details, which is really weird as well. I can seem to access top level keys, such as session.created, but deeper keys value pairs don’t seem to work. I have never dealt with this before so I am very confused.

I have console.logged about every possible way I can think about. I have looked through the documentation, but it mostly just shows me how the session object is structured, but nothing that might make me understand why it is not working.

Headers Error while making discord authorization page (javascript)

Im making a website with dashboard for a discord bot. Only when im making the authorization page with the help of JWT to make a token it keeps giving me a headers error (see below). Ill put my code for that specific item under here. Can someone please help me out because I can’t seem to find any source of why said error pops up.


Error:

node:_http_outgoing:703
    throw new ERR_HTTP_HEADERS_SENT('set');
          ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (node:_http_outgoing:703:11)
    at ServerResponse.header (C:xampphtdocsdiscordapiapinode_modulesexpresslibresponse.js:795:10)
    at ServerResponse.send (C:xampphtdocsdiscordapiapinode_modulesexpresslibresponse.js:175:12)
    at ServerResponse.json (C:xampphtdocsdiscordapiapinode_modulesexpresslibresponse.js:279:15)
    at C:xampphtdocsdiscordapiapisrcroutesauthindex.js:109:32
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 'ERR_HTTP_HEADERS_SENT'
}

Node.js v21.6.0
Failed running '.'

Code:

const express = require('express');
const User = require('../../models/User');
const jwt = require('jsonwebtoken');

const router = express.Router();

// GET: designy.xyz/auth/signin
// GET: designy.xyz/auth/callback

const DASHBOARD_URL = 'http://localhost:5173';

router.get('/signin', (req, res) => {
    res.redirect(
        `https://discord.com/oauth2/authorize?client_id=${process.env.DISCORD_CLIENT_ID}&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3001%2Fauth%2Fcallback&scope=guilds+identify`
    );
});

router.get('/callback', async (req, res) => {
    const DISCORD_ENDPOINT = 'https://discord.com/api/v10';
    const CLIENT_ID = process.env.DISCORD_CLIENT_ID;
    const CLIENT_SECRET = process.env.DISCORD_CLIENT_SECRET;
    const REDIRECT_URI = process.env.DISCORD_REDIRECT_URI;
    const DASHBOARD_URL = process.env.DASHBOARD_URL;

    const { code } = req.query;

    if (!code) {
        return res.status(400).json({
            error: 'A "code" query parameter must be present in the URL.',
        });
    }

    try {
        const oauthRes = await fetch(`${DISCORD_ENDPOINT}/oauth2/token`, {
            method: 'POST',
            body: new URLSearchParams({
                client_id: CLIENT_ID,
                client_secret: CLIENT_SECRET,
                grant_type: 'authorization_code',
                redirect_uri: REDIRECT_URI,
                code,
            }).toString(),
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
            },
        });

        if (!oauthRes.ok) {
            return res.status(500).json({
                error: 'Failed to obtain OAuth token.',
            });
        }

        const oauthResJson = await oauthRes.json();

        const userRes = await fetch(`${DISCORD_ENDPOINT}/users/@me`, {
            method: 'GET',
            headers: {
                Authorization: `Bearer ${oauthResJson.access_token}`,
            },
        });

        if (!userRes.ok) {
            return res.status(500).json({
                error: 'Failed to fetch user data.',
            });
        }

        const userResJson = await userRes.json();

        let user = await User.findOne({ id: userResJson.id });

        if (!user) {
            user = new User({
                id: userResJson.id,
                username: userResJson.username,
                avatarHash: userResJson.avatar,
                accessToken: oauthResJson.access_token,
                refreshToken: oauthResJson.refresh_token,
            });
        } else {
            user.username = userResJson.username;
            user.avatarHash = userResJson.avatar;
            user.accessToken = oauthResJson.access_token;
            user.refreshToken = oauthResJson.refresh_token;
        }

        await user.save();

        const token = jwt.sign(
            {
                id: userResJson.id,
                username: userResJson.username,
                avatarHash: userResJson.avatar,
            },
            process.env.JWT_SECRET,
            { expiresIn: '7d' }
        );

        res.cookie('access_token', token, {
            httpOnly: true,
            secure: process.env.NODE_ENV === 'production',
            maxAge: 6.048e8,
        });

        return res.redirect(DASHBOARD_URL);
    } catch (error) {
        console.error(error);
        return res.status(500).json({
            error: 'Internal Server Error.',
        });
    }
});

router.get('/signout', (req, res) => {
    res.clearCookie('token').sendStatus(200);
});

module.exports = router;

Help would be much appreciated!

Ive tried to remove the .send .json. status to see if that would fix the issue.

Ive also tried to remove the res.redirect + token to see if that would’ve helped

Both of these things did not work, also tried some other things but i can’t remember them anymore.

Multiple re-renders issue when updating loading state on parent component in React

I have a parent component which has a isLoading useState and read params from the URL and pass them to child component:

export function MainUserDetails() {
  const [isLoading, setIsLoading] = React.useState<boolean>(false);
  const { t } = useTranslation();

  const params = useParams();
  const userId = params.userId as string;

  const handleLoadingStateChanged = React.useCallback((loading: boolean) => {
    setIsLoading(loading);
  }, []);

  const MemoizedUserDetailsForm = React.useMemo(
    () => <UserDetailsForm userId={userId} onLoadingStateChanged={handleLoadingStateChanged} />,
    [userId, handleLoadingStateChanged]
  );

  return (
    <Stack spacing={4}>
      <Stack direction={{ xs: 'column', sm: 'row' }} spacing={3} sx={{ alignItems: 'flex-start' }}>
        <Box sx={{ flex: '1 1 auto' }}>
          <Typography variant="h4">User Details</Typography>
        </Box>
        <Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>{isLoading && <CircularProgress />}</Box>
      </Stack>
      {isLoading ? <Skeleton variant="rounded" height={500} /> : <>{MemoizedUserDetailsForm}</>}
    </Stack>
  );
}

In my child component inside useEffect I am fetching the component’s data and the useEffect has a dependencies of the userId (which is being send from the parent component):

  React.useEffect(() => {

    const fetchBusinesses = async () => {
      try {
        const businesses = await getBusinesses(businessFilters);
        if (businesses.status === 'success') {
          setBusinesses(businesses.data);
        }
      } catch (error) {
        logger.error(`[UserDetailsForm/#fetchBusinesses]: Failed to fetch businesses:`, error);
      }
    }

    const fetchData = async () => {
      if (userId) {
        onLoadingStateChanged?.(true);
        await fetchBusinesses();
        onLoadingStateChanged?.(false);
      }
    };

    fetchData();

  }, [userId]);

My problem is that in this way, one of the components (I guess it’s the parent one) is re-renders endlessly, when commenting out the onLoadingStateChanged from the fetchData function, it downloads the data once and everything is working fine.

I already changed the onLoadingStateChanged to use memoize function and also now the whole UserDetailsForm is memoize, but still the problem persists.

How can I overcome this problem?

UseState React hook: updating state is not consistent

I’m trying to build a shopping cart. Just about an hour ago I created another post with a similar problem I was running into, and that problem was solved and I was able to implement that properly.

In this shopping cart, I will display all the items in the cart on one side of the page and display the order details/pricing on the other side. Here is a visual of what I have:

Image of shopping cart, currently.

As seen in the picture, with each item, I should be able to update the quantity (using the dropdown) and remove the item if necessary (the X sign on each item). My last question was regarding an issue where when I updated the dropdown (quantity) on an item, the price value of that specific item would not update. It seems that I was not properly updating the state, and I have since gotten that issue fixed.

The issue I’m running into now is that when I update the quantity on one of the items, the order summary should also update. I am using the same item (cart) to calculate values in both.

It is important to note that I only run into this issue when I try to update the quantity of an item. When I remove an item, the order summary changes appropriately. It only doesn’t work when I try to change the quantity.

Here is the code for the component that includes the list and the order summer (titled OuterCart):

import React, { useState } from 'react';
import CartItem from './CartItem';
import CartOrder from './CartOrder';

const OuterCart = () => {
  const items = [
    {
      name: 'Boardwalk view',
      quantity: 3,
      finish: 'Non-matted',
      size: '4x8',
      price: 19.99,
      id: 0,
    },
    {
      name: '2 Boardwalk view',
      quantity: 1,
      finish: 'Matted',
      size: '3x6',
      price: 20.99,
      id: 1,
    },
    ... // there are more items
  ]
  
  const [cart, setCart] = useState(items);

  function removeItem(id) { // function run when I click the X
    const newCart = cart.filter((item) => item.id !== id)
    setCart(newCart);
  }

  function changeQuantity(event, id) { // function run when I update the dropdown on an item
    let newCart = [...cart];
    for (let i=0; i<cart.length; i++) {
      if (cart[i].id === id) {
        newCart[i] = {
          ...newCart[i],
          quantity: parseInt(event.target.value),
        }
      }
    }
    setCart(newCart);
  }

  return (
    <div className='px-3 text-gray-800'>
      <h1 className='my-10 text-2xl font-bold'>Shopping Cart</h1>
      <div className='grid grid-cols-10'>
        <div className='col-span-5 flex flex-col mb-5 border-b text-gray-800'>
          {cart.length === 0 ? <p>You have no items in your cart. <a href="/shop" className='text-indigo-600'>Go shopping</a> to add some!</p> : ""}
          {cart.map((value, index, array) => {
            return(<CartItem value={value} removeItem={removeItem} changeQuantity={changeQuantity} />)
          })} // this works just fine for each item, no matter the action
        </div>
        <div className='col-span-1'></div>
        <CartOrder cart={cart} /> // this is the order summary, only updates appropriately when I remove an item, not when I change the quantity
      </div>
    </div>
  );
};

export default OuterCart;

If necessary, here’s the code for the Order Summary component:

import { React } from 'react';

const CartOrder = ({cart}) => {
  const subtotal = cart.reduce((sum, item) => sum + item.price, 0);

  const taxEstimate = parseFloat((subtotal * .06).toFixed(2));
  const taxEstimateString = parseFloat(subtotal * .06).toFixed(2);

  const orderTotalString = parseFloat(subtotal + taxEstimate).toFixed(2);
  const orderTotal = parseFloat((subtotal + taxEstimate).toFixed(2));

  return (
    <div className='col-span-4 text-gray-800'>
      <div className='rounded-xl bg-gray-100 px-7 py-10'>
        <h1 className='text-lg font-medium mb-4'>Order summary</h1>

        <div className='flex justify-between border-b py-4'>
          <p className='text-gray-500 text-sm'>Subtotal</p>
          <p className='font-medium text-sm'>${subtotal}</p>
        </div>
        <div className='flex justify-between border-b py-4'>
          <p className='text-gray-500 text-sm'>Tax estimate</p>
          <p className='font-medium text-sm'>${taxEstimateString}</p>
        </div>
        <div className='flex justify-between py-4'>
          <p className='font-semibold'>Order total</p>
          <p className='font-semibold'>${orderTotalString}</p>
        </div>
        
        <a href='/' className='mt-5 inline-block w-full bg-indigo-600 hover:bg-indigo-700 transition text-gray-50 text-center px-3 py-3 font-medium tracking-wide rounded-lg'>Checkout</a>
      </div>
    </div>
  );
};

export default CartOrder;

I hate to ask two related questions in such quick succession, but I’ve been stuck on this problem for a good hour now. Any help is appreciated.

Interpolation of vertex attributes during rasterization

RENDER

There is a set of vertices:

        new Vector(-0.5,-0.5,-0.5),
        new Vector(-0.5, 0.5,-0.5),
        new Vector( 0.5, 0.5,-0.5),
        new Vector( 0.5,-0.5,-0.5),
        new Vector(-0.5,-0.5, 0.5),
        new Vector(-0.5, 0.5, 0.5),
        new Vector( 0.5, 0.5, 0.5),
        new Vector( 0.5,-0.5, 0.5)

and a set of triangles:

        new Triangle(0,2,1,[0,0,255,255],[255,255,255,255],[0,255,255,255]),
        new Triangle(0,3,2,[255,0,255,255],[0,0,255,255],[255,255,255,255]),

        new Triangle(6,5,1,[0,255,255,255],[255,255,0,255],[0,255,0,255]),
        new Triangle(1,2,6,[255,255,255,255],[255,255,0,255],[0,255,255,255]),

        new Triangle(2,6,3,[255,0,255,255],[255,255,255,255],[255,255,0,255]),
        new Triangle(6,3,7,[255,0,255,255],[255,0,0,255],[255,255,0,255]),

        new Triangle(5,1,0,[0,0,255,255],[0,255,255,255],[0,255,0,255]),
        new Triangle(5,0,4,[0,0,255,255],[0,0,0,255],[0,255,0,255]),

        new Triangle(0,7,4,[0,0,255,255],[255,0,0,255],[0,0,0,255]),
        new Triangle(0,3,7,[255,0,255,255],[0,0,255,255],[255,0,0,255]),

        new Triangle(6,5,7,[255,0,0,255],[255,255,0,255],[0,255,0,255]),
        new Triangle(7,5,4,[255,0,0,255],[0,0,0,255],[0,255,0,255])

After the projection transformation, to interpolate the attributes (in this case, the colors in the vertices), I need to sort the value at each vertex of the polygon in ascending order.

class Triangle {
constructor(vA, vB, vC, cA = [255,0,0,255], cB = cA, cC = cA) {
    this.vA = vA;
    this.vB = vB;
    this.vC = vC;
    this.cA = cA;
    this.cB = cB;
    this.cC = cC;
}

static normal(vA, vB, vC) {
    var ab = Vector.subtract(vB, vA);
    var ac = Vector.subtract(vC, vA);
    return Vector.cross(ab, ac);
}

sort(projected) {
    let vA = this.vA;
    let vB = this.vB;
    let vC = this.vC;
    let cA = this.cA;
    let cB = this.cB;
    let cC = this.cC;

    if (projected[vB].y < projected[vA].y) {
        [vA, vB] = [vB, vA];
        [cA, cB] = [cB, cA];
    }

    if (projected[vC].y < projected[vA].y) {
        [vA, vC] = [vC, vA];
        [cA, cC] = [cC, cA];
    }

    if (projected[vC].y < projected[vB].y) {
        [vB, vC] = [vC, vB];
        [cB, cC] = [cC, cB];
    }
    return new Triangle(vA,vB,vC,cA,cB,cC)
}
}

Why are the vertices getting out of place? I tried to sort differently, but when animating the camera movement, this artifact still occurs. If someone has encountered it, can you tell me what could be the problem?

Error: Element type is invalid. Received a promise that resolves to: [object Module]. Lazy element type must resolve to a class or function

Hi I’m getting this Error: Error: Element type is invalid. Received a promise that resolves to: [object Module]. Lazy element type must resolve to a class or function.

//firebase.js
import { initializeApp } from "firebase/app";
import {getFirestore} from 'firebase/firestore';

const firebaseConfig = {
  apiKey: "", //removed that for the post
  authDomain: "pantry-tracker-b6215.firebaseapp.com",
  projectId: "pantry-tracker-b6215",
  storageBucket: "pantry-tracker-b6215.appspot.com",
  messagingSenderId: "878062175026",
  appId: "1:878062175026:web:56ef5381a42ea41e75cd2b",
  measurementId: "G-DLLNYX2ZV4"
};
const app = initializeApp(firebaseConfig);
const firestore = getFirestore(app);

export {firestore};
//page.js
'use client'
import { firestore } from "@/firebase";
import { useState, useEffect } from "react";
import { getDocs, deleteDoc, doc, getDoc, collection, setDoc, query } from "firebase/firestore";
import { Box, Container, Modal, Typography, Stack, TextField, Button, TableContainer, Paper, Table, TableHead, TableRow, TableCell, TableBody, IconButton } from "@mui/material";
import AddIcon from '@mui/icons-material/Add';
import RemoveIcon from '@mui/icons-material/Remove';
import EditIcon from '@mui/icons-material/Edit';
import { useTheme } from "@mui/material/styles";

export default function Home() {
  const [inventory, setInventory] = useState([])
  const [open, setOpen] = useState(false)
  const [editOpen, setEditOpen] = useState(false)
  const [itemName, setItemName] = useState('')
  const [currentItem, setCurrentItem] = useState(null)
  const [itemQuantity, setItemQuantity] = useState('')
  const [searchQuery, setSearchQuery] = useState('')
  const theme = useTheme();

  const tableCellStyle = {
    header: {
      fontSize: '1.1rem', 
      fontWeight: 'bold',
      color: '#FFFFFF', 
      backgroundColor: theme.palette.secondary.main
    },
    body: {fontSize: '1rem'}
  }

  useEffect(()=> {
    const fetchInventory = async () => {
      const snapshot = await getDocs(collection(firestore, 'pantry'))
      const inventoryList = snapshot.docs.map(doc => ({
        name: doc.id,
        ...doc.data()
      }))
      setInventory(inventoryList)
    }
    fetchInventory()
  }, [])

  const handleSearchChange = (event) => {
    setSearchQuery(event.target.value)
  }

  const filteredInventory = inventory.filter(item =>
    item.name.toLowerCase().includes(searchQuery.toLowerCase())
  )

  const updateInventory = async () => {
    const snapshot = query(collection(firestore, 'pantry'))
    const docs = await getDocs(snapshot)
    const inventoryList = []
    docs.forEach((doc)=>{
      inventoryList.push({
        name: doc.id,
        ...doc.data()
      })
    })
    setInventory(inventoryList)
  }

  const removeItem = async (itemName) => {
    const docRef = doc(collection(firestore, 'pantry'), itemName)
    const docSnap = await getDoc(docRef)
    if (docSnap.exists()) {
      const {quantity} = docSnap.data()
      if (quantity === 1) {
        await deleteDoc(docRef)
      }
      else {
        await setDoc(docRef, {quantity: quantity-1})
      }
    }
    await updateInventory()
  }

  const updateItem = async (currentItemName, newItemName, newQuantity) => {
    const currentDocRef = doc(collection(firestore, 'pantry'), currentItemName.trim().toLowerCase())
    const currentDocSnap = await getDoc(currentDocRef)
    if (currentDocSnap.exists()) {
      if (currentItemName.trim().toLowerCase() != newItemName.trim().toLowerCase()) {
        const newDocRef = doc(collection(firestore, 'pantry'), newItemName.trim().toLowerCase());
        await setDoc(newDocRef, {quantity: parseInt(newQuantity)})
        await deleteDoc(currentDocRef)
      } else {
        await setDoc(currentDocRef, {quantity: parseInt(newQuantity)})
      }
    } else {
      console.warn(`Item "${currentItemName}" not found.`)
    }
    await updateInventory()
  }

  const addNewItem = async (itemName, itemQuantity) => {
    const cleanedItemName = itemName.trim().toLowerCase()
    const docRef = doc(collection(firestore, 'pantry'), cleanedItemName)
    const docSnap = await getDoc(docRef)
    const quantity = parseInt(itemQuantity)

    if (isNaN(quantity) || quantity <= 0) {
      console.warn("Invalid quantity.");
      return;
    }

    if (docSnap.exists()) {
      const {quantity: existingQuantity} = docSnap.data()
      await setDoc(docRef, {quantity: existingQuantity + quantity})
    } else {
      await setDoc(docRef, {quantity})
    }
    await updateInventory()
  }

  const addItem = async (itemName) => {
    const docRef = doc(collection(firestore, 'pantry'), itemName.trim().toLowerCase())
    const docSnap = await getDoc(docRef)
    if (docSnap.exists()) {
      const {quantity} = docSnap.data()
      await setDoc(docRef, {quantity: quantity + 1})
    } else {
      await setDoc(docRef, {quantity: 1})
    }
    await updateInventory()
  }

  useEffect(()=>{
    updateInventory()
  }, [])

  const handleOpen = () => setOpen(true)
  const handleClose = () => setOpen(false)

  const handleEditOpen = (item) => {
    setCurrentItem(item)
    setItemName(item.name)
    setItemQuantity(item.quantity)
    setEditOpen(true)
  }
  const handleEditClose = () => setEditOpen(false)

  return (
    <Container>
      <Box sx={{ textAlign: 'center', m: 2, mt:4}}>
        <Typography variant="h5">Pantry Tracker</Typography>
        <Typography color='secondary' variant="h6">Track your items with ease ✨</Typography>
      </Box>
      <Box sx={{display:'flex', flexDirection: 'column', alignItems: 'center'}}>
        <Box sx={{display:'flex', justifyContent:'space-between', width: '100%', maxWidth:600, mb:2}}>
        <TextField
            label="Search"
            variant="outlined"
            fullWidth
            value={searchQuery}
            onChange={handleSearchChange}
            sx={{mt:4, width: 'calc(100% - 170px)', height:'100%'}}
          />
          <Button 
            variant="contained" 
            color="secondary"
            onClick={()=> {handleOpen()}}
            sx={{mt: 6, color: '#FFFFFF', height:'100%'}}
          >
            Add New Item
          </Button>
        </Box>
        <TableContainer component={Paper} sx={{maxWidth:600, m:'0 auto', mt:2, maxHeight:'300px', overflow:'auto'}}>
          <Table>
            <TableHead>
              <TableRow>
                <TableCell sx={tableCellStyle.header}>Item</TableCell>
                <TableCell align="right" sx={tableCellStyle.header}>Quantity</TableCell>
                <TableCell align="right" sx={tableCellStyle.header}> </TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {filteredInventory.map(({name, quantity}) => (
                  <TableRow key={name}>
                    <TableCell sx={tableCellStyle.body}>
                      {name.charAt(0).toUpperCase() + name.slice(1)}
                    </TableCell>
                    <TableCell align="right" sx={tableCellStyle.body}>{quantity}</TableCell>
                    <TableCell align="right" sx={tableCellStyle.body}>
                      <IconButton onClick={()=> addItem(name)}><AddIcon/></IconButton>
                      <IconButton onClick={()=> removeItem(name)}><RemoveIcon/></IconButton>
                      <IconButton onClick={()=> {handleEditOpen({ name, quantity })}}><EditIcon/></IconButton>
                    </TableCell>
                  </TableRow>
                )
              )}
          

  </TableBody>
          </Table>
        </TableContainer>
      </Box>

      {/*Modal component for Add*/}
      <Modal open={open} onClose={handleClose}>
        <Box
          position="absolute" top="50%" left="50%"
          width={400} bgcolor="white" border="2px solid #000"
          boxShadow={24} p={3} display="flex" 
          flexDirection="column" gap={3} sx={{transform: "translate(-50%, -50%)"}}
        >
          <Typography variant="body2">Add New Item</Typography>
          <Stack width="100%" direction="row" spacing={2}>
            <TextField label="Item Name" fullWidth
            sx={{flex: 1}}
            value={itemName} onChange={(e)=>{
              setItemName(e.target.value)
            }}/>
            <TextField label="Quantity" fullWidth
            sx={{flex: 0.50}}
            value={itemQuantity} onChange={(e)=>{
              setItemQuantity(e.target.value)
            }}/>
          </Stack>
          <Button variant="text" onClick={()=>{
            addNewItem(itemName, itemQuantity)
            setItemName('')
            setItemQuantity('')
            handleClose()
          }}>Done</Button>
        </Box>
      </Modal>

      {/*Modal component for Edit*/}
      <Modal open={editOpen} onClose={handleEditClose}>
        <Box
          position="absolute" top="50%" left="50%"
          width={400} bgcolor="white" border="2px solid #000"
          boxShadow={24} p={3} display="flex" 
          flexDirection="column" gap={3} sx={{transform: "translate(-50%, -50%)"}}
        >
          <Typography variant="body2">Edit Item</Typography>
          <Stack width="100%" direction="row" spacing={2}>
            <TextField label="Item Name" fullWidth
            sx={{flex: 1}}
            value={itemName} onChange={(e)=>{
              setItemName(e.target.value)
            }}/>
            <TextField label="Quantity" fullWidth
            sx={{flex: 0.50}}
            value={itemQuantity} onChange={(e)=>{
              setItemQuantity(e.target.value)
            }}/>
          </Stack>
          <Button variant="text" onClick={()=>{
            if (currentItem) {
              updateItem(currentItem.name, itemName, itemQuantity)
            }
            setItemName('')
            setItemQuantity('')
            handleEditClose()
          }}>Done</Button>
        </Box>
      </Modal>
    </Container>
  )
}

//layout.js
import { Poppins } from "next/font/google";
import { Box, Typography } from "@mui/material";
import ThemeProviderComponent from "./ThemeProvider";
import "./globals.css";

const poppins = Poppins({ 
  subsets: ["latin"],
  weight: ['400', '700']
});

export const metadata = {
  title: "Pantry Tracker",
  description: "Generated by create next app",
};

const Header = () => (
  <Box
    component="header" sx={{
      bgcolor: 'background.paper', padding: '0.75rem',
      textAlign: 'start', paddingLeft: '3rem',
      top:0, width: '100%'
    }}
  >
    <Typography variant="h6" sx={{
      color: 'text.primary', fontWeight: 'bold', fontSize: '1.5rem'
    }}>
      Pantry Tracker
    </Typography>
  </Box>
)

const Footer = () => (
  <Box component="footer" sx={{p:2, textAlign:'center', bgcolor:'background.default'}}>
    <Typography variant="body2">
    &copy; 2024 Pantry Tracker. All rights reserved.
    </Typography>
  </Box>
)

export default function RootLayout({children}) {
  return (
    <html lang="en">
      <body className={poppins.className} style={{
        display: 'flex', flexDirection: 'column', minHeight: '100vh'
      }}>
        <ThemeProviderComponent>
          <Header/>
          <Box component="main" sx={{flex: '1 0 auto'}}>{children}</Box>
          <Footer/>
        </ThemeProviderComponent>
      </body>
    </html>
  )
}
// ThemeProvider.js
'use client';

import { CssBaseline } from "@mui/material";
import { green, red } from "@mui/material/colors";
import { ThemeProvider, createTheme } from "@mui/material/styles";
import PropTypes from 'prop-types'

const theme = createTheme({
    palette: {
        primary: {main: '#0959AA'},
        secondary: {main: '#6709AA'},
        error: {main: red[500]},
        success: {main: green[500]},
        background: {default: '#f5f5f5', paper: '#ffffff'},
        text: {primary: '#333333', secondary: '#777777'}
    },
    typography: {
        fontFamily: 'Poppins, Arial, sans-serif'
    }
});

const ThemeProviderComponent = ({children}) => {
    return (


        <ThemeProvider theme={theme}>
            <CssBaseline/>
            {children}
        </ThemeProvider>
    )
}

ThemeProviderComponent.propTypes = {
    children: PropTypes.node.isRequired
}

export default ThemeProviderComponent;

Here’s the js code for my nextjs14 project:

My program was actually running fine when i was developing my pantry tracker app but then the next day i decided to integrate open ai vision api (left that after finding out its paid) but that was in a separate file. all the new changes i made as far as i remember was in new files but even after deleting i keep seeing this error

Force line breaks in javascript html string

I have php which is being used to pull a text description of an image data-caption="'.$attachment_data['caption'].'"

I’m calling that text in my javascript modal window with $("article").html(this.el.attr('data-caption'))

It outputs the text correctly, but I cannot get the text to include any line breaks. I’ve tried using css styles to fix this, such as word-wrap: break-word; but none of them affect the line breaks. I’ve also made sure to use .html() instead of .text().

Is the problem with the string itself, and the solution is using javascript to break lines manually?

Here’s the fiddle

Apps Script – How to send an HTTP 200 response?

I have an Apps Script running as a web app for Slack. Slack sends a POST request and expects a HTTP 200 response within 3 seconds, otherwise it shows a timeout error to the Slack user. I have been using the following line to accomplish this, after my script is finished executing.

return ContentService.createTextOutput("")

However, my latest script is taking longer than 3 seconds to execute. So if I run my code before that line, my users will see the timeout error, but if I run the code after, it obviously will not be executed since the function has terminated with the return statement.

Is there a way to respond to the POST request with a 200 without terminating my script?

I have tried using ContentService without the return statement but it does not appear to do anything, unless I am using it incorrectly.

cannot acces elements when using x-for

I made this code to get a json file and display a field from each of the elements

<script src="https://www.unpkg.com/alpinejs" defer></script>

<script>
  function note(a) {
      console.log(a);
      return a;
  }
</script>

<div x-data="{ bios: [] }",
     x-init="bios = note(await (await fetch('http://localhost:8000/bios.json')).json())">
  <template x-for="h in bios">
    <p>member</p>
    <p x-text="h.Name"></p>
  </template>
</div>

when it loads, the console has the json file in it, but the page just has the word member as many times as there are elements and nothing else

Using setInterval in javascript

I modified a javascript code using setInterval to alternate between two messages using innerHTML. I tried document.write and it works. But innerHTML does not work.

// Create global interval and color variables
let interval;
let sentence;
let word;
let color = 'white';
// const word = document.querySelector("#one").innerHTML;
function startTogglingBg() {
  word = document.querySelector("#one").innerHTML;
  interval = setInterval(function() {
    if (color === 'white') {
      sentence = "Hey";
      color = 'red';
    } else {
      sentence = "You!";
      color = 'white';
    }
    // document.body.style.backgroundColor=color;
    // document.write(sentence);
    word = sentence;
  }, 1500);
}

function stopTogglingBg() {
  clearInterval(interval);
}

window.addEventListener('load', function() {
  btnStart = document.getElementById('start');
  btnStop = document.getElementById('stop');

  btnStart.addEventListener('click', startTogglingBg);
  btnStop.addEventListener('click', stopTogglingBg);
});
<button id="start">Start</button>
<button id="stop">Stop</button>

<p id = "one">Hello</p>

I expected the p tag value will change from the words “Hey” and “You!” every 1.5 seconds using setInterval. I tried document.write and it worked but the result is like this:

HeyYou!HeyYou!HeyYou!.......

Embedding Google Maps Into HTML using A Variable as the “src” attribute

I am trying to make a pokemon go (you walk around in real life and catch pokemon) style game in HTML. Geolocation is okay at the moment but my main problems are in the map.

The map automatically sets itself to a predetermined location and then changes to the player’s location, so I can check it works. But when I press the show position button, it switches from the predetermined location to google saying that the URL is malformed.

It shows not exactly my location but near, maybe 5 mile radius, so I know the location is fine.

This is my code:

<!DOCTYPE html>
<html>
<body>

<h2>JavaScript Geolocation API</h2>
<p>Click the button to get your coordinates.</p>

<button onclick="getLocation()">Try It</button>

<p id="demo"></p>
<iframe id="irfm_pos" width="100%" height="600" src="https://maps.google.com/maps?width=100%&amp;height=600&amp;hl=en&amp;coord=53.2188, -6.6736&amp;q=53.2188,-6.6736&amp;ie=UTF8&amp;t=&amp;z=14&amp;iwloc=B&amp;output=embed" frameborder="0" scrolling="no" marginheight="0" marginwidth="0"></iframe>

<script>
const x = document.getElementById("demo");

function getLocation() {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(showPosition);
  } else { 
    x.innerHTML = "Geolocation is not supported by this browser.";
  }
}

function showPosition(position) {
    const ifrm = document.getElementById("irfm_pos");

    x.innerHTML = "Latitude: " + position.coords.latitude + 
    "<br>Longitude: " + position.coords.longitude;

    var pos_url = "https://maps.google.com/maps?width=100%&amp;height=600&amp;hl=en&amp;coord="+ position.coords.latitude +", "+ position.coords.longitude + "&amp;q="+ position.coords.latitude + ", "+ position.coords.longitude + "&amp;ie=UTF8&amp;t=&amp;z=14&amp;iwloc=B&amp;output=embed";
    ifrm.setAttribute("src", pos_url);
    console.log(pos_url);
}
</script>
</body>
</html>

As you can see, I am declaing a variable called “pos_url” and setting the iframe attribute to the value of “pos_url”.

var pos_url = "https://maps.google.com/maps?width=100%&amp;height=600&amp;hl=en&amp;coord="+ position.coords.latitude +", "+ position.coords.longitude + "&amp;q="+ position.coords.latitude + ", "+ position.coords.longitude + "&amp;ie=UTF8&amp;t=&amp;z=14&amp;iwloc=B&amp;output=embed";
ifrm.setAttribute("src", pos_url);
console.log(pos_url);

I also am printing pos_url so I can check it is fine, it is not but I do not know how to fix it.
https://maps.google.com/maps?width=100%&height=600&hl=en&coord=53.22043653273203,%20-6.674305582123162&q=53.22043653273203,%20-6.674305582123162&ie=UTF8&t=&z=14&iwloc=B&output=embed

It keeps the %20 in the url when it is supposed to be a space, I have tried this without the space but then it is malformed (I used f12 to add the spaces)

UseState React hook is changing variable, but not re-rendering

I’m working on creating a shopping cart. The cart itself (the list of all items in the cart) is a component and each cart item is a component. In each cart item, there’s a dropdown list to change the quantity of that item you’d like to buy (1-5). There is also an X button that will remove that item from the cart. The X button (removing the item) works just fine. Here’s the code for that:

  function removeItem(id) {
    const newCart = cart.filter((item) => item.id !== id)
    setCart(newCart);
  }

This is how I’m removing an item from the cart (creating a newCart variable and using setCart(newCart) to update that. This code, again, works just fine and the cart item is removed from the user’s screen.

However, I’m trying to incorporate a similar approach to adjusting the quantity of an item. When I run the code below, however, the visual updates are not happening. However, when I console.log() the cart variable, the updates are shown there. Here is the code I have for that:

  function changeQuantity(event, id) {
    let newCart = cart;
    for (let i=0; i<cart.length; i++) {
      if (cart[i].id === id) {
        newCart[i].quantity = parseInt(event.target.value); // update quantity
        newCart[i].name = "CHANGED"; // debugging purposes, to know if it was changed
      }
    }
    setCart(newCart); // this doesn't seem to change the cart visually
    console.log(cart); // but in the console I can see the changes being made
  }

For some reason, when I go into the console, I can see the changes being made (the price is adjusted to the correct quantity, and the name of the item is “CHANGED”); but visually in the cart, this change is not shown. The removal of an item works fine, but using the same approach is not working to adjust the quantity.

I’m not sure where the logic is going wrong here, so any help is appreciated.

EDIT: Something interesting that happens is that when I save the code and the page is re-rendered, everything is updated accordingly, even it wasn’t before.

Multiple redirects using React router dom

I’m using React Router to redirect to a login page when user is not logged in, like this:

return (
    <main className="App  relative">
      <Routes>
        <Route path="/" element={<AuthLayout />}>
          <Route path="/" element={<Login />} />
        </Route>

        <Route path='/*' element={<PrivateRoute/>}>
          <Route path="/*" element={<Layout />}>
            <Route path="dashboard" element={<Dashboard />} />
          </Route>

          <Route path="/*" element={<Layout />}>
            <Route path="timeline" element={<TimeLine />} />
          </Route>
        </Route>
      </Routes>
    </main>
);

this is my PrivateRoute:

const PrivateRoute = () => {    
    const [currentUser] = useAtom(currentUserAtom);
    const isAuth = (currentUser && currentUser.token);

    const prevLocation = useLocation();
    var redirectTo = `/login?redirectTo=${prevLocation.pathname}`;
    console.log(redirectTo);        
    return isAuth ? <Outlet /> : <Navigate to={redirectTo} />;
}

export default PrivateRoute;

The problem is that the PrivateRoute executes 3 times, with these results:

/login?redirectTo=/dashboard 
/login?redirectTo=/login
/login?redirectTo=/login

And the final redirect page is http://localhost:5173/login?redirectTo=/login instead of http://localhost:5173/login?redirectTo=/dashboard

What’s wrong?

Is it OK to add Google Tag codes onto Github Repository?

Stuff like API keys uploaded to Github Repository could cause some of the devastating problems, so I want to know if the Google Tag codes like( could also become a source of problems.

Example Google Tag code:

<--Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-xxxxxxxxxx"></script> <script> 
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);} gtag('js', new Date()) ;
gtag('config', 'G-xxxxxxxxxx') ;
</script>

custom created cron function is not working

i have attached the following function with my cron which hits every 10 mins , the problem is that

the following function is not running on my stage environment (was running perfectly fine on my local). I believe that the problem resides within function _fetchIdToProcess , since the whole code is dependent on the length of the array . Also using bluebird for “promosifying” redis calls

const CacheUtils = require('../../utils/cache-utils');
const ESUtils = require('../../utils/es-utils');
const esClient = ESUtils.initializeESConnection();


/**
 * @description the following cron fetches all the docs from events needed to be merged on the basis of 
 * fileGroupId , after merging all the "dangling docs" will be deleted 
 */


module.exports = class MergeFileEvents {
  static async mergeFileEvent() {
    const client = await CacheUtils.getClient();
    const fileGroupIds = await this._fetchIdToProcess('file_upload*');

    if (fileGroupIds.length > 0) {

      // locking the fileGroupIds , for preventing dirty reads

      await client.saddAsync('processing_file_ids',fileGroupIds);

      // main implementation for merging docs
      await this._mergeDocs(fileGroupIds);

      // releasing the locks (deleting the ids) 
      await this._releaseLockFileGroupIds(fileGroupIds)
    }
  }


  static async _fetchIdToProcess(pattern) {
    const client = await CacheUtils.getClient();
    let keys = [];
    let cursor = '0';

    do {
      const reply = await client.scanAsync(cursor, 'MATCH', pattern, 'COUNT', 1000);
      cursor = reply[0];
      keys = keys.concat(reply[1]);
    } while (cursor !== '0');

    const processingIds = await client.smembersAsync('processing_file_ids') || [];

    return keys.filter(key => !processingIds.includes(key));
  }


  static async _mergeDocs(fileGroupIds) {
    const date = new Date();
    const client =await CacheUtils.getClient();
    for (let id of fileGroupIds) {
      const eventIds = await client.smembersAsync(id);

      if (eventIds.length <= 1) continue; // no need to merge in this case 
    
      const  body  = await esClient.search({
        index: `events_${date.toJSON().split('T')[0]}`,
        body: {
          query: { ids: { values: eventIds } },
          _source: ['file.id'],
          size: eventIds.length
        }
      });
      
      const fileIdsPayload = await Promise.all(eventIds.map(async (id) => {
        const createdAt = await client.getAsync(id); 
          return{
             id: body.hits.hits.find(hit => hit._id == id)?._source.file.id,
             createdAt
          }
      }));

      const lastUpdate = fileIdsPayload.reduce((max, curr) => {
        return new Date(curr.createdAt) > new Date(max.createdAt) ? curr : max;
      }).createdAt;


      await esClient.update({
        index: `events_${date.toJSON().split('T')[0]}`,
        id: eventIds[0],
        body: {
          doc: {
            files: fileIdsPayload,
            updatedAt: lastUpdate
          }
        }
      });

      const deleteEvents = eventIds.slice(1).map(id => ({
        delete: {
          _index: `events_${date.toJSON().split('T')[0]}`,
          _id: id
        }
      }));

      await esClient.bulk({
        body: deleteEvents
      });

    }
  }

  static async _releaseLockFileGroupIds(fileGroupIds) {

    const client =await CacheUtils.getClient();

    // deleting fileGroupIds from processing set
    await client.sremAsync('processing_file_ids',fileGroupIds);

    // deleting timestamps of eventId
    for (let id of fileGroupIds) {
      await client.delAsync(id);
    }
  }
}