Strange behaviour in useEffect that clearInterval() does not working

I got a strange behaviour in my react code:

In development (strict) mode, the following component did not clear interval successfully in clear function when interval is set to 1000 milliseconds. If I change the interval to slightly larger value (e.g. 1010 milliseconds). The callback can be stopped when component unmount. I have no ideas why the code behave like this. Anyone knows why it have such strange behaviour and how should I fix it?

function Timer() {
  const [secondsPassed, setSecondPassed] = useState(0);
  const [rows, setRow] = useState(1);
  const thresold = 60;

  useEffect(() => {
    console.log("effect start");

    function onInterval() {
      setSecondPassed(prevSec => {
        let currentSecond = prevSec + 1;
        console.log(currentSecond);
        if (currentSecond % thresold === 0) {
          setRow(prevRow => prevRow + 1);
        }
        return currentSecond;
      });
    }
    const intervalId = setInterval(onInterval, 1000);
    console.log(`setinterval: ${intervalId}`);
    
    return () => {
      console.log(`clearInterval: ${intervalId}`);
      clearInterval(intervalId);
    }
  }, []);

  return (
    <>
      <div className="box-x" style={{ width: secondsPassed % thresold }}></div>
    </>
  );
}

However, this clock component works in the same app just fine.

function Clock() {
  const [now, setNow] = useState(new Date());
  
  useEffect(() => {
    const intervalId = setInterval(() => {
      setNow(new Date());
    }, 1000);
    
    return () => {
      clearInterval(intervalId);
    }
  }, []);

  return (<h2 >Time now is {now.getSeconds()}</h2>);
}

If I change to use setTimeout() and setTimeout() instead, it works fine with 1000 milliseconds as well.

Environment:

  • Node: v22.3.0
  • React: 18.3.1
  • vscode: 1.93.1
  • Chrome: 127.0.6533.120 arm64

How to render my web component before connectedCallback but after constructor?

I am making a custom web component using vanilla JavaScript and am having some issues with when the component should be rendered.

At first I had my render() and hydrate() calls inside the constructor of the component. This worked well when the component was already part of the DOM to begin with, however, if the component was created using document.createElement("my-button"), then the render() call would end up executing before I would have the chance to add attributes and child elements ect. which was a major problem.

The other alternative, which is what my code below shows, is it have the render() and hydrate() calls inside the connectedCallback() method. This fixes the document.createElement("my-button") problem, however, it introduces a new one. Because the connectedCallback() only executes after the element is added to the DOM, I could potentially get a FOUC (Flash of unstyled content) before the component is finished rendering. It would’ve been nice if there was a beforeConnectedCallback() so I can execute the code before it is added to the DOM, but this does not seem to exist.

So what should I do to get my component to automatically render and hydrate before it is added to the DOM?

Here is my component:

class MYButton extends HTMLElement {

    constructor() {
        super();
    }

    connectedCallback() {
        this.render();
        this.hydrate();
    }

    render() {
        let type = this.getAttribute("type");
        if (type === "link") {
            this.elmButton = document.createElement("a");
            this.elmButton.setAttribute("href", this.getAttribute("href"));
        } else {
            this.elmButton = document.createElement("button");
            this.elmButton.setAttribute("type", type);
        }
        while (this.firstChild) {
            this.elmButton.appendChild(this.firstChild);
        }
        this.appendChild(this.elmButton);
    }

    hydrate() {
        this.elmButton.addEventListener("click", () => alert("hello world"));
    }
}

customElements.define('my-button', MYButton);

Primordials is not defined – not gulp – when running angular

I’ve got a node setup containing the following node_modules:

"dependencies": {
  "@angular/common": "18.2.6",
  "@angular/compiler": "18.2.6",
  "@angular/core": "18.2.6",
  "@angular/forms": "18.2.6",
  "@angular/platform-browser": "18.2.6",
  "@angular/platform-browser-dynamic": "18.2.6",
  "@angular/router": "18.2.6",
  "graceful-fs": "^4.2.11",
  "ngx-toastr": "^17.0.2",
  "rxjs": "7.8.1",
  "tslib": "2.7.0",
  "zone.js": "~0.14.10"
},
"devDependencies": {
  "@angular-devkit/build-angular": "18.2.6",
  "@angular/cli": "18.2.6",
  "@angular/compiler-cli": "18.2.6",
  "@types/body-parser": "^1.19.5",
  "@types/express": "^5.0.0",
  "express": "^4.21.0",
  "nodemon": "^3.0.1",
  "typescript": "^5.5.0"
}

running node v20.17.0

I’m trying to run the command ‘ng serve’ but I keep getting the error:

fs.js:44
primordials;
    ^

ReferenceError: primordials is not defined
at fs.js:44:5
at req_ (C:UsersthomaAppDataRoamingnpmnode_modulesangular-clinode_modulesnativesindex.js:143:24)
at Object.req [as require] (C:UsersthomaAppDataRoamingnpmnode_modulesangular-clinode_modulesnativesindex.js:55:10)
at Object.<anonymous> (C:UsersthomaAppDataRoamingnpmnode_modulesangular-clinode_modulesyamnode_modulesgraceful-fsfs.js:1:37)
at Module._compile (node:internal/modules/cjs/loader:1469:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1548:10)
at Module.load (node:internal/modules/cjs/loader:1288:32)
at Module._load (node:internal/modules/cjs/loader:1104:12)
at Module.require (node:internal/modules/cjs/loader:1311:19)
at require (node:internal/modules/helpers:179:18)

I’ve tried upgrading and downgrading the angular, node and specifically the graceful-fs module as per other posts instructions. I see that a lot of people had this problem with gulp and a long time ago but I don’t have that installed?

