How to access a member of an object array in JavaScript

I am doing an exercise. I am having an issue accessing an object array’s members. I have the following object array declaration:

const dishData = [
{
name: "Italian pasta",
price: 9.55
},
{
name: "Rice with veggies",
price: 8.65
},
{
name: "Chicken with potatoes",
price: 15.55
},
{
name: "Vegetarian Pizza",
price: 6.45
},
]

I am trying to access each member of the object array within a for loop. My following code will not complie:

dishData.forEach(dish) => {
var finalPrice;
if (taxBoolean == true) {
finalPrice = dishData(dish).price * tax;
}
else if (taxBoolean == false) {
finalPrice = dishData(dish).price;
}
else {
console.log("You need to pass a boolean to the getPrices call!");
return;
}
console.log("Dish: ", dishData(dish).name, "Price: $", dishData(dish).finalPrice);
}

}

When I use dishData(dish).price, I mean to access the price property for that loops iterantion of dishData.

Can anyone help me out?

Javascript: Adding an event listener if the mouse is down and removing it while it is up

I am trying to create a pixel painting project using HTML, CSS, and JS. I am having trouble with the painting mechanics.

expected result:

mousedown –> being able to move mouse and paint using mouseover –> mouseup (stop painting)

I originally did this:

for (let i = 0; i <= pixels.length; i++) {
    pixels[i]?.addEventListener("mouseover", () => {
        pixels[i].setAttribute("style", "background: black");
        console.log("painting");
    })

}

and this works when the mouse is over the canvas. but this doesn’t include the mousedown/mouseup mechanic. so, I tried this:

try 1:

if (mouseDown) {
    pixels.forEach(pixel => {
        pixel.style.background = colorSelector.value;
        pixel.addEventListener("mouseover", function coloring(event) {
            pixel.style.background = colorSelector.value;
            if (!mouseDown) {
                pixel.removeEventListener("mouseover", coloring);
            }
        })
    })
}

try 2:

pixels.forEach(pixel => {
    if (mouseDown) {
        //to color the first pixel clicked
        pixel.style.background = colorSelector.value;
        pixel.addEventListener("mouseover", function coloring(event) {
            pixel.style.background = colorSelector.value;

            if (!mouseDown) {
                //remove the coloring if the mouse button is up
                pixel.removeEventListener("mouseover", coloring);
            }
        })
    }
})

But none seem to work. I have checked the mouseDown and it works so I am not sure why it doesn’t execute.

Failed to load PostCSS config: module is not defined in ES module scope

I’m encountering an issue with my PostCSS configuration in my project. When I try to run my development server, I receive the following error message:Failed to load PostCSS config: module is not defined in ES module scope
Context:
I have a postcss.config.js file that I am using to configure PostCSS.
My project is set up to use ES modules (I have “type”: “module” in my package.json).
I am using Node.js version 22.13.0
My project is built with [insert framework or build tool, e.g., React, Vite

Steps Taken:
I checked my package. Json and confirmed that it includes “type”: “module”.
I attempted to rename my Post CSS config file to postcss.config.cjs to use CommonJS syntax, but I still encounter issues.
I verified that all required dependencies are installed.

What could be causing this error, and how can I resolve it? Are there specific changes I need to make to my PostCSS configuration or project setup to ensure compatibility with ES modules?

Thank you for your help!

OTS parsing error: invalid sfntVersion: 1702391919 Error Semantic UI

I started a small project in react + webpack in which I tried to use the components proposed by Semantic UI, here, I set up a small search form which contains an icon proposed by Semantic UI, but at the time of the build of my application, the icon does not load and the error message below appears:

error icon

after several searches I modified the .gitattributes file :

gitattributes file

I also modified the webpack file:

const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');

const port = 8080;

module.exports = merge(common, {
  mode: 'development',
  devtool: 'inline-source-map',
  module: {
    rules: [
      // Styles for SCSS files
      {
        test: /.(scss)$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              sourceMap: true,
              importLoaders: 2,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true,
            },
          },
          {
            loader: 'sass-loader',
            options: {
              sourceMap: true,
              implementation: require('sass'),
            },
          },
        ],
      },
      // Styles for CSS files
      {
        test: /.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              sourceMap: true,
            },
          },
          {
            loader: 'postcss-loader',
            options: {
              sourceMap: true,
            },
          },
        ],
      },
      // Fonts and assets
      {
        test: /.(woff|woff2|eot|ttf|otf)$/,
        type: 'asset/resource',
      },
    ],
  },

  devServer: {
    historyApiFallback: true,
    static: path.resolve(__dirname, '../dist'),
    static: {
      watch: {
        ignored: /node_modules/,
      },
    },
    open: true,
    compress: true,
    hot: true,
    port,
  },
});

