Google provider no response after user login

I have a simple snippet involving Firebase Authentication with two options: email/pass and google.
My problem is that users can use the option email/pass smoothly.
Regarding Google provider, it can redirect to Google’s Oauth2 page, where users select and log in with their Google account. It also redirects back to my original page. However, it seems that the successful callback and error callback have never fired. (Note: with user/pass those callbacks fire as expected)

Here is what I tried

  1. Whitelist domain -> done
  2. Enable Google Provider in Firebase’s console -> done

Are there any tips to debug this issue?

window.addEventListener('load', function () {
    document.getElementById('sign-out').onclick = function () {
      firebase.auth().signOut();
    };
  
    // FirebaseUI config.
    var uiConfig = {
      signInSuccessUrl: '/',
      signInOptions: [
        firebase.auth.GoogleAuthProvider.PROVIDER_ID,
        firebase.auth.EmailAuthProvider.PROVIDER_ID
      ],
    };
  
    firebase.auth().onAuthStateChanged(function (user) {
      if (user) {
        // if user is signed in, handle business logic here
      } else {
        // User is signed out.
        // Initialize the FirebaseUI Widget using Firebase.
        var ui = new firebaseui.auth.AuthUI(firebase.auth());
        // Show the Firebase login button.
        ui.start('#firebaseui-auth-container', uiConfig);
      }
    }, function (error) {
      console.error("Authentication state change error:", error);
    });
  });

Copy and paste in the draggable object

I have html script like this,

<div style={{width:"200px",height:"100px"}} draggable={true}>
<div style={{width:"100px",height:"50px"}}> copy test</span>
</div>

This div is draggable, then I want to copy the text,

However I tried to copy the text, the div is dragged.

Is there any good method for this?

Change hover color of an input type=”range”

I have a react component named DocumentView that has a range slider to show the zoom level of a document. I tried to change the color of the input range on hovering with Tailwind CSS, but it shows the browser’s default color.

const DocumentView = ({ data }) => {
  const [scale, setScale] = useState(1);

  return (
    <div className="flex items-center w-32">
      <input
        type="range"
        min="0.3"
        max="1.5"
        step="0.1"
        value={scale}
        onChange={zoomLevel}
        aria-label="Zoom Level"
        className="w-full h-2 accent-[#7357fd] bg-transparent rounded-lg hover:accent-[#694cfdce] cursor-pointer"
        />
    </div>
  );
};

I need help figuring out how to display the color of the input range when hovering over the filled portion. I tried to find a solution with the help of AI, but I couldn’t get a proper answer because the custom CSS solutions provided did not work at all.

Why is my CO2 threshold check failing when the value is slightly over the limit (floating-point issue)

I’m building a CO2 savings tracker for a hospitality site, where users save CO2 and can plant a tree when their total savings reach 100kg. However, I’m encountering an issue where, when the CO2 value is just over 100kg, the “Plant a Tree” button does not get enabled.

I suspect this is a floating-point precision problem, as the CO2 value seems to be slightly less than 100 when displayed, even though it’s technically above the threshold. Below is the code for the tracker:

class GreenHospitality {
    constructor() {
        this.co2Saved = 0;
        this.treeThreshold = 100.00; // Threshold to plant a tree
    }

    updateCO2() {
        setTimeout(() => {
            const newCO2 = Math.random() * 5 + 0.01; // Small random CO2 increase
            this.co2Saved += newCO2;
            console.log(this.co2Saved.toFixed(2));

            if (this.co2Saved >= this.treeThreshold) {
                console.log("You can plant a tree!");
            }
        }, 3000);
    }
}

This is for enabling the button:

<!-- Plant Tree Button -->
<button id="plant-tree-btn" disabled>Plant a Tree</button>

<!-- Upgrade Tree Button -->
<button id="upgrade-tree-btn" disabled>Upgrade Tree</button>

if (this.co2Saved >= this.treeThreshold) {
    this.treeStatus.textContent = "You can now plant a tree!";
    this.plantTreeBtn.disabled = false;
} else {
    this.treeStatus.textContent = "You need more CO₂ to plant a tree!";
    this.plantTreeBtn.disabled = true;
}

Button Event Listeners:

document.getElementById("plant-tree-btn").addEventListener("click", () => game.plantTree());
document.getElementById("upgrade-tree-btn").addEventListener("click", () => game.upgradeTree());

Even though the CO₂ savings show as slightly above 100kg (like 100.001), the button to plant a tree does not get enabled. It seems like there’s a tiny difference in value due to floating-point precision errors.

I’ve tried rounding the value and using .toFixed(2) for display, but it still doesn’t enable the button at the correct moment.

Has anyone encountered this issue? Any suggestions on how to fix the threshold check in a way that accounts for small floating-point precision errors?

What I’ve Tried:

  • Using .toFixed(2) for display

  • Rounding the number manually before the comparison

  • Using Math.floor() and Math.ceil() to adjust the value

Encountering this issue “ERR_INVALID_FILE_URL_PATH: File URL path must be absolute” while running test suite

I am trying to set up Webpack locally and try to run the test,

yarn test

but it failed I don’t know how to fix it. I am trying to set up Webpack locally and run its test suite, but I am encountering an error related to “ERR_INVALID_FILE_URL_PATH: File URL path must be absolute” during schema precompilation.