what means “yarn pnp”‘s advantage “Shared installs across disks”

The advantage are discussed at https://yarnpkg.com/features/pnp.

I couldn’t find any information about this.

So I had to guess, and I came to this conclusion.

enter image description here

At this image, two project has “swr” package but they had a same packageLocation.

If “node_modules” is used, the path of “swr” for each project will be different.

I think this is the advantage mentioned above, am I right to understand?

How to customize the seek backward/forward interval of the “ARD Mediathek” media player?

The default seek backward/forward interval of the ARD Mediathek media player is around 70 seconds. In my opinion, that is way too much. That’s why I’m looking for a way to adjust the interval to my liking.

I tried to modify the aria-valuenow attribute value of the div.noUi-handle.noUi-handle-lower element.

const valueNow = document.querySelectorAll('.noUi-handle.noUi-handle-lower')[0];

// seek backward 10 seconds
valueNow.setAttribute('aria-valuenow', valueNow.getAttribute('aria-valuenow') - 10);

// seek forward 10 seconds
valueNow.setAttribute('aria-valuenow', valueNow.getAttribute('aria-valuenow') + 10);

But this doesn’t work, my value is immediately overwritten again. Besides, there is probably a more elegant way. The implementation should be done as a Tampermonkey userscript.

Fill With White, Out Of Bounds Part Of Image – Crop Image With CSS Transformations on it

I have a DIV Container and an image inside of it.
The image has CSS transformations like: translate, rotate, scale

The image can be dragged by a user (and scaled, rotated) in the parent div. Sometimes, parts of the image can be outside of the parent div.

I want to create a new image, identical in size with the original image (with no rotation applied, with no scaling applied), but the part of the image (that is rotated/scaled) from the div that is not visible, should be fully white in the final image.

Let me show what I want to obtain using an image:
enter image description here

I tried various methods, but I fail to get the result I am looking for.
Here is the code I came up with (with the help of ChatGPT also)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Dynamic Image Transformation and Cropping</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
        }
        .container {
            width: 300px; /* You can change this to any size */
            height: 300px; /* You can change this to any size */
            border: 2px solid #000;
            position: relative;
            overflow: hidden;
            margin-bottom: 20px;
        }
        .container img {
            width: 150px; /* You can change this to any size */
            height: 150px; /* You can change this to any size */
            /* Apply CSS transformations */
            transform: translate(-30px, -30px) rotate(45deg) scale(1.2);
            transform-origin: center center;
            position: absolute;
            top: 0;
            left: 0;
        }
        #buttons {
            margin-bottom: 20px;
        }
        #tempCanvases {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
        }
        #tempCanvases canvas {
            border: 1px solid #ccc;
        }
    </style>
