JIT
Convert HTML CSS JS Code to React
<!DOCTYPE html> <html lang="fr"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <title>Inscription Moonlie - Étudiante</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" /> <style> :root { --primary: #6366f1; /* Indigo / Violet */ --secondary: #f472b6; --background: #f8fafc; --surface: #ffffff; --text: #1e293b; --radius: 16px; --transition: all 0.4s cubic-bezier(0.22, 1, 0.36, 1); } * { margin: 0; padding: 0; box-sizing: border-box; font-family: 'Inter', system-ui; -webkit-tap-highlight-color: transparent; } body { background: #1a1a1a; display: flex; justify-content: center; align-items: center; min-height: 100vh; padding: 1rem; } .phone-frame { width: 375px; height: 667px; background: var(--surface); border-radius: 40px; box-shadow: 0 0 0 12px #000, 0 20px 40px rgba(0,0,0,0.3); overflow: hidden; position: relative; } .notch { width: 30%; height: 24px; background: #000; border-radius: 0 0 20px 20px; position: absolute; top: 0; left: 50%; transform: translateX(-50%); z-index: 2; } .container { padding: 2rem 1.5rem; height: calc(100% - 80px); position: relative; overflow: hidden; /* Pour éviter d’éventuels débordements */ } .step { position: absolute; width: calc(100% - 3rem); opacity: 0; transform: translateX(30px); transition: var(--transition); pointer-events: none; } .step.active { opacity: 1; transform: translateX(0); pointer-events: all; } /* Bouton Retour avec l’icône chevron-left */ .back-btn { display: none; position: absolute; left: 1.5rem; top: 3rem; background: none; border: none; color: var(--primary); font-size: 1.2rem; z-index: 1000; cursor: pointer; } .back-btn.visible { display: block; } /* Barre de progression */ .progress-bar { width: calc(100% - 3rem); height: 6px; background: #e0e7ff; border-radius: 12px; margin: 0 auto 2rem; position: relative; overflow: hidden; } .progress-fill { height: 100%; width: 0%; background: linear-gradient( 90deg, #8b5cf6 0%, #6366f1 50%, #ec4899 100% ); border-radius: 12px; transition: width 0.6s cubic-bezier(0.34, 1.56, 0.64, 1); position: relative; } .progress-fill::after { content: ''; position: absolute; top: -50%; right: -20%; width: 40%; height: 200%; background: linear-gradient( to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.4) 50%, rgba(255, 255, 255, 0) 100% ); transform: rotate(15deg); animation: progress-shine 2.5s infinite; } @keyframes progress-shine { 0% { right: -20%; } 100% { right: 120%; } } h2 { font-size: 1.5rem; margin-bottom: 2rem; text-align: center; color: #111827; /* Couleur plus sombre sur fond blanc */ } .input-group { margin-bottom: 1.5rem; } input, select { width: 100%; padding: 1rem; border: 2px solid #e2e8f0; border-radius: var(--radius); font-size: 1rem; transition: var(--transition); } input:focus, select:focus { border-color: var(--primary); outline: none; } /* Bouton Continuer plus réactif */ .continue-btn { position: absolute; bottom: 20px; left: 1.5rem; right: 1.5rem; width: calc(100% - 3rem); padding: 1rem; background: var(--primary); color: white; border: none; border-radius: var(--radius); font-size: 1rem; cursor: pointer; transform: translateY(0); transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease; } .continue-btn:hover { box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3); } .continue-btn:active { transform: scale(0.95); background: #4f46e5; /* Légèrement plus foncé */ } .error-message { color: #dc2626; background: #fee2e2; padding: 12px; border-radius: 8px; margin: 10px 0; border: 1px solid #fca5a5; animation: shake 0.4s; display: none; } @keyframes shake { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } } /* Roues plus grandes - 240px */ .wheel-container { position: relative; height: 240px; /* hauteur élargie */ margin: 1rem 0; overflow: hidden; border-radius: var(--radius); background: #f8fafc; touch-action: none; /* pour gérer le drag plus facilement */ } .wheel { position: absolute; width: 100%; transform-style: preserve-3d; transition: transform 0.2s ease-out; } .wheel-item { height: 60px; display: flex; align-items: center; justify-content: center; font-size: 1rem; color: #64748b; position: relative; will-change: transform, opacity; filter: drop-shadow(0 2px 4px rgba(0,0,0,0.1)); transition: all 0.3s cubic-bezier(0.18, 0.89, 0.32, 1.28); } /* Traits violets dynamiques quand élément sélectionné */ .wheel-item.highlighted { color: var(--primary); transform: scale(1.2); font-weight: 600; z-index: 2; position: relative; } .wheel-item.highlighted::before, .wheel-item.highlighted::after { content: ''; position: absolute; left: 0; right: 0; height: 3px; background: var(--primary); } .wheel-item.highlighted::before { top: -8px; } .wheel-item.highlighted::after { bottom: -8px; } /* Dual wheels */ .dual-wheels { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 24px 0; } /* Centres d’intérêt */ .interest-category { margin-bottom: 1.5rem; } .category-header { display: flex; justify-content: space-between; align-items: center; padding: 1rem; background: #f8fafc; border-radius: var(--radius); cursor: pointer; transition: var(--transition); position: relative; border: 2px solid #e2e8f0; } .category-header.validated { border-color: var(--primary); } .category-header.unvalidated { border-color: #ef4444; animation: shake 0.4s; } .validation-status { position: absolute; right: 40px; color: var(--primary); opacity: 0; transition: var(--transition); } .validated .validation-status { opacity: 1; } .subcategories { display: none; flex-wrap: wrap; gap: 0.5rem; margin-top: 1rem; padding-left: 0.5rem; } .subcategory-tag { background: var(--surface); border: 2px solid #e2e8f0; border-radius: 2rem; padding: 0.5rem 1rem; cursor: pointer; transition: var(--transition); font-size: 0.9rem; } .subcategory-tag.selected { background: var(--primary); color: white; border-color: var(--primary); } /* Localisation */ .location-step { text-align: center; } .location-icon { font-size: 3rem; color: var(--primary); margin-bottom: 2rem; } .enable-location-btn { background: var(--primary); color: white; border: none; border-radius: 12px; padding: 1rem 2rem; margin-top: 2rem; cursor: pointer; position: relative; transition: box-shadow 0.2s ease, transform 0.2s ease; } .enable-location-btn:hover { box-shadow: 0 4px 8px rgba(99, 102, 241, 0.3); } .enable-location-btn:active { transform: scale(0.95); background: #4f46e5; /* Légèrement plus foncé */ } .enable-location-btn.clicked { animation: pressAnimation 0.2s forwards; } @keyframes pressAnimation { 0% { transform: scale(1); } 50% { transform: scale(0.95); } 100% { transform: scale(1); } } .warning-text { color: #ef4444; font-size: 0.9rem; text-align: center; margin-top: 1rem; } /* Vérification */ .verification-options { display: flex; flex-direction: column; gap: 1rem; } .upload-box { border: 2px dashed #e2e8f0; padding: 2rem; text-align: center; border-radius: var(--radius); cursor: pointer; transition: var(--transition); } .upload-box:hover { border-color: var(--primary); } .verification-toggle { display: flex; align-items: center; gap: 1rem; justify-content: center; } .switch { position: relative; display: inline-block; width: 50px; height: 24px; } .switch input { opacity: 0; width: 0; height: 0; } .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #ccc; transition: 0.4s; border-radius: 24px; } .slider:before { position: absolute; content: ''; height: 18px; width: 18px; left: 3px; bottom: 3px; background-color: white; transition: 0.4s; border-radius: 50%; } input:checked + .slider { background-color: var(--primary); } input:checked + .slider:before { transform: translateX(26px); } </style> </head> <body> <div class="phone-frame"> <div class="notch"></div> <div class="container"> <!-- Barre de progression --> <div class="progress-bar"> <div class="progress-fill" id="progressFill" style="width: 0%"></div> </div> <!-- Bouton retour (fa-chevron-left) --> <button class="back-btn" id="backBtn"> <i class="fas fa-chevron-left"></i> </button> <!-- Étape 1 : Création de compte --> <div class="step active" data-step="1"> <h2>Crée ton compte</h2> <div class="input-group"> <input type="email" placeholder="Adresse email" required /> </div> <div class="input-group"> <input type="text" placeholder="Nom complet" required /> </div> <div class="input-group"> <input type="password" id="password" placeholder="Mot de passe" required /> </div> <div class="input-group"> <input type="password" id="confirmPassword" placeholder="Confirmer le mot de passe" required /> </div> <div class="error-message" id="passwordError"></div> </div> <!-- Étape 2 : Établissement --> <div class="step" data-step="2"> <h2>Ton établissement</h2> <div class="wheel-container"> <div class="wheel" id="schoolWheel"> <div class="wheel-item">Université</div> <div class="wheel-item">École de commerce</div> <div class="wheel-item">École d'ingénieur</div> <div class="wheel-item">DUT/BTS</div> <div class="wheel-item">Autre</div> </div> </div> <div class="input-group" style="margin-top: 1.5rem;"> <input type="text" placeholder="Nom de l'établissement" required /> </div> </div> <!-- Étape 3 : Formation --> <div class="step" data-step="3"> <h2>Ta formation</h2> <div class="dual-wheels"> <div class="wheel-container"> <div class="wheel" id="domainWheel"> <div class="wheel-item">Mathématiques</div> <div class="wheel-item">Économie</div> <div class="wheel-item">Ingénierie</div> <div class="wheel-item">Informatique</div> <div class="wheel-item">Biologie</div> </div> </div> <div class="wheel-container"> <div class="wheel" id="levelWheel"> <div class="wheel-item">L1</div> <div class="wheel-item">L2</div> <div class="wheel-item">L3</div> <div class="wheel-item">M1</div> <div class="wheel-item">M2</div> <div class="wheel-item">Doctorat</div> </div> </div> </div> </div> <!-- Étape 4 : Centres d'intérêt --> <div class="step" data-step="4"> <h2>Tes centres d'intérêt</h2> <div class="error-message" id="interestError"> Veuillez sélectionner au moins un intérêt dans chaque catégorie ! </div> <!-- Musique --> <div class="interest-category"> <div class="category-header"> <span>🎵 Musique</span> <i class="fas fa-chevron-down"></i> <i class="fas fa-check-circle validation-status"></i> </div> <div class="subcategories"> <div class="subcategory-tag">Rock</div> <div class="subcategory-tag">Electro</div> <div class="subcategory-tag">Hip-Hop</div> <div class="subcategory-tag">Classique</div> <div class="subcategory-tag">Jazz</div> <div class="subcategory-tag">Rap</div> <div class="subcategory-tag">Techno</div> <div class="subcategory-tag">Reggae</div> </div> </div> <!-- Sport --> <div class="interest-category"> <div class="category-header"> <span>⚽ Sport</span> <i class="fas fa-chevron-down"></i> <i class="fas fa-check-circle validation-status"></i> </div> <div class="subcategories"> <div class="subcategory-tag">Football</div> <div class="subcategory-tag">Basketball</div> <div class="subcategory-tag">Tennis</div> <div class="subcategory-tag">Rugby</div> <div class="subcategory-tag">Natation</div> <div class="subcategory-tag">Escalade</div> <div class="subcategory-tag">Cyclisme</div> <div class="subcategory-tag">Musculation</div> </div> </div> <!-- Technologie --> <div class="interest-category"> <div class="category-header"> <span>💻 Technologie</span> <i class="fas fa-chevron-down"></i> <i class="fas fa-check-circle validation-status"></i> </div> <div class="subcategories"> <div class="subcategory-tag">Intelligence Artificielle</div> <div class="subcategory-tag">Robotique</div> <div class="subcategory-tag">Gaming</div> <div class="subcategory-tag">Cybersécurité</div> <div class="subcategory-tag">Développement Web</div> <div class="subcategory-tag">Blockchain</div> <div class="subcategory-tag">Réalité Virtuelle</div> <div class="subcategory-tag">IoT (Internet des Objets)</div> </div> </div> </div> <!-- Étape 5 : Localisation --> <div class="step" data-step="5"> <div class="location-step"> <i class="fas fa-map-marker-alt location-icon"></i> <h2>Activer la localisation</h2> <button class="enable-location-btn" id="locationBtn"> Autoriser l'accès </button> <p class="warning-text">Modifiable dans les paramètres</p> </div> </div> <!-- Étape 6 : Vérification --> <div class="step" data-step="6"> <h2>Vérification</h2> <div class="verification-options"> <div class="upload-box" id="uploadBox"> <i class="fas fa-file-upload fa-2x"></i> <p>Déposez votre carte étudiante ou certificat</p> <input type="file" id="verificationFile" hidden accept=".pdf,.jpg,.png" /> </div> <div class="verification-toggle"> <label class="switch"> <input type="checkbox" id="postponeVerification" /> <span class="slider"></span> </label> <p>Vérifier plus tard</p> </div> <p class="warning-text"> ⚠️ Accès limité aux événements privés sans vérification </p> </div> </div> <!-- Bouton Continuer --> <button class="continue-btn" id="continueBtn">Continuer</button> </div> </div> <script> let currentStep = 1; const totalSteps = 6; const steps = document.querySelectorAll('.step'); const continueBtn = document.getElementById('continueBtn'); const backBtn = document.getElementById('backBtn'); const progressFill = document.getElementById('progressFill'); // ----------------------------------- // FONCTIONS DE VALIDATION // ----------------------------------- function validatePassword() { const password = document.getElementById('password').value; const confirm = document.getElementById('confirmPassword').value; const errorElement = document.getElementById('passwordError'); if (!password || !confirm) { errorElement.textContent = "Veuillez remplir tous les champs"; errorElement.style.display = 'block'; return false; } if (password !== confirm) { errorElement.textContent = "Les mots de passe ne correspondent pas"; errorElement.style.display = 'block'; errorElement.style.animation = 'shake 0.4s'; return false; } errorElement.style.display = 'none'; return true; } function validateSchool() { const schoolName = document.querySelector('[data-step="2"] input').value.trim(); const wheelHighlighted = document.querySelector('#schoolWheel .highlighted'); return schoolName.length > 0 && !!wheelHighlighted; } function validateFormation() { const domainHighlighted = document.querySelector('#domainWheel .highlighted'); const levelHighlighted = document.querySelector('#levelWheel .highlighted'); return !!domainHighlighted && !!levelHighlighted; } function checkAllValidated() { const allCategories = document.querySelectorAll('.interest-category'); let allValid = true; allCategories.forEach(category => { if (!category.querySelector('.subcategory-tag.selected')) { category.querySelector('.category-header').classList.add('unvalidated'); allValid = false; } else { category.querySelector('.category-header').classList.remove('unvalidated'); category.querySelector('.category-header').classList.add('validated'); } }); return allValid; } function showInterestError() { const interestError = document.getElementById('interestError'); interestError.style.display = 'block'; interestError.style.animation = 'shake 0.4s'; } async function validateCurrentStep() { switch (currentStep) { case 1: return validatePassword(); case 2: return validateSchool(); case 3: return validateFormation(); case 4: if (!checkAllValidated()) { showInterestError(); return false; } return true; default: return true; } } // ----------------------------------- // FONCTIONS DE NAVIGATION // ----------------------------------- function updateUI() { // Bouton retour visible sauf à l'étape 2 backBtn.classList.toggle('visible', currentStep > 1 && currentStep !== 2); const progressPercent = ((currentStep - 1) / (totalSteps - 1)) * 100; progressFill.style.width = `${progressPercent}%`; steps.forEach(step => { const stepNumber = parseInt(step.dataset.step); step.classList.toggle('active', stepNumber === currentStep); }); } function finishRegistration() { alert("Inscription terminée !"); } continueBtn.addEventListener('click', async () => { if (!(await validateCurrentStep())) return; if (currentStep < totalSteps) { currentStep++; updateUI(); } else { finishRegistration(); } }); backBtn.addEventListener('click', () => { if (currentStep > 1 && currentStep !== 2) { currentStep--; updateUI(); } }); // swipe horizontal (optionnel) let touchStartX = 0; document.addEventListener('touchstart', e => { touchStartX = e.touches[0].clientX; }); document.addEventListener('touchend', e => { const touchEndX = e.changedTouches[0].clientX; const deltaX = touchStartX - touchEndX; if (Math.abs(deltaX) > 50) { if (deltaX > 0 && currentStep < totalSteps) { currentStep++; } else if (deltaX < 0 && currentStep > 1 && currentStep !== 2) { currentStep--; } updateUI(); } }); // Pour masquer l’erreur mot de passe dès qu’on tape document.querySelectorAll('#password, #confirmPassword').forEach(input => { input.addEventListener('input', () => { const errorElement = document.getElementById('passwordError'); errorElement.style.display = 'none'; errorElement.style.animation = ''; }); }); // Gestion des catégories d’intérêts document.querySelectorAll('.category-header').forEach(header => { header.addEventListener('click', () => { const subcats = header.nextElementSibling; subcats.style.display = subcats.style.display === 'flex' ? 'none' : 'flex'; header .querySelector('i.fa-chevron-down') ?.classList.toggle('fa-chevron-up'); }); }); document.querySelectorAll('.subcategory-tag').forEach(tag => { tag.addEventListener('click', () => { tag.classList.toggle('selected'); const category = tag.closest('.interest-category'); const header = category.querySelector('.category-header'); if (category.querySelector('.subcategory-tag.selected')) { header.classList.add('validated'); header.classList.remove('unvalidated'); } else { header.classList.remove('validated'); } const interestError = document.getElementById('interestError'); if (checkAllValidated()) { interestError.style.display = 'none'; interestError.style.animation = ''; } }); }); // Vérification retardée document.getElementById('postponeVerification').addEventListener('change', e => { const uploadBox = document.getElementById('uploadBox'); uploadBox.style.opacity = e.target.checked ? 0.5 : 1; uploadBox.style.pointerEvents = e.target.checked ? 'none' : 'all'; }); // ----------------------------------- // CLASSE WHEELPICKER // ----------------------------------- class WheelPicker { constructor(container) { this.container = container; this.items = Array.from(container.children); this.itemHeight = 60; // correspond à .wheel-item { height: 60px } this.isDragging = false; this.startY = 0; this.currentPos = this.calculateInitialPosition(); this.init(); this.update(); // Force la mise à jour initiale } calculateInitialPosition() { const containerHeight = this.container.parentElement.clientHeight; return (containerHeight / 2) - (this.itemHeight / 2); } init() { this.addEventListeners(); this.container.parentElement.addEventListener('wheel', e => e.preventDefault()); } addEventListeners() { this.container.addEventListener('touchstart', e => this.handleStart(e)); this.container.addEventListener('touchmove', e => this.handleMove(e)); this.container.addEventListener('touchend', () => this.handleEnd()); this.container.addEventListener('mousedown', e => this.handleStart(e)); document.addEventListener('mousemove', e => this.handleMove(e)); document.addEventListener('mouseup', () => this.handleEnd()); } handleStart(e) { this.isDragging = true; this.startY = this.getClientY(e); this.container.style.transition = 'none'; e.preventDefault(); } handleMove(e) { if (!this.isDragging) return; const y = this.getClientY(e); const delta = y - this.startY; this.currentPos += delta; this.startY = y; this.applyBoundaries(); this.update(); } handleEnd() { this.isDragging = false; this.snapToClosest(); } getClientY(e) { return e.clientY || (e.touches && e.touches[0].clientY) || 0; } applyBoundaries() { const maxPos = this.calculateInitialPosition(); const minPos = -this.itemHeight * (this.items.length - 1) + maxPos; this.currentPos = Math.min(maxPos, Math.max(minPos, this.currentPos)); } snapToClosest() { const containerHeight = this.container.parentElement.clientHeight; const targetIndex = Math.round( (containerHeight / 2 - this.currentPos - this.itemHeight / 2) / this.itemHeight ); this.currentPos = containerHeight / 2 - targetIndex * this.itemHeight - this.itemHeight / 2; this.container.style.transition = 'transform 0.6s cubic-bezier(0.23, 1, 0.32, 1)'; this.update(); } update() { this.container.style.transform = `translateY(${this.currentPos}px)`; this.highlightCenterItem(); } highlightCenterItem() { const containerCenter = this.container.parentElement.clientHeight / 2; this.items.forEach(item => { const itemTop = item.offsetTop + this.currentPos; const itemCenter = itemTop + this.itemHeight / 2; const isHighlighted = Math.abs(itemCenter - containerCenter) < this.itemHeight / 3; item.classList.toggle('highlighted', isHighlighted); }); } } // ----------------------------------- // INITIALISATION + BOUTON LOCALISATION // ----------------------------------- document.addEventListener('DOMContentLoaded', () => { updateUI(); // Roulettes new WheelPicker(document.getElementById('schoolWheel')); new WheelPicker(document.getElementById('domainWheel')); new WheelPicker(document.getElementById('levelWheel')); // Bouton localisation dynamique const locationBtn = document.getElementById('locationBtn'); locationBtn.addEventListener('click', () => { // Petit effet visuel locationBtn.classList.add('clicked'); setTimeout(() => { locationBtn.classList.remove('clicked'); }, 200); // Confirmation const userResponse = confirm("Voulez-vous autoriser l'accès à votre localisation ?"); if (userResponse) { alert("Localisation activée !"); // Ici vous pouvez inscrire une logique supplémentaire si nécessaire } else { alert("Accès refusé."); } }); }); </script> </body> </html> convertir ce code html css java en react

