Simple Checkout with State Errors

by oktaviardi pratama

HTML

<div class="container" id="checkoutForm">
        <h1>Checkout</h1>
        
        <form id="orderForm">
            <div class="form-group">
                <label for="name">Full Name *</label>
                <input type="text" id="name" name="name" placeholder="John Doe">
                <div class="error-message" id="nameError">Please enter your name</div>
            </div>

            <div class="form-group">
                <label for="phone">Phone Number *</label>
                <input type="tel" id="phone" name="phone" placeholder="+1 (555) 123-4567">
                <div class="error-message" id="phoneError">Please enter a valid phone number</div>
            </div>

            <div class="form-group">
                <label for="address">Delivery Address *</label>
                <textarea id="address" name="address" placeholder="123 Main Street, City, State, ZIP"></textarea>
                <div class="error-message" id="addressError">Please enter your delivery address</div>
            </div>

            <div class="product-section">
                <div class="product-title">Product</div>
                
                <div class="product-item">
                    <div class="product-info">
                        <div class="product-name">Premium Wireless Headphones</div>
                        <div class="product-price">$79.99</div>
                    </div>
                    <div class="quantity-control">
                        <button type="button" class="qty-btn" id="decreaseBtn" onclick="updateQuantity(-1)">−</button>
                        <span class="qty-value" id="quantity">1</span>
                        <button type="button" class="qty-btn" id="increaseBtn" onclick="updateQuantity(1)">+</button>
                    </div>
                </div>

                <div class="total-section">
                    <div class="total-row">
                        <span>Total:</span>
      ...

CSS

* {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            background-color: #f5f5f5;
            padding: 20px;
        }

        .container {
            max-width: 600px;
            margin: 0 auto;
            background-color: white;
            border-radius: 8px;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            padding: 30px;
        }

        h1 {
            text-align: center;
            color: #333;
            margin-bottom: 30px;
        }

        .form-group {
            margin-bottom: 20px;
        }

        label {
            display: block;
            font-weight: 600;
            color: #555;
            margin-bottom: 8px;
            font-size: 14px;
        }

        input[type="text"],
        input[type="tel"],
        textarea {
            width: 100%;
            padding: 12px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 14px;
            transition: border-color 0.3s;
        }

        input[type="text"]:focus,
        input[type="tel"]:focus,
        textarea:focus {
            outline: none;
            border-color: #4CAF50;
        }

        input.error,
        textarea.error {
            border-color: #f44336;
        }

        textarea {
            resize: vertical;
            min-height: 80px;
        }

        .error-message {
            color: #f44336;
            font-size: 12px;
            margin-top: 5px;
            display: none;
        }

        .error-message.show {
            display: block;
        }

        .product-section {
            background-color: #f9f9f9;
            padding: 20px;
            border-radius: 6px;
            margin: 30px 0;
        }

        .product-title {
      ...

JavaScript

// State management
        const state = {
            quantity: 1,
            pricePerUnit: 79.99,
            errors: {
                name: '',
                phone: '',
                address: ''
            }
        };

        // Update quantity
        function updateQuantity(change) {
            state.quantity += change;
            
            if (state.quantity < 1) {
                state.quantity = 1;
            }
            
            document.getElementById('quantity').textContent = state.quantity;
            
            const total = (state.quantity * state.pricePerUnit).toFixed(2);
            document.getElementById('totalPrice').textContent = '$' + total;
            
            document.getElementById('decreaseBtn').disabled = state.quantity === 1;
        }

        // Validation functions
        function validateName(value) {
            return value.trim() ? '' : 'Please enter your name';
        }

        function validatePhone(value) {
            const phoneRegex = /^[\d\s\-\+\(\)]+$/;
            return value.trim() && phoneRegex.test(value) && value.replace(/\D/g, '').length >= 10 
                ? '' 
                : 'Please enter a valid phone number';
        }

        function validateAddress(value) {
            return value.trim() ? '' : 'Please enter your delivery address';
        }

        // Set error state and update UI
        function setError(field, message) {
            state.errors[field] = message;
            
            const input = document.getElementById(field);
            const errorElement = document.getElementById(field + 'Error');
            
            if (message) {
                input.classList.add('error');
                errorElement.textContent = message;
                errorElement.classList.add('show');
            } else {
                input.classList.remove('error');
               ...