</head>
<body>
    <h1>Dynamic Image Transformation and Cropping</h1>
    <div class="container">
        <img id="sourceImage" src="https://sodevrom.net/your-image.jpg" alt="Source Image">
    </div>
    <div id="buttons">
        <button id="processButton">Process</button>
        <button id="downloadButton" disabled>Download</button>
    </div>
    <h2>Temporary Canvases (For Debugging)</h2>
    <div id="tempCanvases"></div>

    <script>
        // Wait for the DOM to load
        document.addEventListener('DOMContentLoaded', () => {
            const processButton = document.getElementById('processButton');
            const downloadButton = document.getElementById('downloadButton');
            const tempCanvasesDiv = document.getElementById('tempCanvases');
            const sourceImage = document.getElementById('sourceImage');
            let finalCanvas = null; // To store the final processed canvas

            processButton.addEventListener('click', () => {
                // Clear previous temporary canvases
                tempCanvasesDiv.innerHTML = '';
                finalCanvas = null;
                downloadButton.disabled = true;

                // Step 1: Get container and image dimensions
                const container = sourceImage.parentElement;
                const containerWidth = container.clientWidth;
                const containerHeight = container.clientHeight;

                const imageNaturalWidth = sourceImage.naturalWidth;
                const imageNaturalHeight = sourceImage.naturalHeight;

                const imageRenderedWidth = sourceImage.width;
                const imageRenderedHeight = sourceImage.height;

                console.log('Container Dimensions:', containerWidth, containerHeight);
                console.log('Image Natural Dimensions:', imageNaturalWidth, imageNaturalHeight);
                console.log('Image Rendered Dimensions:', imageRenderedWidth, imageRenderedHeight);

                // Step 2: Get computed styles of the image
                const style = window.getComputedStyle(sourceImage);
                const transform = style.transform;

                // If no transform is applied, set it to identity matrix
                const matrix = transform === 'none' ? new DOMMatrix() : new DOMMatrix(transform);

                // Extract transformation components
                const scaleX = matrix.a;
                const scaleY = matrix.d;
                const rotateRadians = Math.atan2(matrix.b, matrix.a);
                const rotateDegrees = rotateRadians * (180 / Math.PI);
                const translateX = matrix.e;
                const translateY = matrix.f;

                console.log('Extracted Transformations:');
                console.log('ScaleX:', scaleX);
                console.log('ScaleY:', scaleY);
                console.log('Rotate (degrees):', rotateDegrees);
                console.log('TranslateX:', translateX);
                console.log('TranslateY:', translateY);

                // Step 3: Create the first temporary canvas (container size) with transformations applied
                const tempCanvas1 = document.createElement('canvas');
                tempCanvas1.width = containerWidth;
                tempCanvas1.height = containerHeight;
                const ctx1 = tempCanvas1.getContext('2d');

                // Fill with white
                ctx1.fillStyle = '#FFFFFF';
                ctx1.fillRect(0, 0, tempCanvas1.width, tempCanvas1.height);

                // Calculate the center of the image
                const centerX = imageRenderedWidth / 2;
                const centerY = imageRenderedHeight / 2;

                // Apply transformations: translate, rotate, scale around the center
                ctx1.translate(translateX + centerX, translateY + centerY); // Move to the center
                ctx1.rotate(rotateRadians); // Apply rotation
                ctx1.scale(scaleX, scaleY); // Apply scaling
                ctx1.translate(-centerX, -centerY); // Move back

                // Draw the image
                ctx1.drawImage(sourceImage, 0, 0, imageRenderedWidth, imageRenderedHeight);

                // Append the first temporary canvas for debugging
                appendCanvas(tempCanvas1, 'Transformed Image');

                // Step 4: Create the second temporary canvas to revert transformations and crop
                const tempCanvas2 = document.createElement('canvas');
                tempCanvas2.width = containerWidth;
                tempCanvas2.height = containerHeight;
                const ctx2 = tempCanvas2.getContext('2d');

                // Fill with white
                ctx2.fillStyle = '#FFFFFF';
                ctx2.fillRect(0, 0, tempCanvas2.width, tempCanvas2.height);

                // To revert transformations, apply inverse transformations
                // Inverse scaling
                const invScaleX = 1 / scaleX;
                const invScaleY = 1 / scaleY;
                // Inverse rotation
                const invRotateRadians = -rotateRadians;

                ctx2.translate(-translateX - centerX, -translateY - centerY); // Reverse translation
                ctx2.translate(centerX, centerY); // Move to center
                ctx2.rotate(invRotateRadians); // Apply inverse rotation
                ctx2.scale(invScaleX, invScaleY); // Apply inverse scaling
                ctx2.translate(-centerX, -centerY); // Move back

                // Draw the image
                ctx2.drawImage(sourceImage, 0, 0, imageRenderedWidth, imageRenderedHeight);

                // Append the second temporary canvas for debugging
                appendCanvas(tempCanvas2, 'Reverted Transformations');

                // Step 5: Crop the image back to original size (natural image size)
                // Create final canvas based on the image's natural size
                finalCanvas = document.createElement('canvas');
                finalCanvas.width = imageNaturalWidth;
                finalCanvas.height = imageNaturalHeight;
                const ctxFinal = finalCanvas.getContext('2d');

                // Fill with white
                ctxFinal.fillStyle = '#FFFFFF';
                ctxFinal.fillRect(0, 0, finalCanvas.width, finalCanvas.height);

                // Calculate the scaling factor between rendered and natural size
                const scaleFactorX = imageNaturalWidth / imageRenderedWidth;
                const scaleFactorY = imageNaturalHeight / imageRenderedHeight;

                // Draw the reverted image onto the final canvas
                ctxFinal.drawImage(
                    tempCanvas2,
                    0, 0, containerWidth, containerHeight, // Source rectangle
                    0, 0, finalCanvas.width, finalCanvas.height // Destination rectangle
                );

                // Append the final canvas for debugging
                appendCanvas(finalCanvas, 'Final Cropped Image');

                // Enable the download button
                downloadButton.disabled = false;
            });

            downloadButton.addEventListener('click', () => {
                if (!finalCanvas) return;

                // Convert the final canvas to a data URL
                const dataURL = finalCanvas.toDataURL('image/png');

                // Create a temporary link to trigger download
                const link = document.createElement('a');
                link.href = dataURL;
                link.download = 'processed_image.png';
                document.body.appendChild(link);
                link.click();
                document.body.removeChild(link);
            });

            /**
             * Utility function to append a canvas with a label for debugging
             * @param {HTMLCanvasElement} canvas 
             * @param {string} label 
             */
            function appendCanvas(canvas, label) {
                const wrapper = document.createElement('div');
                const caption = document.createElement('p');
                caption.textContent = label;
                wrapper.appendChild(caption);
                wrapper.appendChild(canvas);
                tempCanvasesDiv.appendChild(wrapper);
            }
            });
        </script>
</body>
</html>

How to select MenuItem based on prop In React?

What I have:
I created a page for Updating Products in my react app using my custom components.
Product has following fields:

  1. Id
  2. Name
  3. Code
  4. ProductType
  5. Company

What I want to do:
I want to load data in other fields when I select Product from my custom selector

What happens: I am able to load data in code and name fields which are simple text fields but unable to select the appropriate company and product type in my custom selector. It is appearing blank.

Here is my code for EntitySelector.jsx:

import { MenuItem, Select } from "@mui/material";
import { useEffect, useState } from "react";
import { fetchData } from "erp/api/util/DataFetchUtil";
import 'erp/styles/common/EntitySelector.css';

export const EntitySelector = (props) => {
    const [entities, setEntities] = useState([]);

    // Fetch data whenever refresh is true
    useEffect(() => {
        if (props.refresh) {
            fetchData(props.fetchOperationId, props.apiPayload, setEntities);
            props.setRefresh(false);
        }
    }, [props.refresh, props.fetchOperationId, props.apiPayload]);

    // Fetch data on initial load
    useEffect(() => {
        fetchData(props.fetchOperationId, props.apiPayload, setEntities);
    }, [props.fetchOperationId, props.apiPayload]);

    const handleChange = (event) => {
        // Find the selected entity based on the ID
        const selectedEntity = entities.find(entity => entity.id === event.target.value);
        props.setSelectedEntity(selectedEntity);
    };


    return (
        <>
            <Select
                className="entitySelector"
                value={props.selectedEntity ? props.selectedEntity.id : ""} 
                onChange={handleChange}
                displayEmpty
                variant="outlined"
            >
                <MenuItem disabled value="">
                    <em>Select {props.entityName}</em>
                </MenuItem>
                {entities.map((entity) => (
                <MenuItem 
                    key={entity.id} 
                    value={entity.id} 
                    selected={props.selectedEntity ? entity.id === props.selectedEntity.id : false}>
                    {entity.name}
                </MenuItem>
                ))}
            </Select>
        </>
    );
};