yarn run v1.22.22
$ yarn lint
$ yarn setup
$ node ./setup/setup.js
$ yarn code-lint && yarn special-lint && yarn type-lint && yarn typings-test && yarn module-typings-test && yarn yarn-lint && yarn pretty-lint && yarn spellcheck
$ node node_modules/eslint/bin/eslint.js --cache .
(node:8860) ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
$ node node_modules/tooling/lockfile-lint && node node_modules/tooling/schemas-lint && node node_modules/tooling/inherit-types && node node_modules/tooling/format-schemas && node tooling/generate-runtime-code.js && node tooling/generate-wasm-code.js && node node_modules/tooling/format-file-header && node node_modules/tooling/compile-to-definitions && node node_modules/tooling/precompile-schemas && node node_modules/tooling/generate-types --no-template-literals
node:internal/url:1433
    throw new ERR_INVALID_FILE_URL_PATH('must be absolute');
          ^

TypeError [ERR_INVALID_FILE_URL_PATH]: File URL path must be absolute
during precompilation of D:/GSOC/webpack/schemas/plugins/asset/AssetGeneratorOptions.json
    at getPathFromURLWin32 (node:internal/url:1433:11)
    at fileURLToPath (node:internal/url:1464:35)
    at loadSchema (D:GSOCwebpacknode_modulestoolingprecompile-schemasindex.js:21:16)
    at Ajv._loadSchema (D:GSOCwebpacknode_modulestoolingnode_modulesajvdistcore.js:207:52)
    at Ajv.loadMissingSchema (D:GSOCwebpacknode_modulestoolingnode_modulesajvdistcore.js:196:47)
    at Ajv._compileAsync (D:GSOCwebpacknode_modulestoolingnode_modulesajvdistcore.js:186:41)
    at Ajv.runCompileAsync (D:GSOCwebpacknode_modulestoolingnode_modulesajvdistcore.js:171:50)
    at async precompileSchema (D:GSOCwebpacknode_modulestoolingprecompile-schemasindex.js:211:20) {
  code: 'ERR_INVALID_FILE_URL_PATH'
}

Node.js v20.15.0
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

Can someone help me set up Webpack locally or try to fix this problem?

I am trying to set up the webpack locally.

Saving a reservation button [closed]

I have a problem with saving the data of the person who clicked and the function counting down the reservation time after clicking the button. I am completely stuck, does anyone of you know how I can solve this and where the problem lies, after pressing the button changes to reserved with a countdown time, but after refreshing the page no data is saved. so that if any user enters, they can also click this button, and it should work so that when one person clicks, save who clicked, counts down the reservation time 48h back, thus blocking the possibility of pressing the button for other users and they only see how much time is reserved and after that time the button should return to the possibility of re-booking.

Please help.

AJAX:

function handle_reservation() {
    $user_id = get_current_user_id();
    $post_id = intval($_POST['post_id']);

    if (!$user_id) {
        wp_send_json_error(['message' => 'Musisz być zalogowany, aby rezerwować.']);
    }

    $current_reservation = get_post_meta($post_id, '_reserved_by', true);
    $reserved_until = get_post_meta($post_id, '_reserved_until', true);

    if ($current_reservation && time() < $reserved_until) {
        wp_send_json_error([
            'message' => 'Post już zarezerwowany do ' . date('Y-m-d H:i:s', $reserved_until),
            'reserved_by' => get_userdata($current_reservation)->user_login
        ]);
    }

    update_post_meta($post_id, '_reserved_by', $user_id);
    update_post_meta($post_id, '_reserved_until', time() + 48 * 60 * 60);

    wp_send_json_success([
        'message' => 'Post zarezerwowany.',
        'reserved_by' => get_userdata($user_id)->user_login
    ]);
}
add_action('wp_ajax_reserve_post', 'handle_reservation');

function check_reservation_status() {
    $post_id = intval($_POST['post_id']);
    $reservation = get_post_meta($post_id, '_reserved_by', true);
    $reserved_until = get_post_meta($post_id, '_reserved_until', true);

    if ($reservation && time() < $reserved_until) {
        wp_send_json_success([
            'reserved' => true,
            'reserved_by' => get_userdata($reservation)->user_login,
            'time_left' => $reserved_until - time()
        ]);
    } else {
        wp_send_json_success(['reserved' => false]);
    }
}
add_action('wp_ajax_check_reservation_status', 'check_reservation_status');

function enqueue_reserve_post_script() {
    wp_enqueue_script('reserve-post-js', get_template_directory_uri() . '/js/reserve-post.js', ['jquery'], null, true);
    
    wp_localize_script('reserve-post-js', 'wp_vars', [
        'ajax_url' => admin_url('admin-ajax.php'),
        'user_id' => get_current_user_id()
    ]);
}
add_action('wp_enqueue_scripts', 'enqueue_reserve_post_script');

JS:

