Discrepancy in Total Coins During Match in React Game

I’m developing a game using React where 3 players (one human and two AIs) start with 50 coins each, making the total pool of coins 150. However, during the match, I noticed a discrepancy in the total number of coins. For example, during one instance, the coin distribution was 40, 43, and 45, which sums up to 128 instead of 150.

Additionally, I have observed that for the AI players, the coins reduce by 4 instead of 2 as intended.

App.js

    import React, { useState, useEffect, useRef } from 'react';
import StartScreen from './components/StartScreen';
import GameBoard from './components/GameBoard';
import WinnerSelectionModal from './components/WinnerSelectionModal';
import CongratulationsModal from './components/CongratulationsModal';
import { aiDecisions } from './utils/gameLogic';
import { createDeck, shuffleDeck, dealCards } from './utils/deck';

const variations = [
  { name: 'Normal', cards: 3 },
  { name: 'Normal-II', cards:4},
];

function App() {
  const [showStartScreen, setShowStartScreen] = useState(true);
  const [selectedVariation, setSelectedVariation] = useState(null);
  const [players, setPlayers] = useState([
    { name: 'You', coins: 50, hand: [], isHuman: true, active: true, usedCoins: 0, isBlind: false, blindCount: 0, hasSeenCards: false, didUserPickBlindUpfront: false, skipTurnThisRound: false },
    { name: 'AI 1', coins: 50, hand: [], isHuman: false, active: true, usedCoins: 0, isBlind: false, blindCount: 0, hasSeenCards: false, skipTurnThisRound: false },
    { name: 'AI 2', coins: 50, hand: [], isHuman: false, active: true, usedCoins: 0, isBlind: false, blindCount: 0, hasSeenCards: false, skipTurnThisRound: false },
  ]);
  const [currentPlayer, setCurrentPlayer] = useState(0);
  const [deck, setDeck] = useState(shuffleDeck(createDeck()));
  const [gamePhase, setGamePhase] = useState('playing');
  const [showAICards, setShowAICards] = useState(false);
  const [dealing, setDealing] = useState(false);
  const [dealtHands, setDealtHands] = useState([]);
  const [showWinnerModal, setShowWinnerModal] = useState(false);
  const [showCongratulationsModal, setShowCongratulationsModal] = useState(false);
  const [winner, setWinner] = useState(null);
  const [blindChoicePhase, setBlindChoicePhase] = useState(false);
  const [roundCount, setRoundCount] = useState(1);
  const [playersSkippedInRound1, setPlayersSkippedInRound1] = useState(0);
  const [showAllCards, setShowAllCards] = useState(false);
  const [currentRoundPool, setCurrentRoundPool] = useState(0); // New state for tracking round pool

  const prizePool = 150;
  const isAITurnProcessing = useRef(false);

  const startGame = (variation) => {
    setSelectedVariation(variation);
    setBlindChoicePhase(true);
    setCurrentRoundPool(0); // Reset round pool at game start
  };

  const handleUserBlindChoice = (choice) => {
    setPlayers((prev) => {
      const updated = [...prev];
      const userIndex = 0;
      let userCoinsDeducted = 0;
      let aiCoinsDeducted = 0;

      if (choice === 'blind') {
        updated[userIndex].isBlind = true;
        updated[userIndex].blindCount = 1;
        updated[userIndex].coins -= 2;
        updated[userIndex].usedCoins += 2;
        userCoinsDeducted = 2;
      } else {
        updated[userIndex].coins -= 1;
        updated[userIndex].usedCoins += 1;
        userCoinsDeducted = 1;
        updated[userIndex].isBlind = false;
        updated[userIndex].hasSeenCards = true;
      }
      updated[userIndex].skipTurnThisRound = true;

      // Deduct 1 coin from each AI
      updated.forEach((p, idx) => {
        if (!p.isHuman && p.active) {
          p.coins -= 1;
          p.usedCoins = 1;
          p.skipTurnThisRound = true;
          aiCoinsDeducted += 1;
        }
      });

      // Update round pool by total deducted
      setCurrentRoundPool((prevPool) => prevPool + userCoinsDeducted + aiCoinsDeducted);

      return updated;
    });

    setBlindChoicePhase(false);
    setCurrentPlayer(0);
    distributeCards(selectedVariation);
  };

  const distributeCards = async (variation) => {
    const newDeck = shuffleDeck(createDeck());
    setDeck(newDeck);
    setDealing(true);
    const hands = dealCards(newDeck, players.length, variation.cards);
    const tempDealtHands = players.map(() => []);
    for (let cycle = 0; cycle < variation.cards; cycle++) {
      for (let playerIndex = 0; playerIndex < players.length; playerIndex++) {
        if (hands[playerIndex][cycle]) {
          tempDealtHands[playerIndex].push(hands[playerIndex][cycle]);
          setDealtHands([...tempDealtHands]);
          await new Promise((resolve) => setTimeout(resolve, 100));
        }
      }
    }
    setPlayers((prev) =>
      prev.map((player, index) => ({
        ...player,
        hand: hands[index] || [],
        active: true,
      }))
    );
    setDealing(false);
    setGamePhase('playing');
    setShowAICards(false);
    setPlayersSkippedInRound1(0);
    await new Promise((resolve) => setTimeout(resolve, 500));
    nextTurn();
  };

  const handlePlayBlind = () => {
    setPlayers((prev) => {
      const updated = [...prev];
      if (updated[currentPlayer].blindCount < 2) {
        updated[currentPlayer].isBlind = true;
        updated[currentPlayer].blindCount += 1;
        updated[currentPlayer].coins -= 2;
        updated[currentPlayer].usedCoins += 2;
        setCurrentRoundPool(prevPool => prevPool + 2); // Add to round pool
      }
      return updated;
    });
  };

  const handleSeeCards = () => {
    setPlayers((prev) => {
      const updated = [...prev];
      updated[currentPlayer].isBlind = false;
      updated[currentPlayer].hasSeenCards = true;
      return updated;
    });
  };

  const handleContinue = () => {
    setPlayers((prev) => {
      const updated = [...prev];
      updated[currentPlayer].coins -= 2;
      updated[currentPlayer].usedCoins += 2;
      setCurrentRoundPool(prevPool => prevPool + 2); // Add to round pool
      return updated;
    });
    nextTurn();
  };

  const handleOut = () => {
    setPlayers((prev) => {
      const updated = [...prev];
      updated[currentPlayer].active = false;
      return updated;
    });
    if (activePlayers() === 1) {
      awardPrizeToWinner();
    } else if (activePlayers() === 2) {
      setGamePhase('showdown');
    }
    nextTurn();
  };

  const handleRevealCards = () => {
    setPlayers((prev) => {
      const updated = [...prev];
      if (updated[currentPlayer].isHuman && !updated[currentPlayer].hasSeenCards) {
        updated[currentPlayer].isBlind = false;
        updated[currentPlayer].hasSeenCards = true;
      }
      return updated;
    });
  };

  const handleBlindShow = () => {
    if (activePlayers() === 2 && players[currentPlayer].isHuman && !players[currentPlayer].hasSeenCards) {
      console.log('Blind Show triggered, setting showAllCards to true');
      setShowAllCards(true);
      setTimeout(() => setShowWinnerModal(true), 2000);
    } else {
      console.log('Blind Show conditions not met:', {
        activePlayers: activePlayers(),
        isHuman: players[currentPlayer].isHuman,
        hasSeenCards: players[currentPlayer].hasSeenCards,
      });
    }
  };

  const handleShow = () => {
    if (activePlayers() === 2 && players[currentPlayer].isHuman) {
      setShowAllCards(true); // Reveal all cards during regular show
      setTimeout(() => setShowWinnerModal(true), 2000);
    }
  };

  const handleSelectWinner = (winnerName) => {
    setShowWinnerModal(false);
    if (winnerName !== null) {
      const updatedPlayers = [...players];
      const activePlayersList = updatedPlayers.filter((p) => p.active);
      if (activePlayersList.length === 2) {
        const winnerPlayer = activePlayersList.find((p) => p.name === winnerName);
        const loserPlayer = activePlayersList.find((p) => p.name !== winnerName);
        if (winnerPlayer && loserPlayer) {
          const winnerGlobalIndex = updatedPlayers.findIndex((p) => p.name === winnerPlayer.name);
          updatedPlayers[winnerGlobalIndex].coins += currentRoundPool; // Use current round pool
          updatedPlayers[winnerGlobalIndex].usedCoins = 0;
          const loserGlobalIndex = updatedPlayers.findIndex((p) => p.name === loserPlayer.name);
          updatedPlayers[loserGlobalIndex].active = false;
          updatedPlayers[loserGlobalIndex].usedCoins = 0;
        }
      }
      setPlayers(updatedPlayers);
      const finalWinner = updatedPlayers.find((p) => p.active);
      setWinner(finalWinner);
      setShowCongratulationsModal(true);
      setTimeout(() => {
        setShowCongratulationsModal(false);
        resetGame();
      }, 5000);
    }
  };

  const handleQuit = () => {
    setPlayers((prev) => {
      const updated = [...prev];
      updated[currentPlayer].active = false;
      return updated;
    });
    awardPrizeToWinner();
  };

  const nextTurn = () => {
    let next = (currentPlayer + 1) % players.length;
    while (!players[next].active) {
      next = (next + 1) % players.length;
    }

    if (roundCount === 1 && players[next].skipTurnThisRound) {
      setPlayers((prev) => {
        const updated = [...prev];
        updated[next].skipTurnThisRound = false;
        return updated;
      });
      setPlayersSkippedInRound1((prev) => prev + 1);
      if (playersSkippedInRound1 + 1 === activePlayers()) {
        setRoundCount(2);
        setCurrentPlayer(0);
        setPlayersSkippedInRound1(0);
      } else {
        setCurrentPlayer(next);
      }
    } else if (players[next].skipTurnThisRound) {
      setPlayers((prev) => {
        const updated = [...prev];
        updated[next].skipTurnThisRound = false;
        return updated;
      });
      next = (next + 1) % players.length;
      while (!players[next].active) {
        next = (next + 1) % players.length;
      }
      if (next === 0) {
        setRoundCount((prev) => prev + 1);
      }
      setCurrentPlayer(next);
    } else {
      if (next === 0) {
        setRoundCount((prev) => prev + 1);
      }
      setCurrentPlayer(next);
    }
  };

  const activePlayers = () => players.filter((p) => p.active).length;

  const awardPrizeToWinner = () => {
    const winnerIndex = players.findIndex((p) => p.active);
    if (winnerIndex !== -1) {
      const updatedPlayers = [...players];
      updatedPlayers[winnerIndex].coins += currentRoundPool; // Use current round pool
      updatedPlayers[winnerIndex].usedCoins = 0;
      updatedPlayers.forEach((p, idx) => {
        if (idx !== winnerIndex) p.usedCoins = 0;
      });
      setPlayers(updatedPlayers);
      setWinner(updatedPlayers[winnerIndex]);
      setShowCongratulationsModal(true);
      setTimeout(() => {
        setShowCongratulationsModal(false);
        resetGame();
      }, 5000);
    }
  };

  const resetGame = () => {
    const totalCoins = players.reduce((sum, p) => sum + p.coins, 0);
    const adjustment = 150 - totalCoins;
    const coinsPerPlayer = Math.floor((150 + adjustment) / 3);

    setSelectedVariation(null);
    setGamePhase('playing');
    setShowAICards(false);
    setShowAllCards(false);
    setDealtHands([]);
    setCurrentPlayer(0);
    setRoundCount(1);
    setPlayersSkippedInRound1(0);
    setCurrentRoundPool(0); // Reset round pool
    setPlayers((prevPlayers) =>
      prevPlayers.map((player) => ({
        ...player,
        hand: [],
        active: true,
        coins: coinsPerPlayer,
        usedCoins: 0,
        isBlind: false,
        blindCount: 0,
        hasSeenCards: false,
        didUserPickBlindUpfront: false,
        skipTurnThisRound: false,
      }))
    );
  };

  // AI player turn logic
  useEffect(() => {
    const current = players[currentPlayer];
    if (
      !current.isHuman &&
      current.active &&
      activePlayers() > 1 &&
      !current.skipTurnThisRound &&
      !isAITurnProcessing.current
    ) {
      isAITurnProcessing.current = true;
      const timer = setTimeout(() => {
        const decisions = aiDecisions(activePlayers());
        setPlayers((prev) => {
          const updated = [...prev];
          const p = updated[currentPlayer];
          if (!p.isHuman && p.active && !p.skipTurnThisRound) {
            const decision = decisions[`ai${currentPlayer}`];
            console.log(`AI ${currentPlayer} - Decision: ${decision}`);
            if (decision && p.coins >= 2) {
              console.log(`AI ${currentPlayer} - Before deduction: Coins = ${p.coins}, UsedCoins = ${p.usedCoins}, CurrentRoundPool = ${currentRoundPool}`);
              p.coins -= 2; // Consistently 2 coins for AI
              p.usedCoins += 2;
              setCurrentRoundPool((prevPool) => prevPool + 2); // Add to round pool - using functional update
              console.log(`AI ${currentPlayer} - After deduction: Coins = ${p.coins}, UsedCoins = ${p.usedCoins}, CurrentRoundPool = ${currentRoundPool}`);
            } else {
              console.log(`AI ${currentPlayer} - Cannot continue, going out: Coins = ${p.coins}, UsedCoins = ${p.usedCoins}`);
              p.active = false;
            }
          }
          return updated;
        });
        if (activePlayers() === 1) {
          awardPrizeToWinner();
        } else if (activePlayers() === 2) {
          setGamePhase('showdown');
        }
        nextTurn();
        isAITurnProcessing.current = false;
      }, 1500);
      return () => {
        clearTimeout(timer);
        isAITurnProcessing.current = false;
      };
    }
  }, [currentPlayer, players]);

  if (showStartScreen) {
    return <StartScreen onStart={() => setShowStartScreen(false)} />;
  }

  return (
    <div className="min-h-screen bg-gray-800 flex flex-col items-center justify-center relative">
      {!selectedVariation ? (
        <div className="text-white">
          <h1 className="text-3xl mb-4">Select a Game Variation</h1>
          <div className="grid grid-cols-3 gap-4">
            {variations.map((variation) => (
              <button
                key={variation.name}
                onClick={() => startGame(variation)}
                className="p-4 bg-blue-600 rounded-lg hover:bg-blue-700"
              >
                {variation.name} ({variation.cards} cards)
              </button>
            ))}
          </div>
        </div>
      ) : blindChoicePhase ? (
        <div className="flex flex-col items-center space-y-4 text-white">
          <h2 className="text-2xl">Do you want to Play Blind or Play Seen?</h2>
          <div className="flex space-x-4">
            <button
              onClick={() => handleUserBlindChoice('blind')}
              className="px-6 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700"
            >
              Play Blind
            </button>
            <button
              onClick={() => handleUserBlindChoice('seen')}
              className="px-6 py-3 bg-green-600 text-white rounded-lg hover:bg-green-700"
            >
              Play Seen
            </button>
          </div>
        </div>
      ) : (
        <>
          <GameBoard
            players={players}
            currentPlayer={currentPlayer}
            onContinue={handleContinue}
            onOut={handleOut}
            variation={selectedVariation}
            prizePool={prizePool}
            currentRoundPool={currentRoundPool} // Pass the current round pool
            gamePhase={gamePhase}
            onRevealCards={handleRevealCards}
            onBlindShow={handleBlindShow}
            onShow={handleShow}
            onQuit={handleQuit}
            dealing={dealing}
            dealtHands={dealtHands}
            showAICards={showAICards}
            onPlayBlind={handlePlayBlind}
            onSeeCards={handleSeeCards}
            roundCount={roundCount}
            showAllCards={showAllCards}
          />
          {showWinnerModal && (
            <WinnerSelectionModal
              players={players.filter((p) => p.active)}
              onSelectWinner={handleSelectWinner}
            />
          )}
          {showCongratulationsModal && winner && (
            <CongratulationsModal winner={winner} />
          )}
        </>
      )}
    </div>
  );
}