And here is the code for Update Screen:

import { Checkbox, Typography } from "@mui/material";
import { addProducts, updateProduct } from "erp/api/inventory/ProductService";
import { ActionButton } from "erp/components/common/ActionButton";
import { ContainerHeading } from "erp/components/common/ContainerHeading";
import { CustomInput } from "erp/components/common/CustomInput";
import { EntitySelector } from "erp/components/common/EntitySelector";
import { Message } from "erp/components/common/Message";
import { VIEW_COMPANY_OPERATION_ID, VIEW_PRODUCT_TYPE_OPERATION_ID, VIEW_PRODUCTS_OPERATION_ID } from "erp/constants/OperationsInfo";
import ProductDTO from "erp/dto/ProductDTO";
import { isInputEmpty } from "erp/util/InputUtil";
import { useEffect, useState } from "react";

export const EditProductScreen = () => {

    const [selectedProduct, setSelectedProduct] = useState({}); 

    const [selectedCompany, setSelectedCompany] = useState({}); 

    const [selectedProductType, setSelectedProductType] = useState({}); 

    const [productName, setProductName] = useState('');

    const [productCode, setProductCode] = useState('');

    const [message, setMessage] = useState('');

    const [expiryApplicable, setExpiryApplicable] = useState(true); 

    useEffect(() => {
        if (selectedProduct) {
            const company = selectedProduct.company || {};
            const productType = selectedProduct.productType || {};
            setSelectedCompany(company);
            console.log("Test");
            console.log(selectedCompany);
            setSelectedProductType(productType);
            setExpiryApplicable(selectedProduct.isExpiryApplicable);
            setProductName(selectedProduct.name);
            setProductCode(selectedProduct.productCode);
        }
    }, [selectedProduct]); 

    const handleExpiryApplicable = (event) => {
        setExpiryApplicable(event.target.checked); 
    };

    const setProductNameAction = (event) => {
        setProductName(event.target.value);
    };

    const setProducCodeAction = (event) => {
        setProductCode(event.target.value);
    };


    const handleEditProduct = async () => {

        if (isInputEmpty(selectedProduct)) {
            setMessage({
                messageType: "ERR",
                txt: `Error: Please select a company`
              });
            return;
        }

        if (isInputEmpty(selectedCompany)) {
            setMessage({
                messageType: "ERR",
                txt: `Error: Please select a company`
              });
            return;
        }

        if (isInputEmpty(selectedProductType)) {
            setMessage({
                messageType: "ERR",
                txt: `Error: Please select a Product Type`
              });
            return;
        }

        if (isInputEmpty(productName)) {
            setMessage({
                messageType: "ERR",
                txt: `Error: Please enter product name`
              });
            return;
        }

        if (isInputEmpty(productCode)) {
            setMessage({
                messageType: "ERR",
                txt: `Error: Please enter product code`
              });
            return;
        }


        const product = new ProductDTO();

        let productId = selectedProduct.id;

        product.companyId = selectedCompany.id ;
        product.productTypeId = selectedProductType.id;

        product.name = productName;
        product.productCode = productCode;

        product.isExpiryApplicable = expiryApplicable;

        const payload = JSON.stringify(product);
        try {
            const responseData = await updateProduct(productId, payload);
            if (responseData.responseCode === 20) {
                setMessage({
                messageType: "MSG",
                txt: responseData.message
                });
            } else {
                setMessage({
                messageType: "DETAILED_ERR",
                txt: `Error: ${responseData.message}`
                });
            }
        } catch (error) {
        console.error('Error updating product:', error);;
        setMessage({
            messageType: "ERR",
            txt: `Error: An error occurred. Please try again.`
        });
        }

    };

    let req = {
        fetchProductCount: false,
        fetchId: true
    }


    return (
    <div className="addEntityContainer"> 
        <ContainerHeading heading = "h4" headingTxt = "Edit Product"/> 

        <EntitySelector 
                entityName="Product"
                fetchOperationId={VIEW_PRODUCTS_OPERATION_ID} 
                selectedEntity={selectedProduct} 
                setSelectedEntity={setSelectedProduct}
                apiPayload = {{fetchAllData: true}}
                />

        <EntitySelector 
                entityName="Company"
                fetchOperationId={VIEW_COMPANY_OPERATION_ID} 
                selectedEntity={selectedCompany} 
                setSelectedEntity={setSelectedCompany}
                apiPayload = {req}
                />

        <EntitySelector 
                entityName="Product Type" 
                fetchOperationId={VIEW_PRODUCT_TYPE_OPERATION_ID} 
                selectedEntity={selectedProductType} 
                setSelectedEntity={setSelectedProductType}
                apiPayload = {req}
                />

        <CustomInput 
                    inputType="text" 
                    inputValue = {productName}
                    inputPlaceholder="Enter Product Name" 
                    isRequired = {true}
                    changeHandler = {setProductNameAction}
                />

        <CustomInput 
                    inputType="text" 
                    inputValue = {productCode}
                    inputPlaceholder="Enter Product Code" 
                    isRequired = {true}
                    changeHandler = {setProducCodeAction}
                />

        <div style={{display: "flex", flexDirection: "row"}}>
            <Typography variant="body1" sx={{alignContent: 'center', color: 'white'}}>Is Expiry Applicable</Typography>
            <Checkbox
            defaultChecked
            style={{ color: '#fff' }}
            checked={expiryApplicable}
            onChange={handleExpiryApplicable}
            />
        </div>

        <ActionButton handleClick={handleEditProduct} buttonText = "Add Product"/>

        <Message message={message}/>

    </div>
    );
}

