Skip to main content

Form Components

Tienda ETCA includes three main form components for user interaction and product management. These forms handle contact submissions, product creation, and product editing.

Overview

Formulario

Contact form for user inquiries

FormularioProducto

Add new products to catalog

FormularioEdicion

Edit existing product details

Formulario (Contact Form)

A simple contact form for user inquiries and messages. File: src/components/Formulario.jsx

Overview

The contact form collects basic user information including name, email, and message. It uses Bootstrap styling for a clean, responsive design.

Component Structure

Formulario.jsx
import React, { useState } from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';

export default function Formulario() {
  const [nombre, setNombre] = useState('');
  const [mensaje, setMensaje] = useState('');
  const [mail, setMail] = useState('');

  function manejarEnvio(evento) {
    evento.preventDefault();
    alert(`Formulario enviado por: ${nombre}`);
  }

  return (
    <form className="container my-4" onSubmit={manejarEnvio}>
      <div className="row g-3">
        <div className="col-md-4">
          <input
            type="text"
            className="form-control"
            value={nombre}
            onChange={(e) => setNombre(e.target.value)}
            placeholder="Nombre"
            required
          />
        </div>
        <div className="col-md-4">
          <input
            type="email"
            className="form-control"
            value={mail}
            onChange={(e) => setMail(e.target.value)}
            placeholder="Correo"
            required
          />
        </div>
        <div className="col-md-4">
          <input
            type="text"
            className="form-control"
            value={mensaje}
            onChange={(e) => setMensaje(e.target.value)}
            placeholder="Mensaje"
            required
          />
        </div>
        <div className="col-12 text-end">
          <button type="submit" className="btn btn-primary">
            Enviar
          </button>
        </div>
      </div>
    </form>
  );
}

Features

Three input fields arranged in a responsive grid:
  • Nombre - User’s name (text input, required)
  • Correo - Email address (email input, required)
  • Mensaje - User’s message (text input, required)
All fields use Bootstrap’s form-control class for consistent styling.
Uses React useState for controlled form inputs:
const [nombre, setNombre] = useState('');
const [mensaje, setMensaje] = useState('');
const [mail, setMail] = useState('');
Each field’s value is controlled by state and updates on change.
Handles form submission with basic alert:
function manejarEnvio(evento) {
  evento.preventDefault();
  alert(`Formulario enviado por: ${nombre}`);
}
Currently displays an alert. In production, this should send data to a backend API.
Uses HTML5 validation:
  • All fields are required
  • Email field uses type="email" for format validation
  • Browser-native validation messages

Layout

The form uses Bootstrap’s grid system:
  • Three columns on medium screens and above (col-md-4)
  • Stacks vertically on smaller screens
  • Submit button aligned to the right

Usage Example

In Contacto Layout
import Formulario from '../components/Formulario';

const Contacto = () => {
  return (
    <>
      <Header />
      <h1>Contacto</h1>
      <Formulario />
      <Footer />
    </>
  );
};

FormularioProducto (Add Product Form)

Modal form for adding new products to the catalog with validation. File: src/components/FormularioProducto.jsx

Overview

A modal-based form that allows administrators to add new products. Includes comprehensive validation and error handling.

Component Structure

FormularioProducto.jsx
import React, { useState } from 'react';
import './style/Modal.css';