Okay, let's convert this HTML, CSS, and JavaScript registration form into a React application.

We'll break it down into several components for better organization and maintainability.

Explanation:

  1. Project Setup (index.html, index.css, index.jsx, App.jsx, App.css): Basic React setup. index.html includes the Font Awesome CDN link. App.jsx renders the phone frame and the main RegistrationForm component.
  2. RegistrationForm.jsx (Core Logic):
    • Manages currentStep and formData using useState.
    • handleDataChange and handleInterestChange update the form state. useCallback is used for optimization.
    • Includes validation functions (validateStep1, validateStep2, etc.) for each step.
    • handleNext performs validation based on the current step. If valid, it proceeds to the next step or shows the final alert. If invalid, it sets errors using setErrors.
    • handleBack navigates to the previous step.
    • renderStep conditionally renders the component for the current step.
    • Renders the ProgressBar, back button, the current step's component, and the continue/finish button.
  3. ProgressBar.jsx: A simple component to display the progress based on currentStep and totalSteps.
  4. ErrorMessage.jsx: Displays validation error messages with a shake animation.
  5. Step Components (Step1Account to Step6Verification):
    • Each component receives data, onChange, and errors as props.
    • They render the specific inputs/UI for that step.
    • Inputs call the onChange prop to update the central formData state in RegistrationForm.
    • They use the ErrorMessage component to display relevant errors passed down via props.
  6. WheelPicker.jsx:
    • This is the most complex component, replicating the scrolling wheel selector.
    • Uses useState for dragging state (isDragging), positions (startY, currentPos, startPos), and the selectedIndex.
    • Uses useRef to access DOM elements (containerRef, wheelRef).
    • Uses useEffect to initialize position, update the wheel's transform: translateY, and highlight the centered item.
    • Handles touch (onTouchStart, onTouchMove, onTouchEnd) and mouse (onMouseDown, global mousemove/mouseup/mouseleave listeners added/removed via useEffect) events for dragging.
    • snapToClosest calculates the nearest item index and animates the wheel to center it on drag end, calling the onChange prop with the selected value.
    • Includes a highlight-indicator element for the visual center lines.
    • Accepts hasError prop to apply error styling.
  7. InterestCategory.jsx & Step4Interests.jsx:
    • Step4Interests maps over the interestData and renders an InterestCategory for each.
    • InterestCategory manages its open/closed state (isOpen). It renders the header (clickable to toggle) and the list of subcategory-tag buttons. Clicking a tag calls the onChange prop passed down from RegistrationForm. It also shows validation status (check icon) and error state.
  8. SwitchToggle.jsx & Step6Verification.jsx:
    • SwitchToggle is a reusable component for the styled checkbox.
    • Step6Verification uses SwitchToggle for the "verify later" option. It includes a hidden file input (useRef) and a styled upload-box div that triggers the file input click. It disables the upload box if "verify later" is checked.

This structure separates concerns, making the code more organized and easier to manage than the original monolithic HTML/JS file. Remember to install any necessary dependencies (React itself).*

Updated files
v1
peux tu m'envoyer le code integral en 2 fichier jsx et css

Okay, here is the consolidated code in two files: App.jsx for all JavaScript/React logic and components, and App.css for all the styles.

Key Changes:

  1. src/App.css: Contains all CSS rules previously spread across multiple files.
  2. src/App.jsx:
    • Imports React, hooks, ReactDOM, and the single App.css.
    • Defines all components (ErrorMessage, ProgressBar, Step1Account, WheelPicker, Step2School, Step3Formation, InterestCategory, Step4Interests, Step5Location, SwitchToggle, Step6Verification, RegistrationForm, App) in order.
    • The RegistrationForm component holds the main state and logic. Step components receive data and callbacks via props.
    • The ReactDOM.createRoot(...).render(<App />) call is at the very end to render the application after all components are defined.
    • Step components now check data.currentStep === N to apply the active class, as currentStep is passed down within the data prop.*
Updated files
v2