The problem persists in spite of everything. Any ideas? ^^

StrictMode + useEffect creates two items in LocalStorage

I am currently working on a pokemon team builder. The issue I have is the following:

On my SelectedTeamBanner component I have an effect that should check if user has any team stored on localStorage. If the user doesn´t have any team on localStorage it should create only one team and save it on localStorage:

import { PokemonTeam } from '../../../../domain/teamMemberEntities';
import useWeavileStore from '../../../../globalContext/WeavileStore';
import { useDefaultTeam } from '../../hooks/useDefaultTeam';
import { BannerMember, SelectedTeamName, TeamTypesDropdown } from './';

import '../../styles/selectedTeamBanner.css';

export const SelectedTeamBanner = () => {

    const { getDefaultTeam } = useDefaultTeam();
    const selectedTeam: PokemonTeam | null = useWeavileStore((state) => state.selectedPokemonTeam);
    
    /* Faulty effect */
    useEffect(() => {
        const asyncEffectWrapper = async () => {            
            await getDefaultTeam(); 
            /* I don´t know if there is any point on awaiting this promise
             but not awaitng it neither fixed the issue */
        }
        asyncEffectWrapper();
    }, []);

    return (
        <section className="selected-team-banner">
            {/* This component renders before useEffect finishes causing to selectedTeam to be undefined
            That is why all nodes check if selectedTeam is undefined before rendering*/}
            {
                selectedTeam && <SelectedTeamName />
            }
            {
                selectedTeam?.teamMembers
                && selectedTeam.teamMembers.map((member, index) => (
                    <BannerMember member={member} key={index} />
                ))
            }
            <TeamTypesDropdown />
        </section>
    );
}

getDefaultTeam is the function that based on checkIfUserHasTeams result decides what to do:

import { PokemonTeam } from "../../../domain/teamMemberEntities";
import useWeavileStore from '../../../globalContext/WeavileStore';
import { createNewTeamRequest } from "../api/nonLoggedUsers";
import { checkIfUserHasTeams, storePokemonTeam } from "../helpers/nonLoggedUser";

export const useDefaultTeam = () => {

    const changeSelectedTeam = useWeavileStore((state) => state.changeSelectedTeam);     
    const changeSelectedMember = useWeavileStore((state) => state.changeSelectedPokemon);

    const getDefaultTeam = async(): Promise<PokemonTeam> => {
        const result: PokemonTeam | null = checkIfUserHasTeams();
        
        if (result !== null) return result;
        else {
            const response = await createNewTeamRequest(TeamType.INDIVIDUAL); // Server side works fine
            if (response.status === 201) {
                const firstTeam: PokemonTeam = storePokemonTeam(response.data);
                changeSelectedTeam(firstTeam);
                changeSelectedMember(firstTeam.teamMembers[0]);      
                
                return firstTeam;
            } 
            else throw new Error("Error creating default first pokemon " + response.statusText);
        };
    }

    return { getDefaultTeam };
    
};

checkIfUsersHasTeams() is meant to search items on localStorage looping between 0 and 14.
If it finds one, then return the JSON parsed as PokemonTeam, if not then return null to create a new team on server side.

/* Items in localStorage saves using a numeric key between 0 and 14 */