How to resolve this issue? Secondly whenever is EntitySelector is updated it calls the API to fetch entities. How to avoid this?

Axios error when doing a POST request of a .txt file

I am working on a project with Flask and React and more specifically, right now I am working on an API endpoint that has the utility of receiving a .txt file from the frontend, and then it would run a script that analyzes data from that .txt and saves plots related to the analysis.

Here is the API endpoint at the backend:

@analysis_ns.route('/upload', methods=["POST", "GET"])
class ChatAnalysisUploadResource(Resource):
    def get(self):
        return {"message": "This endpoint is only for file uploads via POST."}, 405

    #@jwt_required()
    def post(self):
        """Upload a chat file, process it and return the image URLs"""
        try:
            if 'file' not in request.files:
                abort(400, description="No file part")

            file = request.files['file']

            if file.filename == '':
                abort(400, description="No selected file")

            if file and allowed_file(file.filename):
                filename = secure_filename(file.filename)
                file_path = os.path.join(UPLOAD_FOLDER, filename)
                file.save(file_path)

                # Procesa el archivo y genera las imágenes
                process_chat_file(file_path)

                image_paths = ['static/mensajes_enviados.png', 'static/emojis.png']
                missing_images = [path for path in image_paths if not os.path.exists(path)]

                if missing_images:
                    abort(500, description=f"Imagen no encontrada: {missing_images}")

                image_urls = {
                    'urls': [
                        url_for('static', filename='mensajes_enviados.png', _external=True),
                        url_for('static', filename='emojis.png', _external=True)
                    ]
                }

                # Guardar los resultados en la base de datos
                result_summary = ";".join(image_urls['urls'])
                new_analysis = ChatAnalysisResults(
                    chat_name=filename,
                    result_summary=result_summary
                )
                new_analysis.save()

                return jsonify(image_urls), 200

            abort(400, description="File type not allowed")

        # Captura el error y devuelve un mensaje serializable
        except Exception as e:
            return jsonify({"error": str(e)}), 500

And here is the React component that handle the upload of the document (I am not very good at react):

import { useState } from 'react'
import PropTypes from 'prop-types'
import axios from 'axios'
import '../App.css'

function UploadForm({ setImages }) {
    const [file, setFile] = useState(null);
    const [response, setResponse] = useState('');
    const [loading, setLoading] = useState(false);

    const handleFileChange = (event) => {
        setFile(event.target.files[0]);
    };

    const handleUpload = async () => {
        if (!file) {
            alert("Please select a chat file to upload.");
            return;
        }

        const formData = new FormData()
        formData.append('file', file) // Cambiado de 'chat_file' a 'file'

        setLoading(true) // Inicia la animación de carga
        setImages([])   // Limpia los resultados anteriores
        setResponse('') // Opcional: limpia el mensaje de respuesta

        console.log('Loading started');

        try {
            const response = await axios.post('http://127.0.0.1:8080/analysis/upload', formData, {
                headers: {
                    'Content-Type': 'multipart/form-data'
                }
            });

            console.log('Response data:', response.data);

            setImages(response.data.urls);
            setResponse('Chat file analyzed successfully');
        } catch (error) {
            console.error('Error uploading the file:', error);
            setResponse('Error uploading the file: ' + (error.response ? error.response.data.error : error.message));
        } finally {
            setLoading(false); // Detiene la animación de carga independientemente del resultado
            console.log('Loading finished');
        }
    }

    return (
        <div className="upload-form">
            <input type="file" onChange={handleFileChange} accept=".txt" />
            <button onClick={handleUpload}>
                {loading ? 'Analyzing' : 'Analyze Chat'}
            </button>
            {response && <p>{response}</p>}
            {loading && (
            <div className='loading'>
                <div className='spinner'></div>
            </div>
            )}
        </div>
    )
}

UploadForm.propTypes = {
    setImages: PropTypes.func.isRequired, 
}

export default UploadForm

And this is the error that I am getting in the console:

enter image description here

Also the error that I can see on the VSCode Terminal is the following:

  File "/opt/anaconda3/lib/python3.11/json/encoder.py", line 180, in default
    raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type Response is not JSON serializable

All the API endpoints so far work correctly except for this one, I have been stuck with this problem for a week and I could use some help. If you need more info or want to see more parts/scripts of the project feel free to ask 🙂

D3 GeoJSON generated from TopoJSON renders some but not all features.. why is this?

I am trying to render a congressional boundaries map using d3. I am able to do this with the plain geojson file, but because it is quite large, I was trying to trim it down geo2topo. Now when I try to render the topojson file in d3 by calling the topojson.feature() API method, and then pass the new returned feature collection it only renders some of the geometries but not all. I have tested the topojson file on https://geojson.io/, where it able to render it correctly. So I am now a bit stumped about what to do.

what my d3 code draws
how it looks on geojson.io

<!DOCTYPE html>
<div id="container"></div>
<script src="d3.v7.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>

<center>
    <div id="map"></div>
</center>
<style>
    #map {
        margin: 10px;
    }