function FormularioProducto({ onAgregar, onClose }) {
  const [producto, setProducto] = useState({
    nombre: '',
    precio: '',
    descripcion: '',
  });
  const [errores, setErrores] = useState({});

  const handleChange = (e) => {
    const { name, value } = e.target;
    setProducto({ ...producto, [name]: value });
  };

  const validarFormulario = () => {
    const nuevosErrores = {};
    if (!producto.nombre.trim()) 
      nuevosErrores.nombre = 'El nombre es obligatorio.';
    if (!producto.precio || producto.precio <= 0) 
      nuevosErrores.precio = 'El precio debe ser mayor a 0.';
    if (!producto.descripcion.trim() || producto.descripcion.length < 10)
      nuevosErrores.descripcion = 'La descripción debe tener al menos 10 caracteres.';
    setErrores(nuevosErrores);
    return Object.keys(nuevosErrores).length === 0;
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!validarFormulario()) return;
    onAgregar(producto);
    setProducto({ nombre: '', precio: '', descripcion: '' });
    onClose();
  };

  return (
    <div className="modal-overlay">
      <div className="modal-content p-4 rounded" 
           style={{ maxWidth: '500px', backgroundColor: 'white' }}>
        <button
          type="button"
          className="btn-close float-end"
          aria-label="Close"
          onClick={onClose}
        ></button>

        <form onSubmit={handleSubmit} noValidate>
          <h2 className="mb-4">Agregar Producto</h2>

          <div className="mb-3">
            <label htmlFor="nombre" className="form-label">Nombre:</label>
            <input
              type="text"
              id="nombre"
              name="nombre"
              value={producto.nombre}
              onChange={handleChange}
              className={`form-control ${errores.nombre ? 'is-invalid' : ''}`}
              required
            />
            {errores.nombre && 
              <div className="invalid-feedback">{errores.nombre}</div>}
          </div>

          <div className="mb-3">
            <label htmlFor="precio" className="form-label">Precio:</label>
            <input
              type="number"
              id="precio"
              name="precio"
              value={producto.precio}
              onChange={handleChange}
              min="0"
              className={`form-control ${errores.precio ? 'is-invalid' : ''}`}
              required
            />
            {errores.precio && 
              <div className="invalid-feedback">{errores.precio}</div>}
          </div>

          <div className="mb-3">
            <label htmlFor="descripcion" className="form-label">Descripción:</label>
            <textarea
              id="descripcion"
              name="descripcion"
              value={producto.descripcion}
              onChange={handleChange}
              className={`form-control ${errores.descripcion ? 'is-invalid' : ''}`}
              rows={3}
              required
            />
            {errores.descripcion && 
              <div className="invalid-feedback">{errores.descripcion}</div>}
          </div>

          <button type="submit" className="btn btn-primary w-100">
            Agregar Producto
          </button>
        </form>
      </div>
    </div>
  );
}

export default FormularioProducto;

Props

onAgregar
function
required
Callback function to handle product creation. Receives the product object as a parameter.
onAgregar={(producto) => {
  // Add product to database/state
}}
onClose
function
required
Callback function to close the modal.
onClose={() => setOpen(false)}

Features

Three fields for new product data:
FieldTypeValidation
NombreTextRequired, non-empty
PrecioNumberRequired, must be > 0
DescripciónTextareaRequired, min 10 characters
Custom validation with error messages:
const validarFormulario = () => {
  const nuevosErrores = {};
  if (!producto.nombre.trim()) 
    nuevosErrores.nombre = 'El nombre es obligatorio.';
  if (!producto.precio || producto.precio <= 0) 
    nuevosErrores.precio = 'El precio debe ser mayor a 0.';
  if (!producto.descripcion.trim() || producto.descripcion.length < 10)
    nuevosErrores.descripcion = 'La descripción debe tener al menos 10 caracteres.';
  setErrores(nuevosErrores);
  return Object.keys(nuevosErrores).length === 0;
};
Validation Rules:
  • Name cannot be empty
  • Price must be greater than 0
  • Description must be at least 10 characters
Dynamic error feedback:
  • Error messages appear below invalid fields
  • Uses Bootstrap’s is-invalid class for styling
  • Real-time validation on submit
  • Clear error messages in Spanish

State Management

const [producto, setProducto] = useState({
  nombre: '',
  precio: '',
  descripcion: '',
});

Usage Example

In Admin Layout
import FormularioProducto from '../components/FormularioProducto';
import { useState } from 'react';

const Admin = () => {
  const [open, setOpen] = useState(false);
  
  const agregarProducto = (producto) => {
    // Add product to database
    console.log('Adding product:', producto);
  };
  
  return (
    <>
      <button onClick={() => setOpen(true)}>
        Agregar Producto
      </button>
      
      {open && (
        <FormularioProducto 
          onAgregar={agregarProducto}
          onClose={() => setOpen(false)}
        />
      )}
    </>
  );
};

FormularioEdicion (Edit Product Form)

Modal form for editing existing product information. File: src/components/FormularioEdicion.jsx

Overview

A comprehensive form for editing product details, including all product attributes. Displayed in a modal overlay.

Component Structure

FormularioEdicion.jsx (excerpt)
import React, { useState, useEffect } from 'react';