export const checkIfUserHasTeams = ():  PokemonTeam | null => {

    for(let i: number = 0; i < 15; i++) {
        const storedItem = localStorage.getItem(i.toString());
        
        if(storedItem !== null) {
            const parsedItem: PokemonTeam = JSON.parse(storedItem);
            return parsedItem;
        }  
    }

    return null;
}

And finally, the function that stores the team on localStorage:

import { PokemonTeam } from '../../../../domain/teamMemberEntities/PokemonTeam';
import { getAllTeamsLocalStorage } from "./getAllTeamsLocalStorage";

export const storePokemonTeam = (argTeam: PokemonTeam): PokemonTeam => {

    /* This code is to give the team a default name */
    if (!argTeam.name || argTeam.name === undefined || argTeam.name === '') {
        const allTeams: PokemonTeam[] = getAllTeamsLocalStorage();

        const unamedTeamsNumber: number = allTeams.filter(team => team.name.startsWith("Unamed")).length;
        argTeam.name = `Unamed${unamedTeamsNumber + 1}`;
    }

    /* teams are stored on localStorage using a key between 0 and 14 */
    for (let i: number = 0; i < 15; i++) {
        if (localStorage.getItem(i.toString()) === null) {
            argTeam.id = i;
            localStorage.setItem(i.toString(), JSON.stringify(argTeam));
            return JSON.parse(localStorage.getItem(argTeam.id.toString())!) as PokemonTeam;
        };
    }

    throw new Error("Already stored 15 teams"); 
}

The problem is that React StrictMode is causing to run twice the effect and for whatever reason the checkIfUserHasTeams function is returning twice null, causing to create two teams on localStorage instead of one (Removing StrictMode made the code work fine)

I would like a solution that doesn´t imply removing StrictMode. I am using Zustand for global context. None of the issues I have found on StackOverflow helped me with this problem.

Easepick – close calender on second click on input field

I would like to close the calander, if the user clicks again on the input field.
Do you know a good way to realise that?

const picker = new easepick.create({
    element: document.getElementById('datepicker'),
    css: [
    'https://cdn.jsdelivr.net/npm/@easepick/bundle@1.2.1/dist/index.css',
    ],
    calendars: 2,
    grid: 2,
    plugins: ['LockPlugin','RangePlugin'],
    LockPlugin: {
    minDate: new Date(),
    },
    RangePlugin: {
    tooltip: true,
    startDate: new Date(),
    endDate: new Date('2027-12-31'),
    locale: {
        one: 'day',
        other: 'days',},
    },    
});

Thank you in advance for your help!

ElasticSearch without response data in PHP

This is my mapping:

$ curl -X GET -k -u elastic:elastic “http://127.0.0.1:9200/_mapping”

{"photobank":{"mappings":{"properties":{"id":{"type":"long"},"title":{"type":"text","fields":{"keyword":{"type":"keyword","ignore_above":256}}}}}}}
I inserted test data like title: “aaa” and “bbb”.
But when I search I heva no response from ES:

$params = [
                    'index' => 'photobank',
                    'body'  => [
                        'query' => [
                            /*'match' => [
                                'title' => 'aaa'
                            ],*/
                            'bool' => [
                                'must' => [
                                    'match' => [
                                        'title' => 'aaa'
                                    ]
                                ],
                            ]
                        ]
                    ],
                ];
$response = $client->search($params);

$response has status 200 but nothing more. Where are result data or is my query wrong?

Laravel API in docker is returning HTML instead of JSON?

I’m dockerzing my Laravel API and going quite crazy.

Locally with either php artisan serve or symfony serve everything works well and has been working for years.

Now I’m finally dockerzing (not an expert in Docker) Laravel for production and having some problems.

The main one is that all the api routes return text/html as content type and not application/json, but locally it returns application/json.

The code base obviously is the same one. And the return of each api route is like this response()->json($data);

This is my dockerfile and it’s being used in a docker-compose file. Anyone has any idea why?