</style>
<script>
    const longitude = -96;
    const latitude = 39;
    const scaleValue = 1000;
    const width = 960;
    const height = 640;

    // zoom helper funcitons
    function zoomFunction(event) {
        d3.selectAll("path")
            .attr("transform", event.transform);
        d3.selectAll("circle")
            .attr("transform", event.transform);
    }

    var zoom = d3.zoom()
        .scaleExtent([.6, 7])
        .on("zoom", zoomFunction);

    // Load the files and then call the render function
    function loadFiles() {
        // load all the files here
        const mapDataPromise = d3.json('assets/2002usaSIMPLE.topo.json');

        // Use Promise.all to wait for all promises to resolve for each file
        Promise.all([mapDataPromise])
            .then(([data]) => {
                const data2 = topojson.feature(data, data.objects["2002usa"])
                // Render the chart with both datasets
                renderChart(data2);
            })
            .catch(error => {
                console.error('Error loading files:', error);
            });
    }

    // Do actual rendering of map
    function renderChart(data) {

        const projection = d3.geoAlbersUsa()
            .scale(scaleValue)
            .translate([width / 2, height / 2]);

        const pathGenerator = d3.geoPath().projection(projection);

        const svg = d3.select("#map")
            .append("svg")
            .attr("width", width)
            .attr("height", height)
            .style("border", "2px solid black");

        const innerSpace = svg
            .append("g")
            .attr("class", "inner_space")
            .call(zoom);

        var usaSVG = innerSpace
            .selectAll(".feature")
            .data(data.features)
            .enter().append("path")
            .attr("class", "feature")
            .attr("d", pathGenerator)
            .attr("fill", "lightblue")
            .attr("stroke", "blue")
            .on("error", (error) => console.error("Error rendering feature:", error)); // Log rendering errors;
    }

    loadFiles();
</script>

I tried to see if the topojson -> geojson api conversion call produced a broken geojson file, but it does not appear to be broken, because I can load it in mapshaper.org.

Is this a good api hook for react? [closed]

So im working on a project and unfortunately dont have contact with any experienced devs for feedback. Im trying to learn so any advice is appreciated!

This is an api hook i developed. Im using axios to make requests. the hook aims to consolidate all api logic within one central location rather than having to handle the different errors in inappropriate locations. its also implemented in a way that allows for easy modeification/expantion. the nesting especially in makeRequest method is to limit the ammount of arguments passed to functions and to keep the code easier to follow.

if you forsee any issues with the implmentation, or have any recommendation im open to it all!

the code:

const api = (method, url, headers = {}) => {
    const navigate = useNavigate();
    const [isWorking, setIsWorking] = useState(false)

    const axiosConfig = (method, url, headers, content) => {
        return {
            method: method,
            baseURL: "http://127.0.0.1:8000/",
            url: url,
            headers: {
                'Content-Type' : 'application/json',
                ...headers
            },
            params : method === 'get' ? content : undefined,
            data : method !== 'get' ? content : undefined,
            withCredentials: true,
            credentials: 'include',
            paramsSerializer : objectToQuerString, 
        }};

    const axiosRequest = async (config, responseCallback, errorCallback) => {
        await axios(config)
            .then(response => responseCallback(response))
            .catch(error => errorCallback(error))
    }

    const makeRequest = (variables = {}) =>
        new Promise((resolve, reject) => {
            setIsWorking(true);

            const config  = axiosConfig(method, url, headers, variables)
            let prevError = undefined;

            const handleError = error => {
                if (error.response) {
                    const responseStatus =  error.response.status;
                    if (responseStatus != prevError && responseStatus in ErrorHandlers) {
                        prevError = responseStatus;
                        ErrorHandlers[responseStatus]();
                    } else {
                        reject(error)
                    }
                } else {
                    reject({
                        code : 'INTERNAL_ERROR',
                        message : 'Oops... It seems that something went wrong. Please try again later.',
                        status: 503,
                        data: {}
                    })
                }
            }

            const handleResponse = response => {
                setIsWorking(false);
                resolve(response);
            }

            const ErrorHandlers = {
                401: () => {
                    const refreshConfig = axiosConfig('post', 'auth/refresh', {}, {})
                    axiosRequest(refreshConfig,
                        reponse => axiosRequest(config, handleResponse, handleError),
                        error =>  navigate(`/login?redirect=${window.location.pathname}&error=session_expired`)
                    )
                },
                403: () => {
                    navigate(-1);
                }
            }
            
            axiosRequest(config, handleResponse, handleError)
        })

    return [isWorking, makeRequest]
}

Thanks in advance to anyone who comments!

Compile time typescript function overloading

There is function overloading in typescript which allows to specify multiple signatures for one implementation:

function sayHello(name: string): string;
function sayHello(name: string[]): string[];
function sayHello(name: unknown): unknown {
  if (typeof name === "string") {
    return `Hello there, ${name}!`;
  } else if (Array.isArray(name)) {
    return name.map((name) => `Hello, ${name}!`);
  }
  throw new Error("Something went wrong");
}

This method is ok if I just want to give Typescript interface for users, however it still produces runtime type check. Conceptually it defies the logic of typed languages because in the end we are the ones who make the type checks not the “language” or transpiler in this case. It is technically possible to write something like this because Typescript transpiler knows the types of parameters passed from callee, so it can make a dispatch.

function sayHello(name: string): string {
  return `Hello there, ${name}!`;
}

function sayHello(name: string[]): string[] {
  return name.map((name) => `Hello, ${name}!`);
}

sayHello("Joe")
sayHello(["Joe", "Ann"])

I see that this can cause a transpiled Javascript to be invalid but compiled names doesn’t need to be the same. We can add some markers to the end of each function and use them in the places where the function is used. Is it against the philosophy of the Typescript community?

function sayHello_$1(name) {
  return `Hello there, ${name}!`;
}