document.addEventListener('DOMContentLoaded', function() {
    const buttons = document.querySelectorAll('.reserve-button');

    buttons.forEach(function(button) {
        button.style.backgroundColor = "green";
        button.style.color = "white";
        button.style.border = "none";
        button.style.padding = "10px 20px";
        button.style.fontSize = "16px";
        button.textContent = "Zarezerwuj";

        const postId = button.getAttribute('data-post-id');
        const userId = wp_vars.user_id;
        const reservationInfo = button.nextElementSibling;
        const reservedByElement = reservationInfo.querySelector('.reserved-by');

        checkReservation(postId, button, reservationInfo, reservedByElement);

        button.addEventListener('click', function() {
            reservePost(postId, userId, button, reservationInfo, reservedByElement);
        });

        button.addEventListener('mouseenter', function() {
            if (!button.disabled) {
                button.style.backgroundColor = "white";
                button.style.color = "orange";
                button.style.border = "2px solid orange";
                button.textContent = "ZAREZERWUJ";
            }
        });

        button.addEventListener('mouseleave', function() {
            if (!button.disabled) {
                button.style.backgroundColor = "green";
                button.style.color = "white";
                button.style.border = "none";
                button.textContent = "Zarezerwuj";
            }
        });
    });
});

function checkReservation(postId, button, reservationInfo, reservedByElement) {
    jQuery.ajax({
        url: wp_vars.ajax_url,
        type: 'POST',
        data: {
            action: 'check_reservation_status',
            post_id: postId
        },
        success: function(response) {
            if (response.success && response.data.reserved) {
                const { time_left, reserved_by } = response.data;
                button.textContent = `Zarezerwowane na ${formatTime(time_left)}`;
                button.style.backgroundColor = "orange";
                button.style.color = "white";
                button.disabled = true;
                startCountdown(button, time_left);

                reservedByElement.textContent = reserved_by;
                reservationInfo.style.display = "block";
            }
        },
        error: function() {
            console.error('Błąd podczas sprawdzania rezerwacji.');
        }
    });
}

function reservePost(postId, userId, button, reservationInfo, reservedByElement) {
    jQuery.ajax({
        url: wp_vars.ajax_url,
        type: 'POST',
        data: {
            action: 'reserve_post',
            post_id: postId,
            user_id: userId
        },
        success: function(response) {
            if (response.success) {
                const time_left = 48 * 60 * 60;
                button.textContent = `Zarezerwowane na ${formatTime(time_left)}`;
                button.style.backgroundColor = "orange";
                button.style.color = "white";
                button.disabled = true;
                startCountdown(button, time_left);

                reservedByElement.textContent = response.data.reserved_by;
                reservationInfo.style.display = "block";
            } else {
                alert('Nie udało się zarezerwować: ' + response.data.message);
            }
        },
        error: function() {
            alert('Błąd podczas rezerwacji.');
        }
    });
}

function formatTime(seconds) {
    const hours = Math.floor(seconds / 3600);
    const minutes = Math.floor((seconds % 3600) / 60);
    return `${hours}h ${pad(minutes)}m`;
}

function pad(num) {
    return num < 10 ? '0' + num : num;
}

function startCountdown(button, remainingTime) {
    const countdownInterval = setInterval(() => {
        remainingTime -= 1;
        if (remainingTime <= 0) {
            clearInterval(countdownInterval);
            button.textContent = "Zarezerwuj ponownie";
            button.style.backgroundColor = "green";
            button.style.color = "white";
            button.disabled = false;
        } else {
            button.textContent = `Zarezerwowane na ${formatTime(remainingTime)}`;
        }
    }, 1000);
}

Potential CSP issue with Safari only devices and Google Tag Manager