export default App;

GameBoard.js

import React from 'react';
import PlayerPanel from './PlayerPanel';
import Controls from './Controls';
import ReactionButton from './ReactionButton';

function GameBoard({
  players,
  currentPlayer,
  onContinue,
  onOut,
  variation,
  prizePool,
  currentRoundPool, // New prop
  gamePhase,
  onRevealCards,
  onBlindShow,
  onShow,
  onQuit,
  dealing,
  dealtHands,
  showAICards,
  onPlayBlind,
  onSeeCards,
  roundCount,
  showAllCards,
}) {
  const activePlayersCount = players.filter((p) => p.active).length;
  const currentPlayerHasSeen = players[currentPlayer].hasSeenCards;
  const didUserPickBlindUpfront = players[0].didUserPickBlindUpfront || false;

  return (
    <div className="text-center">
      <div className="aquamorphic-bg mx-auto max-w-5xl mt-8">
        <h1 className="text-4xl text-white mb-8">Teen Patti - {variation.name}</h1>
        <div className="flex justify-center space-x-12 mb-4">
          <p className="text-2xl text-yellow-300">Total Prize Pool: {prizePool}</p>
          <p className="text-2xl text-green-300">Current Pot: {currentRoundPool}</p>
        </div>
        <div className="grid grid-cols-3 gap-4">
          {players.map((player, index) => (
            <PlayerPanel
              key={index}
              player={player}
              isCurrent={index === currentPlayer}
              dealing={dealing}
              dealtHand={dealtHands[index] || []}
              showAICards={showAICards}
              showAllCards={showAllCards}
            />
          ))}
        </div>

        <div className="mt-8">
          <ReactionButton />
        </div>

        {gamePhase === 'showdown' && activePlayersCount === 2 && players[currentPlayer].isHuman ? (
          <div className="mt-8 flex space-x-4 justify-center">
            {!currentPlayerHasSeen && (
              <button
                onClick={onRevealCards}
                className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
              >
                Reveal Cards
              </button>
            )}
            {!currentPlayerHasSeen && (
              <button
                onClick={onBlindShow}
                className="px-6 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700"
              >
                Blind Show
              </button>
            )}
            {currentPlayerHasSeen && (
              <button
                onClick={onShow}
                className="px-6 py-3 bg-purple-600 text-white rounded-lg hover:bg-purple-700"
              >
                Show
              </button>
            )}
            <button
              onClick={onOut}
              className="px-6 py-3 bg-red-600 text-white rounded-lg hover:bg-red-700"
            >
              Out
            </button>
          </div>
        ) : (
          <Controls
            onContinue={onContinue}
            onOut={onOut}
            isHumanTurn={players[currentPlayer].isHuman && players[currentPlayer].active}
            onPlayBlind={onPlayBlind}
            onSeeCards={onSeeCards}
            currentBlindCount={players[currentPlayer].blindCount}
            isUserBlind={players[currentPlayer].isBlind}
            hasSeenCards={currentPlayerHasSeen}
            roundCount={roundCount}
            didUserPickBlindUpfront={didUserPickBlindUpfront}
          />
        )}
      </div>
    </div>
  );
}