function sayHello_$2(name) {
  return name.map((name) => `Hello, ${name}!`);
}

sayHello_$1("Joe")
sayHello_$2(["Joe", "Ann"])

When a function is called using google.script.run, the function status goes to Paused until time out, Google apps Script

I’m currently developing a script to manage a company resources using a Google Spreadsheet, where people would request a resource and inform a due date to it, when that date comes a email is sent remembering the person to give it back. The script works fine when I’m using my account, the account in which I created the spreadsheet and wrote the script. However, when I share the script with another account (with editor authorization), every time the person runs the script, and the google.script.run is called the scripts just shows “paused” until the time out.

I’ve tried different deployments settings, and the same happens. I’ve also tested that the issue seems to reproduce only when the google.script.run is called through a HTML form I made on a sidebar menu.

Here is the current Code.gs:

/**
 * @OnlyCurrentDoc
 */

// function getToken() {
//   let token = ScriptApp.getOAuthToken()
//   return token;
// }

// Displays the menu and the sidebar once the Spreadsheet is loaded
function onOpen(e) {
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('ESIM Menu')
      .addItem('Request ESIM', 'showRequestEsimSidebarForm')
      .addItem('Release ESIM', 'showReleaseEsimSidebarForm')
      .addItem('Test', 'test')
      .addToUi();

  // updateLineCells()

}


function test() {
  displayAlertMessage("Teste", "Teste")
}

function onEdit(e) {
  updateLineCells()
}

// Creates and shows the RequestEsimSideBarForm
function showRequestEsimSidebarForm() {
  var userForm = HtmlService.createTemplateFromFile("request-esim-form").evaluate().setTitle("Esim Request");

  SpreadsheetApp.getUi().showSidebar(userForm)
}

// Creates and shows the ReleaseEsimSideBarForm
function showReleaseEsimSidebarForm() {
  var userForm = HtmlService.createTemplateFromFile("release-esim-form").evaluate().setTitle("Esim Release");

  SpreadsheetApp.getUi().showSidebar(userForm)
}

function updateLineCells() {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  let data = sheet.getDataRange().getValues();
  
  let headers = data[0];
  let lineIndex = headers.indexOf("LINE");
  let availabilityIndex = headers.indexOf("IS AVAILABLE");

  for (let i = 1; i < data.length; i++) {
    let isAvailable = data[i][availabilityIndex];
    let lineCell = sheet.getRange(i+1, lineIndex+1);

    if (isAvailable === true || isAvailable === "TRUE") {
      lineCell.setBackground("#00ff00");
    } else {
      lineCell.setBackground("#ff0000");
    }
  }
}

function displayAlertMessage(title, message) {
  let ui = SpreadsheetApp.getUi();
  ui.alert(title, message, ui.ButtonSet.OK);
}

function displayApprover(title="Teste", message="TEste") {
  let ui = SpreadsheetApp.getUi();
  let response = ui.alert(title, message, ui.ButtonSet.YES_NO);

  if (response === ui.Button.YES) {
    return true;
  } else {
    return false
  }
}

function findMatch(sheet, value) {
  if (value || value.trim() != "") {
    let texFinder = sheet.createTextFinder(value);
    texFinder.matchEntireCell(true);
    let  match = texFinder.findNext();

  if (!match) {
    return null;
  }

  return match;
  } else {
    return null;
  }
}

function checkCellAvailability(sheet, cell) {

  let headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
  let availabilityIndex = headers.indexOf("IS AVAILABLE");

  let availabilityCell = sheet.getRange(cell.getRow(), availabilityIndex + 1);
  let isAvailable = availabilityCell.getValue()

  if (isAvailable == true || isAvailable == "TRUE") {
    return true
  } else {
    return false
  }
}

function saveRequest({name, email, finalDate, esimLine}, sheet) {
  var data = sheet.getDataRange().getValues();

  var headers = data[0];
  let esimLineIndex = headers.indexOf("LINE");
  let currentResponsibleIndex = headers.indexOf("CURRENT RESPONSIBLE");
  let returnDateIndex = headers.indexOf("RETURN DATE");
  let availabilityIndex = headers.indexOf("IS AVAILABLE");

  for (let i = 1; i < data.length; i++) {
    if (data[i][esimLineIndex] == esimLine) {
      sheet.getRange(i+1, currentResponsibleIndex+1).setValue(name);

      sheet.getRange(i+1, returnDateIndex+1).setValue(finalDate);

      sheet.getRange(i+1, availabilityIndex+1).setValue("FALSE");

      displayAlertMessage("Request Saved", `${name} is now responsible for the ESIM line: ${esimLine}.`)
      break;
    }
  }

  updateLineCells()
  
}

// Handles the event when the Submit button is pressed
// THIS IS THE FUNCTION THAT GOES TO "PAUSED" STATUS WHEN ANOTHER USER WHO IS NOT THE CREATOR RUNS THE SCRIPT
function handleRequestEvent(data) {
  // Surprisingly this log is displayed on the logs, but any log after this line is not displayed
  Logger.log("teste")

  let isApproved = displayApprover("Validation", `Do you want to move the following ESIM line: ${data["esimLine"]} to ${data["name"]}? Return date: ${data["finalDate"]}.`)

  if (!isApproved) {
    return;
  }

  try {
    let sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  
    let match = findMatch(sheet, data["esimLine"]);
    if (match != null) {

      let isAvailable = checkCellAvailability(sheet, match)
      if(isAvailable) {
        
        saveRequest(data, sheet);
        
      } else {
        displayAlertMessage("Esim not available", "The Esim you informed is already in use, please choose another one.")
      }

    } else {
        displayAlertMessage("Esim not found", "The Esim you informed was not found on the table, please check again.")
    }
  } catch(error) {
    // displayAlertMessage("Something went wrong", "Sorry, something went wrong, if the error persistis, please contact support.")
    displayAlertMessage("Erro", `${error.stack}`)
  }
   
}