I am running into an issue with a eCommerce website that appears to work fine on desktop and Android browsers, however when loading the site on a Apple based device with Safari it is breaking some other javascript functionality that the page uses (e.g such as jQuery performing a page-reload on changing an option from a ‘Sort By’ dropdown menu.

I don’t have an actual iOS phone to test but have put the website in question into an iOS simulator running an ‘iPhone 16’ with Safari and enabled debugging so I can view the console errors directly on my Mac Mini. Note – these issues ONLY happen on iOS devices with Safari and not Android (also no issues with regular desktop browsers).

When loading a particular category page I am getting the following error within the simulator console (note I have changed the site url for security reasons)

Refused to load https://www.googletagmanager.com/static/service_worker/53b0/sw_iframe.html?origin=https%3A%2F%2Fwww.mysite.com  because it does not appear in the frame-src directive of the Content Security Policy.
Refused to load https://metrics.mysite.com/_/service_worker/53b0/sw_iframe.html?origin=https%3A%2F%2Fwww.mysite.com&1p=1 because it does not appear in the frame-src directive of the Content Security Policy.

Would this require me to alter some Content Security Policy (CSP) settings to take this into account properly? I am not too familiar with CSP so any advice is welcomed, but I am reading that the Safari browser ITP (Intelligent Tracking Prevention) is much more aggressive than Android browsers and this can affect how service workers and tracking-related iframes are loaded which maybe effecting the Google Tag Manager in this instance.

So an example of the flow would be :

  • User goes to xxx page and selects ‘Sort by lowest price’ from the dropdown
  • This performs a page reload with the lowest price product
  • On safari devices this breaks so using the dropdown will not actually perform a page reload

I am debugging this using an iOS simulator on my mac, with the following versions if this helps:
Running MacOS 15.3.2
Xcode 16.2
Simulator 16.0 (1038)
Safari browser

Can we use WeakMap to improve garbage collection?

Say I have a class that does a lot of fetch requests.
State of each request is stored in a property of that class, which now is an array of objects.

Objects in that array are not needed once the fetch req completes, so they are removed at that point. This is done by searching the array by some object property, then get the index, splice.

Then I saw WeakMap

At first glance, it looks like that array of objects can be replaced with a WeakMap. Then I would simply call map.delete(object) when fetch req completes, instead of searching an array.

However, I’m a bit confused about the values of WeakMaps. I my case, I don’t need a value. I just need the object as a key. So I don’t really understand the purpose of the value, or maybe WeakMap is the wrong tool for the job?

Change background color when Overflow occurs

I have a page that contains a static/max- height div. My page’s second section’s (id =”middle”) div contains a textarea fields for users to provide a response and the entire div is limited to a height of 3 inches (due to this form being printed on 8.5″x11″ paper). I’d like either the entire page’s or this specific div’s background color to change if what has been entered in to the textarea makes the content of this div overflow.

Is there a way, either via JS or CSS, that I can change the background-color of this div to visually indicate when the contents have overflowed?

Considering I cannot provide my exact code, here is an example that will hopefully assist anyone reviewing my inquiry:

CSS

<style>
    div {
        border: 1px solid #CCC;
        background-color: #FFF;
        margin-bottom: 1rem;
        padding: 1rem;
        width: 8.5in;
        max-height: 7in;
    }

    .static-section {
        display: grid;
        overflow: hidden;
        max-height: 3in;
        width: 7.5in;

        * {
            margin-top: .5rem;
            margin-bottom: .5rem;
            padding: .5rem;
        }
    }

    textarea {
        field-sizing: content;
    }
</style>

HTML:

<form id="entry">
    <section id="top">
        <h1>HEADER</h1>
    </section>

    <section id="middle">
        <div id="overflow" class="static-section">
            <label>This section is locked at a max 3 inches. I want the background color to be red if the anything makes this section overflow.</label>
            <textarea placeholder="type in this section until the following paragraph starts to go off the screen. this is when I want the background color to change."></textarea>
            <P>When this information starts to go off the screen (i.e., enabling overflow - which the scrollbars are being hidden by the static-section class), I want the background color of this div to change. This will provide a visual indication that the provided response is too long.</P>
        </div>
    </section>

    <section id="footer">
        <h1>FOOTER</h1>
    </section>
</form>

If possible, I’d really like the entire form (id=”entry”) have it’s background color be updated (to light red – from white). If that is not possible, I’d like the overflowing section’s (id=”middle”) background to change color when overflow occurs.

How Can I Avoid Race Conditions When Users Try to Buy Tree Upgrades in My “Cookie Clicker” CO₂ Savings Game?

I’ve built the most basic CO₂ Tree Saver Game (still a work in progress, don’t judge). The idea is simple: users save CO₂, earn upgrades, and plant trees. However, there’s a small hiccup — when users try to buy a tree upgrade while their CO₂ savings are still being updated in the background, chaos ensues.

The logic around using CO₂ to buy a tree might be a little… unrefined, but that’s not the problem here. The issue is that when users return after being offline, the local localStorage value for CO₂ is out of sync, and they might end up trying to buy an upgrade they can’t afford.

Here’s a simplified version:

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Green Game (Beta)</title>
    <style>
        .tree {
            width: 50px;
            height: 50px;
            background-color: green;
            border-radius: 50%;
            margin-top: 20px;
        }
        .tree.upgraded {
            width: 100px;
            height: 100px;
            background-color: #8B4513; /* Tree brown color when upgraded */
        }
    </style>
</head>
<body>
    <h1>Welcome to the Green Game (Beta)</h1>
    <p id="game-status">Collecting CO₂ data...</p>
    <p id="co2-output">Current CO₂: 0kg</p>
    <p id="tree-status">You can plant a tree with 100kg of CO₂ saved.</p>

    <div id="tree" class="tree"></div>

    <button id="plant-tree-btn" disabled>Plant a Tree</button>
    <button id="upgrade-tree-btn" disabled>Upgrade Tree</button>

    <script>
        class GreenGame {
            constructor() {
                // Retrieve saved data from localStorage or use defaults
                this.co2Saved = parseInt(localStorage.getItem("co2Saved")) || 0; // Load CO₂ or default to 0
                this.treeThreshold = 100; // CO₂ needed to plant a tree
                this.treeUpgraded = localStorage.getItem("treeUpgraded") === "true"; // Load tree upgrade status

                this.co2Output = document.getElementById("co2-output");
                this.gameStatus = document.getElementById("game-status");
                this.treeStatus = document.getElementById("tree-status");
                this.treeElement = document.getElementById("tree");
                this.plantTreeBtn = document.getElementById("plant-tree-btn");
                this.upgradeTreeBtn = document.getElementById("upgrade-tree-btn");

                // Reflect saved tree upgrade state
                if (this.treeUpgraded) {
                    this.treeElement.classList.add("upgraded");
                    this.treeStatus.textContent = "Tree upgraded! Now it’s a mighty tree!";
                    this.upgradeTreeBtn.disabled = true; // Disable upgrade after upgrading
                }
            }

            updateCO2() {
                // Simulate receiving new CO₂ data (could be from an API)
                setTimeout(() => {
                    const newCO2 = Math.floor(Math.random() * 20); // Random CO₂ value
                    this.co2Saved += newCO2;
                    this.co2Output.textContent = `Current CO₂: ${this.co2Saved}kg`;

                    if (this.co2Saved >= this.treeThreshold) {
                        this.treeStatus.textContent = "You can now plant a tree!";
                        this.plantTreeBtn.disabled = false; // Enable plant button
                    } else {
                        this.treeStatus.textContent = "You need more CO₂ to plant a tree!";
                        this.plantTreeBtn.disabled = true; // Disable plant button
                    }

                    this.gameStatus.textContent = `New CO₂ input: +${newCO2}kg`;

                    // Save updated CO₂ value to localStorage
                    localStorage.setItem("co2Saved", this.co2Saved);

                    // Recursively simulate data updates
                    this.updateCO2();
                }, 3000); // Updates every 3 seconds
            }

            plantTree() {
                if (this.co2Saved >= this.treeThreshold) {
                    this.treeElement.classList.remove("upgraded"); // Reset upgrade on new tree
                    this.treeElement.style.display = "block";
                    this.treeStatus.textContent = "You planted a tree!";
                    this.co2Saved -= this.treeThreshold; // Subtract the CO₂ used to plant the tree
                    this.co2Output.textContent = `Current CO₂: ${this.co2Saved}kg`;

                    this.upgradeTreeBtn.disabled = false; // Enable upgrade button
                    this.plantTreeBtn.disabled = true; // Disable plant button after planting

                    // Save updated CO₂ value to localStorage
                    localStorage.setItem("co2Saved", this.co2Saved);
                }
            }

            upgradeTree() {
                this.treeElement.classList.add("upgraded");
                this.treeStatus.textContent = "Tree upgraded! Now it’s a mighty tree!";
                this.upgradeTreeBtn.disabled = true; // Disable upgrade button after upgrading

                // Save tree upgrade state to localStorage
                localStorage.setItem("treeUpgraded", "true");
            }
        }

        // Start the game
        const game = new GreenGame();
        game.updateCO2(); // Begin simulation

        // Attach event listeners to buttons
        document.getElementById("plant-tree-btn").addEventListener("click", () => game.plantTree());
        document.getElementById("upgrade-tree-btn").addEventListener("click", () => game.upgradeTree());
    </script>
</body>
</html>

The Issue:

  1. CO₂ savings are updated every few seconds, but when a user returns after being offline, their CO₂ value is out of sync.

  2. If they try to buy an upgrade, the app mistakenly thinks they have enough CO₂, even though it’s outdated.

The Question:

  1. What’s the best way to prevent race conditions when the user is offline and comes back to find their CO₂ savings completely out of whack?

My js:

// Initial CO2 savings and tree planting state
let co2Saved = 10;
let treeCount = 0;

const plantTreeButton = document.getElementById('plant-tree');
const upgradeTreeButton = document.getElementById('upgrade-tree');

const updateCO2 = (newCO2Value) => {
  co2Saved = newCO2Value;
  console.log(`CO₂ Saved: ${co2Saved}kg`);
};

const plantTree = () => {
  console.log('Planting tree...');
  treeCount++;
  console.log(`You have ${treeCount} trees now.`);
};

const upgradeTree = () => {
  console.log('Upgrading tree...');
  treeCount++;
  console.log(`You have ${treeCount} trees now.`);
};

// Simulating multiple clicks with race condition
plantTreeButton.addEventListener('click', () => {
  // Simulate async updates to CO2 savings and tree planting
  setTimeout(() => {
    updateCO2(co2Saved + 10); // Update CO2 after planting tree
    plantTree();
  }, Math.random() * 1000); // Random delay to simulate timing race
});

upgradeTreeButton.addEventListener('click', () => {
  // Simulate async updates to CO2 savings and tree upgrading
  setTimeout(() => {
    updateCO2(co2Saved + 5); // Update CO2 after upgrading tree
    upgradeTree();
  }, Math.random() * 1000); // Random delay to simulate timing race
});

This is what happens:

  1. Process 1 checks the balance and sees 100kg.

  2. Process 2 also checks the balance and sees 100kg.

  3. Both processes try to withdraw 50kg.

  4. After both processes finish, they both update the balance to 50kg, even though 100kg should have been the correct result after two withdrawals.

Why I think this is happening:

  • Multiple threads or processes are running concurrently.

  • These threads/processes share a common resource – the database that pulls in CO₂ live info.

This is a basic version of the game, and yes, it’s a work in progress. But if you can solve this, I’ll plant a tree (in the game, because it’s a work in progress).

Thanks in advance for any advice — or suggestions to fix the CO₂ as currency logic.

Best practices for JS Module Designs

Ok, I understand in JS modules are preferred versus Classes, and that there is a ton of ways to write your modules, and I am having some trouble figuring out which are the accepted best practices for each case…

For example, if i am writing some sort of utilities class, this seems to be a good way to go:

export function a
export function b
export function c

however i already have reached a doubt, use functions or constants? export func a or export const a = () => {} ?

And when we start to think about other scenarios where you will probably want some kind of “Class” like code, or something that you will need to use some dependency injection an so on and so forth, the questions starts to grow, should i still create my modules like utilities as I just mentioned?

Should i create a function that returns an object like this

export default () => {
  function a
  function b

  return { a, b }
}

?

or should i use a constant like this:

const myModule = {}

myModule.a = () => {}
myModule.b = () => {}

export default myModule

or do i take any other approach i haven’t mentioned? Is there a general standard where i should use one or another based on the type of the module? Or any general best practice?

HTML: image name as url parameter does not work?

I have a HTML page which that using an image name as URL parameter. Why doesn’t show the image, what do i wrong?

<!DOCTYPE html>
<html lang="nl">
<head>
    <script src="lib/functions.js"></script>
</head>


<body>

<!-- test03.html?parameter=mountains.jpg -->

<img src="" id="imagename">

<script>
    const queryString = window.location.search;
    const urlParameters = new URLSearchParams(queryString);
    const Parameter = urlParameters.get('parameter');
    document.getElementById("imagename").innerHTML = Parameter;
</script>

</body>

</html>

Telegram requestWriteAccess popup window

Creating the Webapp game as telegram mini app. And stucked with one feature based on the requestWriteAccess.

What i want to achieve is that when i requestWriteAccess from user, it will show pop up as the screen below

popup i want to achieve

I tried this but it shows only the standart telegram bot alert bot as you can see it below

What we have

Here is the code we use

const allowsWriteToPm = window.Telegram.WebApp.initDataUnsafe.user.allows_write_to_pm;
    if (!allowsWriteToPm) {
        window.Telegram.WebApp.requestWriteAccess(function(ctx) {
            console.log("[TG] requestWriteAccess: " + ctx);

            if (!ctx || ctx !== "allowed" && String(ctx) !== "true"){
                return;
            }

            TG.trySendGreetMsg();
        });
    } else {
        TG.trySendGreetMsg();
    }

I checked another products and it seems that they have almost the same code.

NextJS; TypeError: handleComplete is not a function

Although i transferred props properly, i keep getting “is not function” error. I’ve worked a lot on that but i couldn’t find a solution. When I try to move(I do necessary implementings) handleComplete function to ProductAddTemplate.tsx, this time i get setIsSubmitting() is not function error.

enter image description here

AddProduct.tsx:

"use client";
import React, { useState } from "react";
import PageBreadcrumb from "@/components/common/PageBreadCrumb";
import { ProductAddTemplate } from "../templates/ProductAddTemplate";
import { Product } from "../types/types";
import { Step } from "../types/types";
import { motion } from "framer-motion";
import { FileSpreadsheet, Edit, CheckCircle, ArrowRight } from "lucide-react";
import { ProductService } from "@/utils/api/services/productService";
import {toast} from "sonner";

export default function AddProduct(): React.ReactElement {
    const [currentStep, setCurrentStep] = useState<number>(0);
    const [selectedMethod, setSelectedMethod] = useState<"manual" | "excel" | null>(null);
    const [productName, setProductName] = useState<string>("");
    const [selectedProducts, setSelectedProducts] = useState<Product[]>([]);
    const [isSubmitting, setIsSubmitting] = useState<boolean>(false);

    // Adımları tanımlayalım - renkli ve ikonlu
    const steps: Step[] = [
        {
            id: 0,
            title: "Yükleme Yöntemi",
            icon: <ArrowRight className="h-5 w-5" />,
            color: "bg-blue-500"
        },
        {
            id: 1,
            title: "Ürün Bilgileri",
            icon: <Edit className="h-5 w-5" />,
            color: "bg-purple-500"
        },
        {
            id: 2,
            title: "Önizleme",
            icon: <FileSpreadsheet className="h-5 w-5" />,
            color: "bg-amber-500"
        },
        {
            id: 3,
            title: "Tamamlandı",
            icon: <CheckCircle className="h-5 w-5" />,
            color: "bg-green-500"
        }
    ];

    // Yükleme yöntemini seçme işlevi
    const handleSelectMethod = (method: "manual" | "excel"): void => {
        setSelectedMethod(method);
        // Animasyon için timeout
        setTimeout(() => {
            setCurrentStep(1); // Bir sonraki adıma geç
        }, 300);
    };

    // Ürün ekleme
    const handleAddProduct = (): void => {
        if (productName.trim() === "") return;
        // Basit bir ürün oluştur (gerçek uygulamada API'den gelecek)
        const newProduct: Product = {
            id: Date.now(), // Geçici ID
            name: productName
        };
        // Animasyonlu ekleme
        setSelectedProducts(prev => [...prev, newProduct]);
        setProductName(""); // Input'u temizle
    };

    // Ürün kaldırma
    const handleRemoveProduct = (productId: number): void => {
        setSelectedProducts(selectedProducts.filter(p => p.id !== productId));
    };

    // Sıfırlama işlevi
    const handleReset = (): void => {
        setCurrentStep(0);
        setSelectedProducts([]);
        setProductName("");
        setSelectedMethod(null);
    };

    // Son adıma geçildiğinde ürünleri API'ye gönder ve konfeti efekti
    // Ürün ekleme işlemini tamamla
    // Ürün ekleme işlemini tamamla
    const handleComplete = async (): Promise<void> => {
        // Ürün kontrolü
        if (selectedMethod === "manual" && selectedProducts.length === 0) {
            toast.warning('Lütfen en az bir ürün ekleyin.');
            return;
        }
        // Yükleme durumunu başlat
        setIsSubmitting(true);
        // Ürün verilerini hazırla
        interface ProductData {
            productNames: string[];
            method: "manual" | "excel" | null;
        }
        const productData: ProductData = {
            productNames: selectedProducts.map((product: Product) => product.name),
            method: selectedMethod
        };
        try {
            // API çağrısını yap
            let success: boolean = false;
            if (selectedMethod === "manual") {
                // Manuel ekleme için API çağrısı
                interface ApiResponse {
                    isSuccessful: boolean;
                    errorMessages?: string[];
                    data?: any;
                }
                const response: ApiResponse | undefined = await ProductService.create(productData);
                success = Boolean(response && response.isSuccessful);
            } else {
                // Excel yükleme için farklı bir API çağrısı (örnek)
                // const response = await ProductService.uploadExcel(excelFile);
                // success = Boolean(response && response.isSuccessful);
                // Şimdilik Excel yüklemesini başarılı sayalım
                success = true;
            }
            // Sonuca göre işlem yap
            if (success) {
                // Başarılı olursa son adıma geç
                setCurrentStep(3);
                // Konfeti efekti
                // Başarı mesajı
                const productCount: number | string = selectedMethod === "manual"
                    ? selectedProducts.length
                    : "Excel'den yüklenen";
                toast.success(`${productCount} ürün başarıyla eklendi.`);
            } else {
                // Hata mesajı
                toast.error("Ürünler eklenirken bir hata oluştu. Lütfen tekrar deneyin.");
            }
        } catch (error: unknown) {
            // Hata durumunda
            console.error("Ürün ekleme işlemi başarısız:", error);
            // Hata mesajını daha detaylı göster
            if (error instanceof Error) {
                toast.error(`Hata: ${error.message}`);
            } else {
                toast.error("Bir hata oluştu. Lütfen daha sonra tekrar deneyin.");
            }
        } finally {
            // Her durumda yükleme durumunu sonlandır
            setIsSubmitting(false);
        }
    };
    return (
        <motion.div
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.5 }}
            className="min-h-screen bg-gradient-to-br from-white to-blue-50"
        >
            <PageBreadcrumb pageTitle="Ürün Ekle" />
            <motion.div
                className="space-y-4 max-w-5xl mx-auto p-6"
                initial={{ scale: 0.95 }}
                animate={{ scale: 1 }}
                transition={{ duration: 0.3 }}
            >
                <ProductAddTemplate
                    currentStep={currentStep}
                    steps={steps}
                    selectedMethod={selectedMethod}
                    productName={productName}
                    setProductName={setProductName}
                    selectedProducts={selectedProducts}
                    handleSelectMethod={handleSelectMethod}
                    handleAddProduct={handleAddProduct}
                    handleRemoveProduct={handleRemoveProduct}
                    setCurrentStep={setCurrentStep}
                    handleReset={handleReset}
                    isSubmitting={isSubmitting}
                    handleComplete={handleComplete}
                />
            </motion.div>
        </motion.div>
    );
}