FROM php:8.3-fpm-alpine3.20
 
RUN apk update && apk upgrade
 
# Essentials
RUN echo "Europe/London" > /etc/timezone
RUN apk add git zip unzip curl sqlite supervisor
 
# Install Python
RUN apk add python3 py3-pip
 
RUN apk add nodejs npm
RUN apk add nano
 
RUN apk add php83-gd 
    php83-imap 
    php83-redis 
    php83-cgi 
    php83-bcmath 
    php83-mysqli 
    php83-zlib 
    php83-curl 
    php83-zip 
    php83-mbstring 
    php83-iconv 
    gmp-dev
 
# dependencies required for running "phpize"
# these get automatically installed and removed by "docker-php-ext-*" (unless they're already installed)
ENV PHPIZE_DEPS 
    autoconf 
    dpkg-dev 
    dpkg 
    file 
    g++ 
    gcc 
    libc-dev 
    make 
    pkgconf 
    re2c 
    zlib 
    wget
 
# Install packages
RUN set -eux; 
    # Packages needed only for build
    apk add --virtual .build-deps 
    $PHPIZE_DEPS
 
RUN apk add --no-cache linux-headers
 
# Packages to install
RUN apk add  curl 
    gettext-dev 
    libmcrypt-dev 
    icu-dev 
    libpng 
    libpng-dev 
    libressl-dev 
    libtool 
    libxml2-dev 
    libzip-dev 
    libjpeg-turbo-dev 
    libwebp-dev 
    freetype-dev 
    oniguruma-dev 
    unzip 
 
# pecl PHP extensions
RUN pecl install 
    # imagick-3.4.4 
    mongodb 
    redis
# Configure PHP extensions
RUN docker-php-ext-configure 
    # ref: https://github.com/docker-library/php/issues/920#issuecomment-562864296
    gd --enable-gd --with-freetype --with-jpeg --with-webp
# Install PHP extensions
RUN  docker-php-ext-install 
    bcmath 
    bz2 
    exif 
    ftp 
    gettext 
    gd 
    # iconv 
    intl 
    gmp 
    mbstring 
    opcache 
    pdo 
    pdo_mysql 
    shmop 
    sockets 
    sysvmsg 
    sysvsem 
    sysvshm 
    zip 
    && 
    # Enable PHP extensions
    docker-php-ext-enable 
    # imagick 
    mongodb 
    redis 
    && 
    # Remove the build deps
    apk del .build-deps
RUN apk cache clean 
# fix work iconv library with alphine for PHP 8.1 broken
ENV LD_PRELOAD /usr/lib/preloadable_libiconv.so
# # Installing bash
# RUN apk add bash
# RUN sed -i 's/bin/ash/bin/bash/g' /etc/passwd
 
# Installing composer
RUN curl -sS https://getcomposer.org/installer -o composer-setup.php
RUN php composer-setup.php --install-dir=/usr/local/bin --filename=composer
RUN rm -rf composer-setup.php
 
# Configure supervisor
RUN mkdir -p /etc/supervisord/conf.d/
RUN touch /run/supervisord.sock
COPY ./docker-ops/backend/supervisord/laravel-worker.conf /etc/supervisord/conf.d/laravel-worker.conf
COPY ./docker-ops/backend/supervisord/supervisord.ini /etc/supervisord/supervisord.ini
 
# Cron Config
COPY ./docker-ops/backend/crontab /etc/crontabs/root
 
# Config PHP
COPY ./docker-ops/backend/php/local.ini /usr/local/etc/php/php.ini
# COPY ./ /var/www
COPY --chown=www-data:www-data ./my-app /var/www
COPY ./docker-ops/backend/scripts.env /var/www/resources/scripts/.env
COPY ./docker-ops/backend/.env.laravel /var/www/.env
 
 
# Install Python packages
ENV PIP_BREAK_SYSTEM_PACKAGES 1
RUN pip install -r /var/www/resources/scripts/requirements.txt
 
 
USER root
WORKDIR /var/www
 
