How to put a text on a 3Dbox ( recto and verso ) ?

I have this code in TypeScript, WebGL and I use Reacts, but it does not work to add text on my box on the back, when I add it it colors the box and when I write it, it doesn’t do exactly what I want. a little more precision, I put the text on the box with a menu.

import { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

interface TextStyle {
  color: string;
  size: number;
  isBold: boolean;
  isItalic: boolean;
  isUnderline: boolean;
  font?: string;
}

interface Box3DProps {
  color: string;
  style?: TextStyle;
  frontImage?: File;
  frontText?: string;
  frontTextStyle?: TextStyle;
  backText?: string;
  backTextStyle?: TextStyle;
  imageScale?: number;
  isPaused: boolean;
  onCapture?: (rectoImage: string, versoImage: string) => void;
}

export const Box3D = ({ color, style, frontImage, frontText, frontTextStyle, backText, backTextStyle, imageScale, isPaused, onCapture }: Box3DProps) => {
  const mountRef = useRef<HTMLDivElement>(null);
  const sceneRef = useRef<THREE.Scene | null>(null);
  const modelRef = useRef<THREE.Group | null>(null);
  const rendererRef = useRef<THREE.WebGLRenderer | null>(null);
  const cameraRef = useRef<THREE.PerspectiveCamera | null>(null);
  const controlsRef = useRef<OrbitControls | null>(null);
  const isRotatingRef = useRef<boolean>(!isPaused);
  const frontMeshRef = useRef<THREE.Mesh | null>(null);
  const backMeshRef = useRef<THREE.Mesh | null>(null);
  const sideMeshesRef = useRef<THREE.Mesh[]>([]);
  const frontTextureRef = useRef<THREE.Texture | null>(null);
  const backTextureRef = useRef<THREE.Texture | null>(null);
  const modelLoadedRef = useRef<boolean>(false);
  const [frontImageDimensions, setFrontImageDimensions] = useState<{width: number, height: number} | null>(null);

  console.log("Props received:", { color, frontText, backText, frontImage, frontTextStyle, backTextStyle });

  useEffect(() => {
    isRotatingRef.current = !isPaused;
  }, [isPaused]);

  const createTextCanvas = (text?: string, style?: TextStyle): HTMLCanvasElement | null => {
    if (!text) return null;

    const canvas = document.createElement('canvas');
    canvas.width = 1024;
    canvas.height = 1024;
    const ctx = canvas.getContext('2d');
    if (!ctx) return null;

    let fontStyle = '';
    if (style?.isBold) fontStyle += 'bold ';
    if (style?.isItalic) fontStyle += 'italic ';
    const fontSize = style?.size || 24;
    const fontFamily = style?.font || 'Arial, sans-serif';

    ctx.font = `${fontStyle}${fontSize}px ${fontFamily}`;
    ctx.fillStyle = style?.color || '#000000';
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';

    const lines = text.split('\n');
    const lineHeight = fontSize * 1.2;
    const startY = canvas.height / 2 - (lines.length - 1) * lineHeight / 2;
    
    lines.forEach((line, index) => {
      ctx.fillText(line, canvas.width / 2, startY + index * lineHeight);
    });

    if (style?.isUnderline) {
      lines.forEach((line, index) => {
        const textMetrics = ctx.measureText(line);
        const textWidth = textMetrics.width;
        const underlineY = startY + index * lineHeight + (fontSize * 0.2);
        
        ctx.beginPath();
        ctx.moveTo((canvas.width - textWidth) / 2, underlineY);
        ctx.lineTo((canvas.width + textWidth) / 2, underlineY);
        ctx.strokeStyle = style.color;
        ctx.lineWidth = Math.max(1, fontSize * 0.05);
        ctx.stroke();
      });
    }

    return canvas;
  };

  const updateModelColors = () => {
    if (!modelRef.current || !color) return;
    
    console.log("Updating model color to:", color);
    
    const baseMaterial = new THREE.MeshStandardMaterial({
      color: color,
      roughness: 0.2,
      metalness: 0.3
    });
    
    if (frontMeshRef.current) {
      const frontMaterial = baseMaterial.clone();
      if (frontTextureRef.current) {
        frontMaterial.map = frontTextureRef.current;
      }
      frontMeshRef.current.material = frontMaterial;
    }
    
    if (backMeshRef.current) {
      const backMaterial = baseMaterial.clone();
      backMaterial.side = THREE.DoubleSide;
      if (backTextureRef.current) {
        backMaterial.map = backTextureRef.current;
      }
      backMeshRef.current.material = backMaterial;
    }
    
    sideMeshesRef.current.forEach(mesh => {
      mesh.material = baseMaterial.clone();
    });
    
    captureView();
  };

  const createImageBoundaryCanvas = (originalCanvas: HTMLCanvasElement, boundaryRatio = 0.8): HTMLCanvasElement => {
    const canvas = document.createElement('canvas');
    canvas.width = originalCanvas.width;
    canvas.height = originalCanvas.height;
    const ctx = canvas.getContext('2d');
    
    if (!ctx) return originalCanvas;
    
    ctx.drawImage(originalCanvas, 0, 0);
    
    const boundaryWidth = canvas.width * boundaryRatio;
    const boundaryHeight = canvas.height * boundaryRatio;
    const x = (canvas.width - boundaryWidth) / 2;
    const y = (canvas.height - boundaryHeight) / 2;
    
    ctx.strokeStyle = 'rgba(100, 100, 100, 0.5)';
    ctx.lineWidth = 2;
    ctx.strokeRect(x, y, boundaryWidth, boundaryHeight);
    
    return canvas;
  };

  // Apply front image
  const applyFrontImage = () => {
    if (!frontImage || !frontMeshRef.current) return;

    console.log("Applying front image...");
    const fileReader = new FileReader();

    fileReader.onload = (e) => {
        if (!e.target?.result) return;

        console.log("Front image loaded successfully");

        // Charger l'image dans un élément HTML pour récupérer ses dimensions
        const img = new Image();
        img.onload = () => {
            console.log("Image dimensions:", img.width, "x", img.height);

            // Création d'un canvas pour garantir la bonne orientation
            const canvas = document.createElement("canvas");
            const ctx = canvas.getContext("2d");
            if (!ctx) return;

            // Vérifier si l'image est en mode portrait
            const isPortrait = img.height > img.width;
            
            // Taille du canvas (ajusté selon l'orientation)
            if (isPortrait) {
                canvas.width = 1024;
                canvas.height = 1024;
            } else {
                canvas.width = 1024;
                canvas.height = 1024;
            }

            // Rotation de l'image si nécessaire
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.save(); // Sauvegarde le contexte pour éviter d'affecter d'autres dessins

            if (isPortrait) {
                // Si l'image est en portrait, on la pivote pour qu'elle reste verticale
                ctx.translate(canvas.width / 2, canvas.height / 2);
                ctx.rotate(Math.PI / -2); // Rotation de 90° dans le sens horaire
                ctx.drawImage(img, -canvas.height / 2, -canvas.width / 2, canvas.height, canvas.width);
            } else {
                // Si l'image est en paysage, on la dessine normalement
                ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
            }

            ctx.restore(); // Restaure le contexte

            // Convertir le canvas en texture
            const texture = new THREE.CanvasTexture(canvas);
            texture.needsUpdate = true;
            texture.colorSpace = THREE.SRGBColorSpace;

            // Appliquer la texture à la face
            if (frontMeshRef.current) {
                frontTextureRef.current = texture;

                const material = new THREE.MeshStandardMaterial({
                    color: color,
                    roughness: 0.2,
                    metalness: 0.3,
                    map: texture
                });

                frontMeshRef.current.material = material;
                captureView();
            }
        };

        img.src = e.target.result as string;
    };

    fileReader.readAsDataURL(frontImage);
  };

  const applyFrontText = () => {
    if (!frontText || !frontMeshRef.current) return;
    
    console.log("Applying front text:", frontText);
    const canvas = createTextCanvas(frontText, frontTextStyle);
    
    if (canvas) {
      const texture = new THREE.CanvasTexture(canvas);
      texture.needsUpdate = true;
      
      const material = new THREE.MeshStandardMaterial({
        color: color,
        roughness: 0.2,
        metalness: 0.3,
        map: texture
      });
      
      frontMeshRef.current.material = material;
      frontTextureRef.current = texture;
      
      captureView();
    }
  };

  const applyBackText = () => {
    if (!backText || !backMeshRef.current) return;
    
    console.log("Applying back text:", backText);
    const canvas = createTextCanvas(backText, backTextStyle);
    
    if (canvas) {
      const texture = new THREE.CanvasTexture(canvas);
      texture.needsUpdate = true;
      
      texture.repeat.set(-1, 1);
      texture.offset.set(1, 0);
      
      backTextureRef.current = texture;
      
      const material = new THREE.MeshStandardMaterial({
        color: color,
        roughness: 0.2,
        metalness: 0.3,
        map: texture,
        side: THREE.DoubleSide
      });
      
      backMeshRef.current.material = material;
      
      captureView();
    }
  };

  useEffect(() => {
    const mount = mountRef.current;
    if (!mount) return;

    console.log("Initializing 3D scene...");

    const scene = new THREE.Scene();
    sceneRef.current = scene;
    scene.background = new THREE.Color(0x87a2a3);

    const renderer = new THREE.WebGLRenderer({ 
      antialias: true,
      preserveDrawingBuffer: true
    });
    renderer.toneMapping = THREE.ACESFilmicToneMapping;
    renderer.toneMappingExposure = 1;
    renderer.setSize(846, 520);
    renderer.setClearColor(0x87a2a3, 1.0);
    renderer.shadowMap.enabled = true;
    rendererRef.current = renderer;
    mount.appendChild(renderer.domElement);

    const camera = new THREE.PerspectiveCamera(65, 846 / 520, 1, 1000);
    camera.position.z = 8;
    cameraRef.current = camera;

    const controls = new OrbitControls(camera, renderer.domElement);
    controlsRef.current = controls;
    controls.enableDamping = true;
    controls.dampingFactor = 0.05;
    
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
    scene.add(ambientLight);

    const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight.position.set(2, 3, 5);
    scene.add(directionalLight);

    const directionalLight2 = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight2.position.set(1, 2, 4);
    scene.add(directionalLight2);

    const directionalLight3 = new THREE.DirectionalLight(0xffffff, 1);
    directionalLight3.position.set(0, -10, 3);
    scene.add(directionalLight3);

    const backLight = new THREE.DirectionalLight(0xffffff, 1);
    backLight.position.set(-2, 0, -5);
    scene.add(backLight);

    const pointLight = new THREE.PointLight(0xffffff, 15, 4); 
    pointLight.position.set(1, 2, 1);
    scene.add(pointLight);
    pointLight.distance = 30;

    const pointLight2 = new THREE.PointLight(0xffffff, 15, 4); 
    pointLight2.position.set(-1, -2, -1);
    scene.add(pointLight2);
    pointLight2.distance = 30;

    const backPointLight = new THREE.PointLight(0xffffff, 15, 4);
    backPointLight.position.set(0, 0, -5);
    scene.add(backPointLight);
    backPointLight.distance = 30;

    const loader = new GLTFLoader();
    console.log("Loading 3D model...");
    
    loader.load('/2FINAL.glb', 
      (gltf) => {
        console.log("Model loaded successfully:", gltf);
        
        const model = gltf.scene;
        model.scale.set(0.5, 0.5, 0.5);
        model.rotation.x = 0 * Math.PI / 180;
        model.rotation.y = 90 * Math.PI / 180;
        model.rotation.z = 0 * Math.PI / 180;

        model.traverse((child) => {
          if (child instanceof THREE.Mesh) {
            console.log("Mesh found:", child.name, "position:", child.position);
            
            if (child.position.z > 0 || 
                child.name.toLowerCase().includes("front") || 
                child.name.toLowerCase().includes("recto")) {
              child.name = "front";
              frontMeshRef.current = child;
              console.log("Identified front mesh:", child.name);
            } 
            else if (child.position.z < 0 || 
                    child.name.toLowerCase().includes("back") || 
                    child.name.toLowerCase().includes("verso")) {
              child.name = "back";
              backMeshRef.current = child;
              console.log("Identified back mesh:", child.name);
            } 
            else {
              child.name = "side";
              sideMeshesRef.current.push(child);
            }
          }
        });

        if (!frontMeshRef.current || !backMeshRef.current) {
          console.log("Identifying faces by mesh order...");
          const meshes: THREE.Mesh[] = [];
          
          model.traverse((child) => {
            if (child instanceof THREE.Mesh) {
              meshes.push(child);
            }
          });
          
          if (meshes.length >= 2) {
            if (!frontMeshRef.current) {
              frontMeshRef.current = meshes[0];
              frontMeshRef.current.name = "front";
            }
            
            if (!backMeshRef.current) {
              backMeshRef.current = meshes[1];
              backMeshRef.current.name = "back";
            }
            
            for (let i = 2; i < meshes.length; i++) {
              if (!sideMeshesRef.current.includes(meshes[i])) {
                sideMeshesRef.current.push(meshes[i]);
              }
            }
            
            console.log("Identified by mesh order: Front and Back meshes set");
          }
        }

        const group = new THREE.Group();
        group.add(model);
        group.position.set(0, 0, 3);

        const box = new THREE.Box3().setFromObject(model);
        const center = box.getCenter(new THREE.Vector3());
        model.position.sub(center);

        scene.add(group);
        modelRef.current = group;
        modelLoadedRef.current = true;

        console.log("Model added to scene");
        
        updateModelColors();
        
        if (frontText) applyFrontText();
        if (backText) applyBackText();
        if (frontImage) applyFrontImage();
        
        const animate = () => {
          requestAnimationFrame(animate);
          
          if (isRotatingRef.current && modelRef.current) {
            modelRef.current.rotation.y += 0.006;
          }
          
          controls.update();
          renderer.render(scene, camera);
        };
        animate();
        
        captureView();
      },
      (progress) => {
        console.log("Loading progress:", (progress.loaded / progress.total) * 100, "%");
      },
      (error) => {
        console.error("Error loading model:", error);
      }
    );

    const handleMouseDown = (e: MouseEvent) => {
      if (e.target === renderer.domElement) {
        isRotatingRef.current = false;
      }
    };

    const handleMouseUp = (e: MouseEvent) => {
      if (e.target === renderer.domElement) {
        isRotatingRef.current = !isPaused;
      }
    };

    mount.addEventListener("mousedown", handleMouseDown);
    mount.addEventListener("mouseup", handleMouseUp);

    const handleResize = () => {
      if (!camera || !renderer || !mount) return;
      camera.aspect = 846 / 520;
      camera.updateProjectionMatrix();
      renderer.setSize(846, 520);
    };

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
      mount.removeEventListener("mousedown", handleMouseDown);
      mount.removeEventListener("mouseup", handleMouseUp);
      if (mount.contains(renderer.domElement)) {
        mount.removeChild(renderer.domElement);
      }
      scene.clear();
    };
  }, []);

  useEffect(() => {
    if (modelLoadedRef.current) {
      updateModelColors();
    }
  }, [color]);

  useEffect(() => {
    if (modelLoadedRef.current) {
      applyFrontImage();
    }
  }, [frontImage, imageScale]);

  useEffect(() => {
    if (modelLoadedRef.current) {
      applyFrontText();
    }
  }, [frontText, frontTextStyle]);

  useEffect(() => {
    if (modelLoadedRef.current) {
      applyBackText();
    }
  }, [backText, backTextStyle]);

  const captureView = () => {
    if (!rendererRef.current || !sceneRef.current || !cameraRef.current || !modelRef.current) return;

    const currentRotation = modelRef.current.rotation.y;

    modelRef.current.rotation.y = -90 * Math.PI/180;
    rendererRef.current.render(sceneRef.current, cameraRef.current);
    const rectoImage = rendererRef.current.domElement.toDataURL('image/png');

    modelRef.current.rotation.y = 90 * Math.PI/180;
    rendererRef.current.render(sceneRef.current, cameraRef.current);
    const versoImage = rendererRef.current.domElement.toDataURL('image/png');

    modelRef.current.rotation.y = currentRotation;
    rendererRef.current.render(sceneRef.current, cameraRef.current);

    if (onCapture) {
      onCapture(rectoImage, versoImage);
    }
  };

  return (
    <div className="flex justify-center items-center w-full">
      <div 
        ref={mountRef} 
        className="h-[520px] w-[846px] rounded-lg overflow-hidden shadow-lg"
      />
    </div>
  );
};