// Not implemented yet
function handleReleaseEvent(data) {
  try {
    let sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
    displayAlertMessage("Teste", "Rello")



  } catch(error) {
    displayAlertMessage("Erro", `${error.stack}`)
  }
}


// Sends an email to every email attached to a client
function sendReminderEmail() {
  let sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  let data = sheet.getDataRange().getValues();
  let headers = data[0];  

  let returnDateIndex = headers.indexOf("RETURN DATE");
  let isAvailableIndex = headers.indexOf("IS AVAILABLE");
  let currentResponsibleIndex = headers.indexOf("CURRENT RESPONSIBLE");
  let esimLineIndex = headers.indexOf("LINE");

  let today = new Date();
  today.setHours(0, 0, 0, 0)

  // Email won't be sent on Weekends
  if (today.getDay() === 6 || today.getDay() === 0) {
    return;
  }

  let reminderMap = {};

  for (let i = 1; i < data.length; i++) {
    let returnDate = new Date(data[i][returnDateIndex]);
    let isAvailable = data[i][isAvailableIndex];
    let responsible = data[i][currentResponsibleIndex];
    let esimLine = data[i][esimLineIndex];

    if (isAvailable == true || isAvailable == "TRUE") {
      continue;
    }

    if (returnDate <= today && (isAvailable === false || isAvailable === "FALSE")) {
      if (responsible) {

        if (!reminderMap[responsible]) {
          reminderMap[responsible] = [];
        }
        reminderMap[responsible].push(esimLine);
      }
    }
  }

  for (let responsible in reminderMap) {
    let esimList = reminderMap[responsible].join(', ');

    let emailBody = `
      Hello ${responsible},<br><br>
      This is a reminder to free the following ESIM lines as their return dates have passed or are due today:<br>
      <strong>${esimList}</strong><br><br>
      Please make sure to release them as soon as possible.<br><br>
      Best regards,<br>
      ESIM Management Team
    `;

    MailApp.sendEmail({
      to: "[email protected]",
      subject: "ESIM Return Reminder",
      htmlBody: emailBody
    });
  }
};

Here as follows is the HTML form that is displayed on the sidebar and calls the google.script.run.handleRequestEvent():

<!DOCTYPE html>
<html lang="en">
  <head>
    <base target="_top">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
    
  </head>
 
  <body>
    <div class="container">
      <form id="EsimRequestForm">
        <div class="mb-3">
          <label for="SelectName" class="form-label">Name</label>
          <select class="form-select" id="SelectName" aria-label="name select input">
            <option selected>Open this select menu</option>
            <option value="Natalia Lima">Natalia Lima</option>
            <option value="Jonas Felix">Jonas Felix</option>
            <option value="Flavia Andrade">Flavia Andrade</option>
          </select>
        </div>

        <div class="mb-3">
          <label for="InputEmail" class="form-label">Email address</label>
          <input type="email" class="form-control" id="InputEmail" aria-describedby="emailHelp" required>
        </div>

        <div class="mb-3">
          <label for="InputEsimLine" class="form-label">Esim Line</label>
          <input type="tel" class="form-control" id="InputEsimLine" aria-describedby="esimLineHelp" required>
        </div>

        <div class="mb-3">
          <label for="InputDate" class="form-label">Will use it until</label>
          <input type="date" class="form-control" id="InputDate" aria-describedby="dateHelp" required>
        </div>

        <button type="submit" class="btn btn-primary" id="submitButton">Submit</button>
      </form>
    </div>
  </body>

    <script>
      var userName = document.querySelector("#SelectName")
      var userEmail = document.querySelector("#InputEmail")
      var esimLine = document.querySelector("#InputEsimLine")
      var finalDate = document.querySelector("#InputDate")
      var requestForm = document.querySelector("#EsimRequestForm")

      requestForm.addEventListener('submit', (e)=> {
        e.preventDefault();
        var data = {
            "name": userName.value,
            "email": userEmail.value,
            "finalDate": finalDate.value,
            "esimLine": esimLine.value
        }
        
        google.script.run.handleRequestEvent(data)
      })

    </script>
</html>

From what I understand it is not a code issue, because it works properly when I’m using the “owner” account, it seems to be a authorization issue of some sort, but the sidebar is displayed to the other users, and they can call the Test dialog through the Test option on the menu.

Why isn’t my image showing in my react project?

I just started learning React and I’m trying to build a static HTML page. Everything works fine except the React logo image which has refused to show.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta name="description" content="Web site created using create-react-app" />
    <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
    <title>React App</title>
  </head>
  <body>
    <div id="root"></div>
    
    <script src="index.js"></script>
  </body>
</html>
import React from "react"
import ReactDOM from "react-dom/client"

const page = (
  <div>
    <img src="first-project/public/logo512.png" width="40px"/>
    <h1>Fun facts about React</h1>
    <ul>
      <li>Was released in 2013</li>
      <li>Was originally created by Jordan Walke</li>
      <li>Has well over 100k stars on Github</li>
      <li>Is maintained by facebook</li>
      <li>Powers thousands of enterprise apps, including mobile apps</li>
    </ul>
  </div>
)

const root = ReactDOM.createRoot(document.getElementById("root"))
root.render(page)

I copied the image from my public folder and for some reason it does not display properly on the web browser.

The image below shows how the image appears on the webpage

enter image description here

The image below shows my IDE:

enter image description here