function FormularioEdicion({ productoSeleccionado, onActualizar, onClose }) {
  const [producto, setProducto] = useState(productoSeleccionado);

  useEffect(() => {
    setProducto(productoSeleccionado);
  }, [productoSeleccionado]);

  const handleChange = (e) => {
    const { name, value } = e.target;
    setProducto({ ...producto, [name]: value });
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    onActualizar(producto);
    onClose();
  };

  return (
    <div className="modal-overlay">
      <div className="modal-content p-4 rounded" 
           style={{ maxWidth: '600px', backgroundColor: 'white' }}>
        <form onSubmit={handleSubmit} noValidate>
          <h2 className="mb-4">Editar Producto</h2>
          {/* Form fields */}
          <button type="submit" className="btn btn-success w-100">
            Actualizar Producto
          </button>
        </form>
      </div>
    </div>
  );
}

Props

productoSeleccionado
object
required
Product object to edit. Contains all product properties:
{
  id: 1,
  nombre: 'Product name',
  precio: 100,
  stock: 10,
  imagen: 'url',
  categoria: 'category',
  descripcion: 'description'
}
onActualizar
function
required
Callback function to handle product update. Receives updated product object.
onActualizar={(producto) => {
  // Update product in database/state
}}
onClose
function
required
Callback function to close the modal.
onClose={() => setOpenEditor(false)}

Form Fields

Basic Product Information
  • ID - Read-only product identifier
  • Nombre - Product name (text, required)
  • Precio - Product price (number, required, min: 0)
  • Stock - Available stock (number, required)

Complete Field List

FieldTypeAttributesRequired
IDnumberreadonly
Nombretext-
Precionumbermin=“0”
Stocknumber-
ImagentextURL
Categoríatext-
Descripcióntextarearows=“4”-

Features

Uses useEffect to update form when selected product changes:
useEffect(() => {
  setProducto(productoSeleccionado);
}, [productoSeleccionado]);
This ensures the form always displays the latest product data.
All form fields are controlled components:
<input
  type="text"
  name="nombre"
  value={producto.nombre || ''}
  onChange={handleChange}
/>
Default empty strings prevent uncontrolled input warnings.
Handles update and closes modal:
const handleSubmit = (e) => {
  e.preventDefault();
  onActualizar(producto);
  onClose();
};

Usage Example

In Admin Layout
import FormularioEdicion from '../components/FormularioEdicion';
import { useState } from 'react';

const Admin = () => {
  const [openEditor, setOpenEditor] = useState(false);
  const [seleccionado, setSeleccionado] = useState(null);
  
  const actualizarProducto = (producto) => {
    // Update product in database
    console.log('Updating product:', producto);
  };
  
  const handleEdit = (product) => {
    setSeleccionado(product);
    setOpenEditor(true);
  };
  
  return (
    <>
      {productos.map(product => (
        <div key={product.id}>
          <button onClick={() => handleEdit(product)}>
            Editar
          </button>
        </div>
      ))}
      
      {openEditor && (
        <FormularioEdicion 
          productoSeleccionado={seleccionado}
          onActualizar={actualizarProducto}
          onClose={() => setOpenEditor(false)}
        />
      )}
    </>
  );
};

Styling

All form components use Bootstrap for base styling, with custom CSS for modal behavior.

CSS Files

/* Used for modal overlay and content */
.modal-overlay {
  /* Overlay styles */
}

.modal-content {
  /* Modal content styles */
}

Form Validation Summary

Formulario

HTML5 Validation
  • Required fields
  • Email format
  • Browser-native messages

FormularioProducto

Custom Validation
  • Name: non-empty
  • Price: greater than 0
  • Description: min 10 chars
  • Spanish error messages

FormularioEdicion

Required Fields
  • All fields required except descripción
  • Number validation for precio/stock
  • No custom validation messages

Best Practices

Form SubmissionAll forms use e.preventDefault() to prevent default browser form submission. This allows for custom handling of form data.
Controlled ComponentsAll form inputs are controlled components, meaning their values are managed by React state. This provides better control and validation capabilities.
Production Considerations
  • Formulario currently uses alert() for feedback - implement proper backend integration
  • Add loading states during form submission
  • Implement proper error handling for API failures
  • Consider adding CSRF protection for production

Components Overview

See all available components

Admin Context

Learn about product management state

Build docs developers (and LLMs) love