# Form Validation Guide

## Overview

The M1 ERP system now includes an enhanced validation system with specific, user-friendly error messages. This guide shows you how to use both server-side and client-side validation.

---

## Server-Side Validation (PHP)

### Basic Usage

In your controller, use the `validate()` method:

```php
$errors = $this->validate($_POST, [
    'email' => 'required|email',
    'first_name' => 'required|min:2|max:100',
    'age' => 'required|integer|between:18,100'
]);

if (!empty($errors)) {
    setOldInput($_POST);
    Session::setFlash('error', implode('<br>', $errors), 'error');
    redirect(base_url('customers/create'));
}
```

### Available Validation Rules

| Rule | Example | Description |
|------|---------|-------------|
| `required` | `required` | Field must have a value |
| `email` | `email` | Must be valid email format |
| `min:n` | `min:5` | Minimum length (characters) |
| `max:n` | `max:255` | Maximum length (characters) |
| `numeric` | `numeric` | Must be a number (int or float) |
| `integer` | `integer` | Must be a whole number |
| `decimal` | `decimal` | Must be a decimal number |
| `url` | `url` | Must be a valid URL |
| `date` | `date` | Must be a valid date |
| `alpha` | `alpha` | Letters only (spaces allowed) |
| `alphanumeric` | `alphanumeric` | Letters and numbers only |
| `in:a,b,c` | `in:active,inactive` | Must be one of specified values |
| `between:min,max` | `between:0,100` | Numeric value in range |
| `gt:n` | `gt:0` | Greater than specified value |
| `lt:n` | `lt:1000` | Less than specified value |

### Custom Error Messages

Provide custom messages for better UX:

```php
$customMessages = [
    'email.required' => 'Please provide an email address for this customer.',
    'email.email' => 'The email address format is invalid. Please use a valid email (e.g., customer@example.com).',
    'first_name.required' => 'First name is required when creating an individual customer.',
    'first_name.min' => 'First name must be at least 2 characters long.',
    'age.between' => 'Age must be between 18 and 100 years.'
];

$errors = $this->validate($_POST, $rules, $customMessages);
```

### Conditional Validation

Validate different fields based on conditions:

```php
$rules = [
    'entity_type' => 'required|in:company,individual'
];

// Add conditional rules
if (isset($_POST['entity_type'])) {
    if ($_POST['entity_type'] === 'company') {
        $rules['company_name'] = 'required|min:2|max:255';
    } elseif ($_POST['entity_type'] === 'individual') {
        $rules['first_name'] = 'required|min:2|max:100';
        $rules['last_name'] = 'required|min:2|max:100';
    }
}

$errors = $this->validate($_POST, $rules, $customMessages);
```

### Displaying Errors

**Option 1: Simple list**
```php
if (!empty($errors)) {
    Session::setFlash('error', implode('<br>', $errors), 'error');
    redirect(base_url('customers/create'));
}
```

**Option 2: Formatted list (recommended)**
```php
if (!empty($errors)) {
    $errorHtml = '<ul class="mb-0">';
    foreach ($errors as $error) {
        $errorHtml .= '<li>' . $error . '</li>';
    }
    $errorHtml .= '</ul>';
    Session::setFlash('error', $errorHtml, 'error');
    redirect(base_url('customers/create'));
}
```

---

## Client-Side Validation (JavaScript)

### Setup

1. Include the validator script in your view:

```php
<script src="<?= asset('js/form-validator.js') ?>"></script>
```

2. Initialize the validator:

```javascript
<script>
document.addEventListener('DOMContentLoaded', function() {
    const validator = new FormValidator('customerForm', {
        email: 'required|email',
        first_name: 'required|min:2|max:100',
        last_name: 'required|min:2|max:100',
        phone: 'numeric',
        website: 'url'
    }, {
        // Custom messages (optional)
        'email.required': 'Please provide an email address.',
        'email.email': 'Please enter a valid email address.',
        'first_name.required': 'First name is required.',
        'phone.numeric': 'Phone number must contain only digits.'
    });
});
</script>
```

### Features

- **Real-time validation**: Validates on blur (when user leaves field)
- **Error clearing**: Clears errors when user starts typing
- **Submit prevention**: Prevents form submission if validation fails
- **Auto-focus**: Focuses first field with error
- **Bootstrap styling**: Uses Bootstrap's `.is-invalid` and `.invalid-feedback` classes

### Example: Complete Form

```html
<form id="customerForm" method="POST" action="<?= base_url('customers/store') ?>">
    <?= csrf_field() ?>
    
    <div class="mb-3">
        <label for="email" class="form-label">Email <span class="text-danger">*</span></label>
        <input type="email" name="email" id="email" class="form-control" value="<?= old('email') ?>">
    </div>
    
    <div class="mb-3">
        <label for="first_name" class="form-label">First Name <span class="text-danger">*</span></label>
        <input type="text" name="first_name" id="first_name" class="form-control" value="<?= old('first_name') ?>">
    </div>
    
    <button type="submit" class="btn btn-primary">Submit</button>
</form>

<script src="<?= asset('js/form-validator.js') ?>"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
    new FormValidator('customerForm', {
        email: 'required|email',
        first_name: 'required|min:2|max:100'
    });
});
</script>
```

---

## Field Name Formatting

The validation system automatically formats field names for better readability:

| Field Name | Formatted As |
|------------|--------------|
| `first_name` | First Name |
| `company_name` | Company Name |
| `customer_id` | Customer ID |
| `api_key` | API Key |
| `vat_number` | VAT Number |
| `po_number` | PO Number |
| `sku_code` | SKU Code |
| `qty` | Quantity |
| `amt` | Amount |

---

## Best Practices

### 1. Always Validate on Server-Side
Client-side validation can be bypassed. Always validate on the server.

### 2. Use Custom Messages for Important Fields
Generic messages are fine for simple fields, but use custom messages for:
- Business-critical fields
- Fields with complex requirements
- Fields that users frequently get wrong

### 3. Provide Helpful Examples
```php
'email.email' => 'Please enter a valid email address (e.g., john@example.com).'
```

### 4. Be Specific About Requirements
```php
'password.min' => 'Password must be at least 8 characters long and include uppercase, lowercase, and numbers.'
```

### 5. Group Related Errors
Display all errors at once so users can fix multiple issues in one go.

---

## Migration Guide

### Before (Generic Messages)
```php
$errors = $this->validate($_POST, [
    'email' => 'required|email'
]);
// Error: "Email is required" or "Email must be a valid email"
```

### After (Specific Messages)
```php
$customMessages = [
    'email.required' => 'Please provide an email address for this customer.',
    'email.email' => 'The email address format is invalid. Please use a valid email (e.g., customer@example.com).'
];

$errors = $this->validate($_POST, [
    'email' => 'required|email'
], $customMessages);
// Error: "Please provide an email address for this customer."
```

---

## Examples from the Codebase

See these files for working examples:
- `controllers/CustomerController.php` - Customer create/update with custom messages
- `public/assets/js/form-validator.js` - Client-side validator class
- `core/Controller.php` - Enhanced validate() method

---

## Testing Your Validation

1. **Test all validation rules**: Try submitting empty, invalid, and valid data
2. **Test custom messages**: Verify your custom messages appear correctly
3. **Test client-side validation**: Disable JavaScript and verify server-side still works
4. **Test edge cases**: Very long strings, special characters, boundary values

---

## Support

For questions or issues with validation:
1. Check this guide
2. Review the examples in `controllers/CustomerController.php`
3. Test with the form-validator.js in your browser console