ProductAddTemplate.tsx:

import React from "react";
import { StepIndicator } from "../molecules/StepIndicator";
import { MethodSelection } from "../organisms/MethodSelection";
import { StepHeader } from "../organisms/StepHeader";
import { ManualProductEntry } from "../organisms/ManualProductEntry";
import { ExcelUpload } from "../organisms/ExcelUpload";
import { ProductPreview } from "../organisms/ProductPreview";
import { CompletionMessage } from "../organisms/CompletionMessage";
import ComponentCard from "@/components/common/ComponentCard";
import { Step } from "../types/types";
import { Product } from "../types/types";
import { useRouter } from 'next/navigation';
import { motion, AnimatePresence } from "framer-motion";
import { ArrowRight, ArrowLeft, CheckCircle } from "lucide-react";
import {toast} from "sonner";
import ProductService from "@/utils/api/services/productService";

interface ProductAddTemplateProps {
    currentStep: number;
    steps: Step[];
    selectedMethod: "manual" | "excel" | null;
    productName: string;
    setProductName: (name: string) => void;
    selectedProducts: Product[];
    handleSelectMethod: (method: "manual" | "excel") => void;
    handleAddProduct: () => void;
    handleRemoveProduct: (productId: number) => void;
    setCurrentStep: (step: number) => void;
    handleReset: () => void;
    isSubmitting?: boolean;
    handleComplete: () => Promise<void>;
}