export default GameBoard;

Could anyone help me identify what might be causing these discrepancies and suggest a fix? Any help would be greatly appreciated!

URLPattern class in Node v23.10 returns Uncaught ReferenceError: URLPattern is not defined

I’m trying out the URLPattern class in Node v23.10, installed with nvm

When I ran the following in the cli or as an executable

const p = new URLPattern({pathname: 'annotations/:id'})

I got this

Uncaught ReferenceError: URLPattern is not defined

I understand this is an experimental feature, but there’s no guidance in the docs and examples I’ve read as to what flag(s) to run this script with.

Can’t import public key for encrypting content encryption key Crypto Web API

Im trying to encrypt a Content Encryption Key with a certain public key using javascript Crypto Web API but I get this error :

“SyntaxError: Cannot create a key using the specified key usages.”

The arraybuffer is the certificate, but it doesnt matter if its the public key the error stays the same.

Here is my code:

function(arraybuffer){
    crypto.subtle.importKey(            
        "spki",
        arraybuffer,
        {name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}},
        true,
        ["encrypt"]
    )
    .then(function(key){
        console.log(key);
        return key;
        }
    )
    .catch(function(e){console.log(e)})
}

I think its about keyUsage parameter which I use [“encrypt”], but i need this key to encrypt so Im clueless.