EXPOSE 8000
 
CMD ["php", "artisan", "serve", "--host", "0.0.0.0", "--port=8000"]```

woocommerce wordpress logout issue

I am using storefront theme, WordPress, WooCommerce plugin.
I have been logged out again n again in 5secs
checked all
both site urls which are same,
Clearing browser cache,
activating/deact plugin themes child theme altogether and also one by one, tested page for cache giving proper count in incremental, changing to 2025 theme from storefront,cleared session from database.Nothing is working it still logs out

Force session cookie and path fix
In wp-config.php, 

define('COOKIE_DOMAIN', 'www.domain.com');
define('COOKIEPATH', '/');
define('SITECOOKIEPATH', '/');
define('ADMIN_COOKIE_PATH', '/wp-admin');
define('PLUGINS_COOKIE_PATH', '/wp-content/plugins');
define('FORCE_SSL_ADMIN', true);
already tried clearing sessions and custom paths

LAMP. When the owner /var/www/site/ -> www-data and group USER1, result: er

LAMP.

  • case 1:
    When the owner of /var/www/site/ -> www-data and group USER1, result: files upload on server from index.php BUT i CAN’T edit from sftp the files on server from USER1. (permission denied)

  • case 2:
    When the owner /var/www/site/ USER1 and group www-data, result: error when files uploads on server from index.php BUT i can edit from sftp the files on server from USER1.

Warning: Undefined array key “id” in C:Program FilesAmppswwwadminindex.php on line 182 [duplicate]

I have a problem, I want to make changes to a certain user’s database table. I managed to do this by opening another page.

<td><a href="includes/update.php?id=<? echo $user['id']?>" class="edit">Edit href</a></td>

The page will open as follows
page code img

But through the modal window, when I try to open the data, I get an error message: Warning: Undefined array key “id” in C:Program FilesAmppswwwadminindex.php on line 182

                <td><button tupe="hidden" name="user_id" onclick="window.myDialog.showModal()" value="<? echo $user['id']?>" class="regbtn">Edit modal</button></td>
            </tr>
            <?
        }
        ?>
        </tbody>
        </table>
                    <div class="left">
                        <dialog id="myDialog" class="modal">
                            <div class="reg-form">
                                <h6>Change the data</h6>
                                <button onclick="closeDialog()">x</button>
                                <script>
                                    var x = document.getElementById("myDialog"); 
                                    function showDialog() { 
                                    x.show(); 
                                    } 
                                    function closeDialog() { 
                                    x.close(); 
                                    } 
                                </script>
                                <form method="POST">
                                    <?
                                        $id = $_GET['id'];
                                        $query = mysqli_query($connect, query: "SELECT * from `users` where ID='$id'");
                                        while($row = mysqli_fetch_array($query)) {
                                    ?>
                                    <div class="reg-box">
                                        <input type="text" value="<? echo $row["lastname"]?>" name="lastname" placeholder="Last Name">
                                    </div>
                                    <div class="reg-box">
                                        <input type="text" value="<? echo $row["name"]?>" name="name" placeholder="Name">
                                    </div>
                                    <div class="reg-box">
                                        <input type="text" value="<? echo $row["fathername"]?>" name="fathername" placeholder="Father Name">
                                    </div>
                                    <div class="reg-box">
                                        <input type="number" value="<? echo $row["code"]?>" name="code" placeholder="Code">
                                    </div>
                                    <div class="reg-box">
                                        <input type="text" value="<? echo $row["username"]?>" name="username" placeholder="Login">
                                    </div>
                                    <div class="reg-box">
                                        <input type="number" value="<? echo $row["password"]?>" name="password" placeholder="Password">
                                    </div>
                                    <? } ?>    
                                    <button tupe="reg" class="btn">Edit</button>
                                </form>
                            </div>
                            <style type="text/css">
                                dialog {
                                    margin-inline: auto;
                                    margin-block-start: 120px;
                                    background: transparent;
                                    box-shadow: 0 0 10px rgba(0, 0, 0, 0.211);
                                    border: none;
                                    border-radius: 1rem;
                                }
                                dialog::backdrop {
                                    background-color: rgba(0, 0, 0, 0.2);
                                }
                            </style>
                        </dialog>
                    </div>
    </div>

MetaMask Returns ‘-32603 Internal JSON-RPC error’ with No Revert Reason on Polygon Testnet

I’m building a DApp on the Polygon testnet (the new Amoy network) using MetaMask, ethers.js, and a Solidity smart contract. Whenever I try to call my startMaintenance() function (or several others) from my React frontend, I get an immediate error:

code: -32603
message: "Internal JSON-RPC error."

No revert reason is returned—even though I added try/catch and error decoding with interface.parseError(…). Here’s what I’ve confirmed so far:

  1. Role Checks: I’m calling this as a technician account. In theory, my contract’s onlyTechnician check should pass.
  2. Asset Status: The asset is set to “Broken,” so the require(status == ‘Broken’) check should pass too.
  3. Gas / Funds: I have plenty of test MATIC in the technician’s wallet for gas.
  4. ABI & Address: My ABI is up to date, and the contract address is correct on the Amoy testnet.
  5. No Detailed Revert Data: The node or MetaMask is just returning the generic “Internal JSON-RPC error” with no additional info.
    I’ve also checked block explorers for a transaction log, but sometimes the TX isn’t even appearing. Has anyone seen this before on Polygon testnets or had the node fail to pass back revert messages? Any ideas on how to coax out the real error reason or confirm where the transaction is failing?
    This is the function involved in the error:
try {
  const tx = await assetManagerContract.startMaintenance(asset.id, startComment);
  await tx.wait();
  alert("Maintenance started!");
} catch (error) {
  console.error("Error handleStartMaintenance:", error);
  // tried to decode revert data, but all I get is the -32603 internal error
}
function startMaintenance(uint _id, string memory startComment) public onlyTechnician {
    require(_id < nextAssetId, "Asset does not exist");
    require(!assets[_id].isDeleted, "Asset is deleted");
    require(keccak256(bytes(assets[_id].status)) == keccak256(bytes("Broken")),
        "Asset must be broken to start maintenance"
    );
    // ...
    assets[_id].status = "Under Maintenance";
    assets[_id].technician = msg.sender;
    emit MaintenanceStarted(_id, msg.sender);
}

odoo18: in crm Application, how to solve TypeError: Cannot read properties of undefined (reading ‘disconnect’) at MoveNodePlugin.destroy

in odoo 18, after a successful database migration from odoo 16, in the CRM application, inside the default kanban-view, i get this error when i click on a lead to get its form-view :

UncaughtPromiseError > OwlError
Uncaught Promise > An error occured in the owl lifecycle (see this Error's "cause" property)

Occured on localhost:8078 on 2025-04-06 08:35:01 GMT

OwlError: An error occured in the owl lifecycle (see this Error's "cause" property)
    Error: An error occured in the owl lifecycle (see this Error's "cause" property)
        at handleError (http://localhost:8078/web/assets/6/debug/web.assets_web.js:9567:35)
        at App.handleError (http://localhost:8078/web/assets/6/debug/web.assets_web.js:13964:20)
        at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10475:30)
        at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)
        at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)
        at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)
        at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)
        at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)
        at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)
        at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)

Caused by: TypeError: Cannot read properties of undefined (reading 'disconnect')
    at MoveNodePlugin.destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:131906:35)
    at Editor.destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:121094:24)
    at Wysiwyg.<anonymous> (http://localhost:8078/web/assets/6/debug/web.assets_web.js:145711:41)
    at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10471:28)
    at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)
    at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)
    at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)
    at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)
    at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)
    at ComponentNode._destroy (http://localhost:8078/web/assets/6/debug/web.assets_web.js:10466:23)

enter image description here

ThreeJS – Decals are not projected on Surface (glb)

It’s the first time I’m working with ThreeJS on a “more complex” model. After Exporting the OBJ from blender into glb format and import it into my project it works fine – positioning is good and I can add Decals. But when moving or rotating the model the decal position stays but the projection is not “on surface” of the model.

Current state:

const helmet = new Helmet(
    logoTexture,
    new THREE.Color(0x0C072D),
    new THREE.Vector3(0, 10, 0)
);
scene.add(await helmet.generate())

import * as THREE from 'three';
import {GLTFLoader} from "three/addons/loaders/GLTFLoader";

const loader = new GLTFLoader();
import {rotationHelper} from "./utils.js";
import {DecalGeometry} from "three/addons/geometries/DecalGeometry";

export class Helmet {

    logoTexture;
    position;
    rotation;
    color;
    texture;
    isLogoFacingRight;
    shouldMirrorLogo;

    constructor(logoTexture = "", color = new THREE.Color(0xffffff), position = new THREE.Vector3(0, 0, 0), rotation = new THREE.Euler(0, 0, 0), isLogoFacingRight = false, shouldMirrorLogo = false) {
        this.logoTexture = logoTexture;
        this.position = position;
        this.rotation = rotation;
        this.color = color;
        this.texture = null;
        this.isLogoFacingRight = true;
        this.shouldMirrorLogo = false;
    }

    /**
     *
     * @param pos       object          {x,y,z} coordinates unit based for placement in the scene
     * @param rot       object          {x:radiant, y:radiant, z:radiant}
     * @param color     THREE.color     like: THREE.Color(0xffffff)
     * @param texture   THREE.texture   generated via TextureLoader
     * @param isLogoFacingRight bool    True if the Logo image shows to the right
     * @param shouldMirrorLogo  bool    True if the Logo should be mirrored on the opposite side of the helmet
     */
    generate() {
        return new Promise(async (resolve, reject) => {

            // Texturen vorladen
            const metalStrapsTexture = await this.#safeLoadTexture('../models/helmet/helmetFootball_metalStraps_baseColor.png');
            const strapTexture = await this.#safeLoadTexture('../models/helmet/helmetFootball_strap_baseColor.png');

            loader.load(
                './models/helmet/helmetFootball.glb',  // Passen Sie den Pfad entsprechend an
                (gltf) => {
                    const model = gltf.scene.children[0];

                    if (this.logoTexture) {
                        this.#createDecals(model);
                    }

                    model.traverse((node) => {
                        ...
                    });


                    // Optional: Position anpassen
                    model.position.set(
                      this.position.x, 
                      this.position.y, 
                      this.position.z
                    );

                    // Rotation anpassen (falls nötig)
                    model.rotation.set(
                        this.rotation.x,
                        this.rotation.y,
                        this.rotation.z
                    );

                    model.updateMatrix();
                    model.updateMatrixWorld(true); // Erzwingt vollständige Matrixaktualisierung nach rotation


                    resolve(model);
                },
                // Ladefortschritt
                (progress) => {
                    console.log('Lade: ', (progress.loaded / progress.total * 100) + '%');
                },
                // Fehlerbehandlung
                (error) => {
                    console.error('Fehler beim Laden des Modells:', error);
                    reject(error);
                }
            );
        });
    }

    async #createDecals(model) {

        const texture = await this.#safeLoadTexture(this.logoTexture);
        if (!texture) {
            console.log("No Logo Texture found")
            return;
        }

        const targetNode = model.getObjectByName('helmetFootball_4');

        if (!targetNode) {
            console.log("No target for decal found")
        }

        const boundingBox = targetNode.geometry.boundingBox;

        const boundingBoxHelper = new THREE.Box3Helper(boundingBox, 0xff0000); // Rot als Farbe
        targetNode.add(boundingBoxHelper);

        // Decal-Parameter
        const decalSize = new THREE.Vector3(8, 8, 8); // Anpassen je nach Logogröße
        const decalOrientation = new THREE.Euler(0, rotationHelper(90), 0);
        const yPosition = (targetNode.geometry.boundingBox.min.y + targetNode.geometry.boundingBox.max.y) / 2 + 5;


        // Decal-Positionen (links und rechts)
        const decalPositions = [
            new THREE.Vector3(targetNode.geometry.boundingBox.max.x, yPosition, 0),
            new THREE.Vector3(targetNode.geometry.boundingBox.min.x, yPosition, 0)
        ];


        decalPositions.forEach((position, index) => {

            // Helfer zur Viasualisierung der Position
            const helperBox = new THREE.Mesh(
                new THREE.BoxGeometry(8, 8, 8), // Größe der Hilfsbox (x, y, z)
                new THREE.MeshBasicMaterial({
                    color: 0x00ff00, // Grüne Farbe
                    wireframe: true  // Nur Drahtgitter-Ansicht
                })
            );
            helperBox.position.copy(position);
            targetNode.add(helperBox);

            // Decal-Geometrie erstellen
            const decalGeometry = new DecalGeometry(
                targetNode,
                position,
                decalOrientation,
                decalSize
            );

            // Decal-Material
            const decalMaterial = new THREE.MeshBasicMaterial({
                map: texture,
                transparent: true,
                depthTest: true,
                opacity: 1
            });

            // Decal-Mesh erstellen
            const decalMesh = new THREE.Mesh(decalGeometry, decalMaterial);

            // Zum Ziel-Mesh hinzufügen
            targetNode.add(decalMesh);
        });
    }

    #safeLoadTexture(path) {
        return new Promise((resolve, reject) => {
            const textureLoader = new THREE.TextureLoader();
            textureLoader.load(
                path,
                (texture) => resolve(texture),
                undefined, // onProgress
                (error) => {
                    console.warn(`Texture not found: ${path}`, error);
                    // Fallback-Textur oder null zurückgeben
                    resolve(null);
                }
            );
        });
    };
}

Logo images (decals) positioned correctly but not projected onto mesh

As far as i understand it creates the decal projection based on another / the old position and then moves it to the right position? If that is the case how could I solve that?

See the correct placement and projection if the model position is set to 0,0,0:
correct placed if model is set to 0,0,0

Sidenote: the meshes are unstyled because with the way I export it now somehow the mesh titles where not implemented.

thanks for your hints.

FormData Content-Type mismatch between operating systems

There seems to be a difference between MIME types on macOS and Windows when using FormData for file uploads.

Windows users are complaining that the file upload doesn’t work and the validation error that comes back is:
“Validation failed (current file type is application/octet-stream, expected type is text/tab-separated-values)”

I’m scratching my head because when I check MDN it seems like the FormData API should be compatible with all browsers, but it’s not behaving the same across operating systems.

https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData

There’s clearly a difference in the Content-Type

Edge macOS

------WebKitFormBoundary3WUJCpBdz1ohAJza
Content-Disposition: form-data; name="transactions"; filename="testdata.tsv"
Content-Type: text/tab-separated-values


------WebKitFormBoundary3WUJCpBdz1ohAJza--

Edge Windows:

------WebKitFormBoundaryACGjxE52TKrSKr1F
Content-Disposition: form-data; name="transactions"; filename="testdata.tsv"
Content-Type: application/octet-stream


------WebKitFormBoundaryACGjxE52TKrSKr1F--

I have an ugly fix, but I have no idea if I might be overlooking something?

    const [file] = this.dropzone.files;

    const formData = new FormData();

    formData.append(
      'file',
      // FormData was sent as application/octet-stream from Windows devices so we need to convert it to text/tab-separated-values
      new Blob([file], { type: 'text/tab-separated-values' }),
      file.name,
    );

This will have a huge impact on my workflow because I now have to assume that there is likely more mismatched behavior between Mac and Windows. How do I you deal with stuff like this? Do I have to start running my automated tests for different operating systems now?

For now I’ve built in monitoring for 400 Bad Request on my access logs so I can catch this kind of stuff earlier, but want to hear how other people deal with these kinds of problems.