export const ProductAddTemplate: React.FC<ProductAddTemplateProps> = ({
                                                                          currentStep,
                                                                          steps,
                                                                          selectedMethod,
                                                                          productName,
                                                                          setProductName,
                                                                          selectedProducts,
                                                                          handleSelectMethod,
                                                                          handleAddProduct,
                                                                          handleRemoveProduct,
                                                                          setCurrentStep,
                                                                          handleReset,
                                                                          isSubmitting = false,
                                                                          handleComplete
                                                                      }) => {
    const router = useRouter();

    // Adım geçişleri için animasyon varyantları
    const variants = {
        hidden: { opacity: 0, x: 50 },
        visible: { opacity: 1, x: 0 },
        exit: { opacity: 0, x: -50 }
    };

    // Adım başlıklarını ve renklerini belirle
    const getStepColor = (stepId: number) => {
        const colors = ["bg-blue-500", "bg-purple-500", "bg-amber-500", "bg-green-500"];
        return colors[stepId] || colors[0];
    };

    // Adım 3'e geçiş için işlev - async olarak işaretlendi
    const goToFinalStep = async (): Promise<void> => {
        console.log("handleComplete type:", typeof handleComplete);
        await handleComplete();
    };

    return (
        <ComponentCard
            title="Ürün Ekle"
            className="border shadow-lg rounded-xl overflow-hidden"
        >
            {/* Adım göstergesi - renkli ve animasyonlu */}
            <div className="mb-8 px-4">
                <StepIndicator
                    steps={steps}
                    currentStep={currentStep}
                    activeColor={getStepColor(currentStep)}
                />
            </div>

            <AnimatePresence mode="wait">
                {/* Adım 0: Yükleme Yöntemi Seçimi */}
                {currentStep === 0 && (
                    <motion.div
                        key="step0"
                        initial="hidden"
                        animate="visible"
                        exit="exit"
                        variants={variants}
                        transition={{ duration: 0.3 }}
                        className="py-4"
                    >
                        <MethodSelection
                            onSelectMethod={handleSelectMethod}
                        />
                    </motion.div>
                )}

                {/* Adım 1: Ürün Bilgileri */}
                {currentStep === 1 && (
                    <motion.div
                        key="step1"
                        initial="hidden"
                        animate="visible"
                        exit="exit"
                        variants={variants}
                        transition={{ duration: 0.3 }}
                        className="flex flex-col gap-6 w-full py-4"
                    >
                        <StepHeader
                            title={selectedMethod === "manual" ? "Ürün Bilgilerini Girin" : "Excel Dosyasını Yükleyin"}
                            subtitle={selectedMethod === "manual"
                                ? "Eklemek istediğiniz ürünlerin bilgilerini girin"
                                : "Ürün bilgilerini içeren Excel dosyasını yükleyin"}
                            icon={selectedMethod === "manual" ? "edit" : "file-spreadsheet"}
                            color={getStepColor(1)}
                            onBack={() => setCurrentStep(0)}
                        />

                        {selectedMethod === "manual" ? (
                            <ManualProductEntry
                                productName={productName}
                                setProductName={setProductName}
                                selectedProducts={selectedProducts}
                                handleAddProduct={handleAddProduct}
                                handleRemoveProduct={handleRemoveProduct}
                                onContinue={() => setCurrentStep(2)}
                                buttonColor={getStepColor(1)}
                            />
                        ) : (
                            <ExcelUpload
                                onContinue={() => setCurrentStep(2)}
                                buttonColor={getStepColor(1)}
                            />
                        )}
                    </motion.div>
                )}

                {/* Adım 2: Önizleme */}
                {currentStep === 2 && (
                    <motion.div
                        key="step2"
                        initial="hidden"
                        animate="visible"
                        exit="exit"
                        variants={variants}
                        transition={{ duration: 0.3 }}
                        className="flex flex-col gap-6 w-full py-4"
                    >
                        <StepHeader
                            title="Ürün Bilgilerini Önizleyin"
                            subtitle="Eklediğiniz ürünleri kontrol edin ve onaylayın"
                            icon="eye"
                            color={getStepColor(2)}
                            onBack={() => setCurrentStep(1)}
                        />

                        <ProductPreview
                            selectedMethod={selectedMethod}
                            selectedProducts={selectedProducts}
                            onEdit={() => setCurrentStep(1)}
                            onConfirm={goToFinalStep}
                            buttonColor={getStepColor(2)}
                            isSubmitting={isSubmitting}
                        />
                    </motion.div>
                )}

                {/* Adım 3: Tamamlandı */}
                {currentStep === 3 && (
                    <motion.div
                        key="step3"
                        initial="hidden"
                        animate="visible"
                        variants={variants}
                        transition={{ duration: 0.3 }}
                        className="py-4"
                    >
                        <CompletionMessage
                            selectedMethod={selectedMethod}
                            productCount={selectedProducts.length}
                            onAddNew={handleReset}
                            onGoToList={() => router.push('/products')}
                            color={getStepColor(3)}
                            isProcessing={isSubmitting}
                        />
                    </motion.div>
                )}
            </AnimatePresence>

            {/* İlerleme göstergesi */}
            <div className="mt-8 flex justify-between items-center text-sm text-gray-500 border-t pt-4">
                <div>
                    {currentStep > 0 && currentStep < 3 && (
                        <button
                            onClick={() => setCurrentStep(currentStep - 1)}
                            className="flex items-center text-gray-600 hover:text-gray-900 transition-colors"
                            disabled={isSubmitting}
                        >
                            <ArrowLeft className="h-4 w-4 mr-1" />
                            Geri
                        </button>
                    )}
                </div>

                <div className="flex items-center">
                    <span className={`font-medium ${getStepColor(currentStep).replace('bg-', 'text-')}`}>
                        Adım {currentStep + 1}/{steps.length}
                    </span>
                    {currentStep === 3 && (
                        <CheckCircle className="h-4 w-4 ml-2 text-green-500" />
                    )}
                </div>

                <div>
                    {currentStep < 2 && currentStep > 0 && (
                        <button
                            onClick={() => setCurrentStep(currentStep + 1)}
                            className="flex items-center text-gray-600 hover:text-gray-900 transition-colors"
                            disabled={isSubmitting}
                        >
                            İleri
                            <ArrowRight className="h-4 w-4 ml-1" />
                        </button>
                    )}
                </div>
            </div>
        </ComponentCard>
    );
};