button still refreshing page with event stopPropagation and preventDefault

I have a problem where clicking a button refreshes the page. I even removed the button and directly placed the icon and onClick on the td and used event.preventDefault() and event.stopPropagation() but the refresh still occurs. There is no problem in the rest of the project and I also used BrowserRouter in the main file. On the same page I also had a button for search that was inside the form and it did not cause any problems.

users list component :


export default function UsersList() {
  const users = useSelector((state) => state.users);
  const dispatch = useDispatch();
  useEffect(() => {
    dispatch(fetchAllUsers());
  }, []);

  function handleDeleteUser(e, id) {
    // has refreshing problem
    e.stopPropagation();
    e.preventDefault();
    dispatch(deleteUser({ id }));
  }

  return (
    <div className="m-3">
      <div
        className={`my-5 mx-2 p-2 text-sm ${
          users.loading ? "opacity-50" : "opacity-100"
        }`}
      >
        {users.usersList.length === 0 ? (
          <p>no user</p>
        ) : (
          <table className="border-1 border-stone-300 rounded-lg w-full text-center border-collapse table-auto">
            <tbody>
              {users.usersList.map((user) => {
                return (
                  <tr key={user.id} className="border-1 border-stone-300 py-2">
                    <td className="py-3 border-r-1 border-stone-300 px-1">
                      {user.fName}
                    </td>
                    <td className="py-3 border-r-1 border-stone-300 px-1">
                      {user.lName}
                    </td>
                    <td className="py-3 border-r-1 border-stone-300 px-1">
                      {user.phone}
                    </td>
                    <td className="py-3 border-r-1 border-stone-300 px-1">
                      {user.email}
                    </td>
                    <td className="py-3 border-r-1 border-stone-300 px-1">
                      {user.username}
                    </td>
                    <td onClick={(e) => handleDeleteUser(e, user.id)}>❌</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}   

users slice:

         
const initialUser = {
  fName: "",
  lName: "",
  username: null,
  pass: null,
  phone: "",
  email: "",
  age: 0,
  isAuth: false,
};
const initialState = {
  singleUser: initialUser,
  usersList: [],
  loading: false,
  error: null,
};

const isPendingAction = (action) => action.type.endsWith("/pending");
export const deleteUser = createAsyncThunk(
  "users/deleteUser",
  async (payload, { rejectWithValue }) => {
    try {
      const res = await axios.request({
        method: "delete",
        url: HOST + "/" + payload.id,
      });
      return res;
    } catch (e) {
      return rejectWithValue(e.messege);
    }
  }
);
const usersReducer = createSlice({
  name: "users",
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    // ...
    //deleteUser
    builder.addCase(deleteUser.fulfilled, (state, action) => {
      state.loading = false;
      state.usersList = state.usersList.filter(
        (user) => user.id !== action.payload.id
      );
      state.error = null;
    });
    builder.addCase(deleteUser.rejected, (state, action) => {
      state.loading = false;
      state.error = action.payload;
      state.usersList = [];
    });
    builder.addMatcher(isPendingAction, (state) => {
      state.loading = true;
    });
  },
});                ` 

How can I get this javascript to run inside of my html DIV tags?

I have a php file that does some work that I am trying to run inside of my html via javascript. It is working however it does not appear to be respecting the div tag alignment. The script is running inside of Body but outside of Div. The reason I am running it this way is because without it, the php file takes too long to load and I want the page to load in a timely way, otherwise the browser just spins for 20 seconds while the data is fetched from a third party. The way it should work is the page should load quickly, and then when the data from the php page is ready it displays inside the div tags.

<div>

<script>
window.onload = function () {
        fetch("https://www.testdomain.com/test.php")
                .then(response => {
                        if (!response.ok) {
                                throw new Error("Network Response was not ok " + response.statusText);
                        }
                        return response.text();
})
                .then(data => {
                        console.log("PHP Response:", data);
                        const preElement = document.createElement("pre");
                        preElement.textContent = data;
                        document.body.appendChild(preElement).innerHTML = data;
                })
                .catch(error => console.error("Error loading PHP file:", error));
};
</script>

</div>

CORS Issue between JS program and Python

I kept encountering CORS issue on my RedwoodJS program with my customize authentication connecting to Supabase. I noticed that when I pass a request with a header, CORS issue is seen: Access to fetch at 'http://localhost:8087/profile' from origin 'http://localhost:8910' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

But when I removed the headers on my request. It works properly. Example of this is my /login API endpoint.

This is my built in authentication with RedwoodJS:

import { useState, useEffect, useCallback } from 'react'
import { config } from 'src/config'
import { logger } from 'src/loggers/logger'

export const useAuth = () => {
  const [user, setUser] = useState(null)
  const [token, setToken] = useState(localStorage.getItem('token'))
  const [loading, setLoading] = useState(true)

  const logIn = async (email: string, password: string) => {
    try {
      const url = config.REDWOOD_ENV_API_ENDPOINT + '/login'
      logger.info(`Login URL is: ${url}`)
      logger.info(
        `JSON body: ${JSON.stringify({ email: email, password: password })}`
      )

      const response = await fetch(url, {
        method: 'POST',
        body: JSON.stringify({ email: email, password: password }),
      })
      console.log(response)
      const result = await response.json()
      if (result.error) {
        throw new Error(result.error)
      }
      const accessToken = result.access_token
      localStorage.setItem('token', accessToken)
      setToken(accessToken)
      setUser(result.user)
      return result
    } catch (error: any) {
      console.error('Login error:', error)
      throw error
    }
  }

  const logOut = useCallback(() => {
    localStorage.removeItem('token')
    setToken(null)
    setUser(null)
  }, [])

  const getProfile = useCallback(async () => {
    const url = config.REDWOOD_ENV_API_ENDPOINT + '/profile'
    if (!token) {
      setLoading(false)
      return
    }

    logger.info(`Fetching profile from ${url}`)

    try {
      const response = await fetch(url, {
        method: 'GET',
        headers: {
          Authorization: `bearer ${token}`,
          'Content-Type': 'application/json',
        },
        mode: 'cors',
      })

      logger.info(`Profile response status: ${response.status}`)

      if (!response.ok) {
        const errorText = await response.text()
        logger.error(`Profile error: ${response.status} - ${errorText}`)
        throw new Error(`HTTP error ${response.status}: ${errorText}`)
      }

      const result = await response.json()
      if (result.error) {
        logger.error(`Profile result error: ${result.error}`)
        logOut()
      } else {
        setUser(result.data.user)
      }
    } catch (error) {
      console.error('Profile fetch error:', error)
      logOut()
    }
    setLoading(false)
  }, [token, logOut])

  useEffect(() => {
    getProfile()
  }, [token, getProfile])

  return { user, token, loading, logIn, logOut }
}

Then this is my Python back-end:

def handle_cors_response(request: Request) -> Response:
    origin = request.headers.get("origin")
    allowed_origins = settings.REDWOOD_URL if isinstance(settings.REDWOOD_URL, list) else [settings.REDWOOD_URL]

    if origin in allowed_origins:
        return Response(
            status_code=204,
            headers={
                "Access-Control-Allow-Origin": origin,
                "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
                "Access-Control-Allow-Headers": "Content-Type, Authorization",
                "Access-Control-Allow-Credentials": "true",
            },
        )
    return Response(
        status_code=403,
        headers={"Content-Type": "application/json"},
        description=json.dumps({"error": "Origin not allowed"}),
    )

def get_response_headers(request: Request, content_type: str = "application/json") -> dict:
    origin = request.headers.get("origin")

    return {
        "Content-Type": content_type,
        "Access-Control-Allow-Origin": origin,
        "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
        "Access-Control-Allow-Headers": "content-Type, authorization",
        "Access-Control-Allow-Credentials": "true",
    }

@app.options("/{path:path}")
async def handle_cors(request: Request):
    return Response(
        status_code=204,
        headers=get_response_headers(request),
    )

@app.get("/profile")
async def profile(request):
    auth_header = request.headers.get("Authorization") or request.headers.get("authorization")

    if not auth_header or not auth_header.startswith("bearer "):
        return Response(
            status_code=401,
            headers=get_response_headers(request),
            description=json.dumps({"error": "Unauthorized"})
        )

    token = auth_header.split("bearer ", 1)[1] if "bearer " in auth_header.lower() else auth_header.split("Bearer ", 1)[1]
    try:
        decoded_token = jwt.decode(token, settings.JWT_SECRET, algorithms=["HS256"], audience="authenticated")

        logger.info(f'Decoded token: {decoded_token}')
        return Response(
            status_code=200,
            headers=get_response_headers(request),
            description=json.dumps({"data": {"user": decoded_token}})
        )
    except jwt.ExpiredSignatureError:
        return Response(
            status_code=401,
            headers=get_response_headers(request),
            description=json.dumps({"error": "Token expired"})
        )
    except jwt.InvalidTokenError:
        return Response(
            status_code=401,
            headers=get_response_headers(request),
            description=json.dumps({"error": "Invalid token"})
        )

I’ve tried doing the following:

  • Changed the get_response_headers to use * for debugging purposes. But error still persist:
def get_response_headers(request: Request, content_type: str = "application/json") -> dict:
    origin = request.headers.get("origin")
    return {
        "Content-Type": content_type,
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "*",
        "Access-Control-Allow-Headers": "*",
        "Access-Control-Allow-Credentials": "true",
    }
  • Used bruno to make a request with headers for Authorization as intended. Monitored to be working but CORS issue is shown on RedwoodJS app.

Size of html and performance of javascript

Is it true the size of HTML DOM affect the javascript **performance ** or no ?

For example : i have 200 lines of html and the javascript need the access to two lines of DOM only, is it the same with 10 lines of html and the same javascript ?

Code 1 (200 lines) :

<body>
...........200 lines
<script>
//javascript of code 1 = javascript of code 2
</script>
</body>

Code 2 (10 lines) :

<body>
..........10 lines
<script>
//javascript of code 1 = javascript of code 2
</script>
</body>

React-Virtuoso. The problem with using it together with framer-motion

I have implemented a virtual list using the react-virtuoso library. This list is rendered inside a modal window, and the modal window itself has an animated appearance and disappearance, which I implemented using framer-motion.

And so, when I open the modal window, all the data in the virtual list loads perfectly and everything works as it should. But as soon as I close the modal window, I get an error in the console.:

hook.js:608 react-virtuoso: Zero-sized element, this should not happen

My modal

import { FC } from 'react'
import ReactDOM from 'react-dom'
import clsx from 'clsx'
import { AnimatePresence, motion, Variants } from 'framer-motion'
import { useAppDispatch, useAppSelector } from '../../store/hooks'
import { closeModal } from '../../store/slices/UISlice'

import styles from './Modal.module.sass'

interface ModalProps extends React.PropsWithChildren {
    className?: string
}

const overlayVariants: Variants = {
    initial: {
        opacity: 0,
    },
    animate: {
        opacity: 1,
    },
}

const wrapperVariants: Variants = {
    initial: {
        opacity: 0,
        scale: 0,
        fill: 'blur(5px)',
        transition: {
            duration: 0.5,
            ease: [1, 0, 0, 1],
        },
    },
    animate: {
        opacity: 1,
        scale: 1,
        filter: 'blur(0px)',
        transition: {
            duration: 0.5,
            ease: [1, 0, 0, 1],
        },
    },
}

export const Modal: FC<ModalProps> = ({ className, children }) => {
    const { isModalOpen } = useAppSelector((state) => state.UISlice)
    const dispatch = useAppDispatch()

    return ReactDOM.createPortal(
        <AnimatePresence>
            {isModalOpen && (
                <motion.div
                    variants={overlayVariants}
                    initial='initial'
                    animate='animate'
                    exit='initial'
                    className={clsx(className, styles.overlay)}
                    onClick={() => dispatch(closeModal())}
                >
                    <motion.div
                        variants={wrapperVariants}
                        initial='initial'
                        animate='animate'
                        exit='initial'
                        className={styles.wrapper}
                        onClick={(e) => e.stopPropagation()}
                    >
                        {children}
                    </motion.div>
                </motion.div>
            )}
        </AnimatePresence>,
        document.body
    )
}

Component with react-virtuoso

import { FC } from 'react'
import { motion, Variants } from 'framer-motion'

import styles from './CryptoList.module.sass'
import clsx from 'clsx'
import { Virtuoso } from 'react-virtuoso'
import { IToken } from '../../models/tokenTypes'
import { IOptions } from '../CryptoAddPopup/CryptoAddPopup'

interface CryptoListProps {
    className?: string
    filteredCrypto: IToken[]
    setOptions: React.Dispatch<React.SetStateAction<IOptions>>
    setTokenCount: React.Dispatch<React.SetStateAction<number>>
    setIsOptionsVisible: React.Dispatch<React.SetStateAction<boolean>>
}

const listVariants: Variants = {
    initial: {
        opacity: 0,
    },
    animate: {
        opacity: 1,
    },
}

export const CryptoList: FC<CryptoListProps> = ({
    className,
    filteredCrypto,
    setOptions,
    setTokenCount,
    setIsOptionsVisible,
}) => {
    return (
        <motion.ul
            variants={listVariants}
            initial='initial'
            animate='animate'
            className={clsx(className, styles.list)}
        >
            <Virtuoso
                style={{ height: 400 }}
                data={filteredCrypto}
                totalCount={filteredCrypto.length}
                className={styles.virtual}
                itemContent={(_, token) => (
                    <li
                        className={styles.item}
                        key={token.symbol}
                        onClick={() => {
                            setOptions(() => ({
                                token: token.token,
                                tokenPrice: token.price,
                                priceChangePercent: token.priceChangePercent,
                                symbol: token.symbol,
                                totalPrice: 0,
                            }))
                            setTokenCount(1)
                            setIsOptionsVisible(true)
                        }}
                    >
                        <div className={styles.toke}>{token.token}</div>
                        <div className={styles.price}>{token.price.toFixed(2)}$</div>
                        <div
                            className={clsx(
                                styles.percent,
                                token.priceChangePercent < 0 && styles.red
                            )}
                        >
                            {token.priceChangePercent > 0 && '+'}
                            {token.priceChangePercent.toFixed(2)}%
                        </div>
                    </li>
                )}
            />
        </motion.ul>
    )
}

If I rewrite the modal window like this, I will get rid of this error, but I will also lose the smooth closing of the modal coz I delete AnimatePresence tag. I don’t want to lose animation at all =(

import { FC } from 'react'
import ReactDOM from 'react-dom'
import clsx from 'clsx'
import { AnimatePresence, motion, Variants } from 'framer-motion'
import { useAppDispatch, useAppSelector } from '../../store/hooks'
import { closeModal } from '../../store/slices/UISlice'

import styles from './Modal.module.sass'

interface ModalProps extends React.PropsWithChildren {
    className?: string
}

const overlayVariants: Variants = {
    initial: {
        opacity: 0,
    },
    animate: {
        opacity: 1,
    },
}

const wrapperVariants: Variants = {
    initial: {
        opacity: 0,
        scale: 0,
        fill: 'blur(5px)',
        transition: {
            duration: 0.5,
            ease: [1, 0, 0, 1],
        },
    },
    animate: {
        opacity: 1,
        scale: 1,
        filter: 'blur(0px)',
        transition: {
            duration: 0.5,
            ease: [1, 0, 0, 1],
        },
    },
}

export const Modal: FC<ModalProps> = ({ className, children }) => {
    const { isModalOpen } = useAppSelector((state) => state.UISlice)
    const dispatch = useAppDispatch()

    if (!isModalOpen) return null

    return ReactDOM.createPortal(
        <motion.div
            variants={overlayVariants}
            initial='initial'
            animate='animate'
            exit='initial'
            className={clsx(className, styles.overlay)}
            onClick={() => dispatch(closeModal())}
        >
            <motion.div
                variants={wrapperVariants}
                initial='initial'
                animate='animate'
                exit='initial'
                className={styles.wrapper}
                onClick={(e) => e.stopPropagation()}
            >
                {children}
            </motion.div>
        </motion.div>,
        document.body
    )
}

Maybe someone has already encountered such a problem, please help me solve this problem without losing animation from AnimatePresence

Github repo of this project https://github.com/sparkkkd/crypto-portfolio

I am getting Error: Page closed in playwright

async function sortHackerNewsArticles() {
// launch browser
  const browser = await chromium.launch({ headless: false });
  const context = await browser.newContext();
  const page = await context.newPage()
  await page.goto("*URL*");
`

I am trying to navigate to a URL and I am getting Error: Page closed. I used a try catch , timeout but it didn’t work.

How come the letter `i` breaks my regex for `preg_replace_callback` but works with javascript [duplicate]

I don’t understand why this particular regex with PHP is failing:

<?php
echo preg_replace_callback(
        '/<a(\s[^>]+)* href="([^"]+)"(\s[^>]+)*>/i',
        function ($matches) {
                print_r($matches);
          return $matches[0];
        },
        '<a href="http://s.c/" title=" tickets for htt://v.c/e r ad Clown album elease Martian Crisis Unit Elrichan Wavelengt" tar="">a</a>'
);

When I run the code above, it echoes nothing to terminal.

But if I delete a single character from somewhere, sometimes the echo will work. For example, if change the word Martian to Martan (ie. drop the letter i), then everything works fine.

What have I misunderstood about my regex pattern? What is so special about the letter i that would break my regex?


However, this works fine in javascript:

'<a href="http://s.c/" title=" tickets for htt://v.c/e r ad Clown album elease Martian Crisis Unit Elrichan Wavelengt" tar="">a</a>'.match(/<a(s[^>]+)* href="([^"]+)"(s[^>]+)*>/i);

The javascript version gives me an array of matches.

How do you validate copy/pasted images on the FE? In regards to security and CSP’s

When using a rich text editor, users are able to copy and paste images from the web. This will then get sent to the backend to be stored as markdown text: <p>some input<img src='some_pasted_source'/></p>

Is there any way to validate that the url source is a valid image? My CSP (content security policy) contains img-src https: http: data:. Im considering adding a check in my front end to confirm the URL is a valid URL. But outside of that, i’m not clear on how I can validate that it’s not malicious. Is this CSP too lax, and open to malicious attacks? Im considering saving the images in an S3 bucket so i can restrict the CSP even more, by only allowing my s3 endpoint for images and removing the http: https: from my CSP, but im not sure if that would help or be over engineered.

Javascript Datatables: Mark multiple rows and press “Enter” flips state of checkbox instead of checking/unchecking all

As the title suggests, i need help with my Javascript datatable. I have some existing logic to handle a “enter” press, when multiple rows is marked. When i press enter, i want this logic:

If all rows are unchecked: Check all rows
If all rows are checked: Uncheck all rows
If atleast one row is checked: Check all rows

JS is not my strongest side, and i tried many different AI models to get help, but all where unsuccessful. So please head my call and help me resolve this.

Here is my code that reacts on enter keypress today:

    ///enter
    if (charCode == 13) {

        var inputTryOne = $(e.target).find("input[type='checkbox']");
        var inputTryTwo = $(e.currentTarget).find("input[type='checkbox']");
        var input = inputTryOne[0] ?? inputTryTwo[0];
        if (input != undefined) {

            input = $(input);
            var td = input.closest("td");
            td.closest("tr").toggleClass("selected");
            input.prop("checked", !input.prop("checked"));
            input.trigger("change");

            this.parent.lykoGridKeyboardNavigation.moveDown(false, false);

            return;
        }

        this.commitEditedCells();
        this.parent.lykoGridKeyboardNavigation.moveDown(false, false); 

        return;
    }

Recomendación de API para consultar facturas en SUNAT o cómo desarrollar una API similar

Estoy buscando una API que me permita consultar los datos de una factura registrada en la SUNAT (Perú). Actualmente, he encontrado la API de JSON-PE que permite realizar consultas como la siguiente:

Consulta FACTURA  
{  
  "GET": "https://api.json-pe.com/sunat/factura/20551445322-E001-0000103?apikey=api_key",  
  "credit": 25,  
  "subscription": "basic"  
}  

Ruc Emisor Número  
20551445322  

Código Serie  
E001  

Número Cpe  
0000103  

Enviar  

Datos del Documento:  
{  
  "GET": "https://api.json-pe.com/sunat/factura/20551445322-E001-0000103?apikey=api_key",  
  "credit": 25,  
  "subscription": "basic",  
  "statusCode": 200,  
  "body": {  
    "numeroRUC": "20551445322",  
    "razonSocial": "SG INDUSTRIAL S.A.C.",  
    "razonComercial": "SG SAFETY",  
    "tipoComprobante": "01",  
    "serieComprobante": "E001",  
    "numeroComprobante": "0000103",  
    "tipoDocComprobante": "01",  
    "nombreDistrito": "AFE",  
    "nombreProvincia": "LIMA",  
    "nombreDepartamento": "LIMA",  
    "codigoMoneda": "PEN",  
    "simboloMoneda": "S/",  
    "fechaEmisión": "22/12/2017",  
    "fechaVencimiento": "",  
    "tipoDocumentoCliente": "06",  
    "descripcionDocumentoCliente": "RUC",  
    "numeroDocumentoCliente": "28523195442",  
    "nombreCliente": "EDPC EXPERTS S.A.C.",  
    "observacion": "CONDICION DE PAGO: CONTADO DEP.CTA.CTE."  
  "glosa": "-",  
  "montoTotalGeneral": 165.2,  
  "montoTotalTexto": "SON: CIEN SESENTA Y CINCO Y 20/100 SOLES",  
  "totalDescuentos": 0,  
  "totalAnticipos": 0,  
  "totalISC": 0,  
  "totalIGV": 0,  
  "totalOtrosCargos": 0,  
  "totalValorVenta": 0,  
  "totalValorVentaExonerado": 0,  
  "totalIvaInfoner": 0,  
  "totalGravOner": 0,  
  "totalValorVentaOperaGratuitas": 0,  
  "indicadorEstado": "0",  
  "documentoRelacionadoBean": [  
    {  
      "tipoDocumentoRelacionado": "09",  
      "desTipoDocuReLa": "GUIA DE REMISION REMITENTE",  
      "numeroDocumentoRelacionadoInicial": "EG01 - 20"  
    }  
  ],  
  "detalleComprobanteBean": [  
    {  
      "identificador": 1,  
      "descripcion": "LENTES NITRO STEELPRO LUNA OSCURA ANTIEMPA?ANTE",  
      "cantidad": "50.00",  
      "precioUnitario": "0.0000",  
      "codigoItem": "-",  
      "codigoUnidadMedia": "NIU",  
      "unidadMedidaDesc": "UNIDAD",  
      "valorVtaUnitario": "0"  
    }  
  ]  
}  

Y devuelve una respuesta en formato JSON con información detallada de la factura, incluyendo RUC del emisor, razón social, monto total, detalle de productos, etc.

Mis preguntas son:
¿Existe alguna API oficial o alternativa confiable que proporcione esta información sin necesidad de scraping?
Si no hay una API gratuita o pública disponible, ¿cómo podría desarrollar una API propia para obtener estos datos directamente desde SUNAT? ¿Existe alguna forma de acceder a esta información mediante un servicio web oficial o base de datos pública?
Cualquier recomendación de servicios, documentación o experiencia en la implementación de una solución similar sería de gran ayuda.

How to use javascript in the JSP file

I am encountering an issue with my Java web project using Maven. I want to use AJAX to fetch data without reloading the page, but I am having trouble displaying the response in HTML. Here is how I implemented it:

Java Servlet

package com.mycompany.stock_managment.controller;

import java.io.IOException;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;
import com.mycompany.stock_managment.model.Vente;
import com.mycompany.stock_managment.service.ProductService;

@WebServlet("/searchSellHistory")
public class SearchSellHistoryServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String query = request.getParameter("query");
        ProductService productService = new ProductService();
        // Get the results based on the search query
        List<Vente> results = productService.searchVentes(query);

        // Convert to JSON with Gson
        Gson gson = new Gson();
        String json = gson.toJson(results);

        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(json);
    }
}

JSP / JS / HTML

$(document).ready(function() {
    $("#searchInput").on("keyup", function() {
        let query = $(this).val();
        
        $.ajax({
            url: "/stock_managment/searchSellHistory",
            type: "GET",
            data: { query: query },
            dataType: "json",
            success: function(response) {
                let tableBody = $("#sellHistoryTable tbody");
                tableBody.empty(); // Vider le tableau avant d'ajouter les nouveaux résultats
            if (typeof response === "string") {
            response = JSON.parse(response);
            }
        console.log("Données reçues après conversion :", response);
                response.forEach(function(vente) {
                    let debtClass = vente.reste_a_payer > 0 ? "text-red-500" : "text-green-500";
                    console.log(vente);
                    let row = `<tr class="hover:bg-slate-50">
                <td class="p-4 border-b border-slate-200 py-5">
                    <p class="block font-semibold text-sm text-slate-800">${vente.product_name || 'N/A'}</p>
                </td>
                <td class="p-4 border-b border-slate-200 py-5">
                    <p class="text-sm text-slate-500">${vente.quantite || '0'}</p>
                </td>
                <td class="p-4 border-b border-slate-200 py-5">
                    <p class="text-sm text-slate-500">${vente.prix || '0'}</p>
                </td>
                <td class="p-4 border-b border-slate-200 py-5">
                    <p class="text-sm text-slate-500">${vente.montant_total || '0'}</p>
                </td>
                <td class="p-4 border-b border-slate-200 py-5">
                    <p class="text-sm text-slate-500">${vente.paiement || '0'}</p>
                </td>
                <td class="p-4 border-b border-slate-200 py-5 text-red">
                    <p class="text-sm ${vente.reste_a_payer > 0 ? 'text-red-500' : 'text-green-500'}">
                        ${vente.reste_a_payer || '0'}
                    </p>
                </td>
            </tr>`;
                    console.log(row);
                    tableBody.append(row);

                });
            }
        });
    });
});





</script>

Result:

console.log data are good in json but if I try to map that tableBody.append(row); every row got false.