# Record Type Watchers

This directory contains record-type-specific form watchers that add custom logic to the DynamicForm component.

## Overview

The watcher system allows you to inject custom behavior into forms based on the record type, without modifying the core DynamicForm component. This keeps the codebase modular and maintainable.

## How It Works

1. **`useRecordTypeWatchers.js`** - The main hook that dynamically imports watchers based on record type
2. **`{recordType}Watchers.js`** - Individual watcher files for each record type (e.g., `workOrderWatchers.js`)

The `useRecordTypeWatchers` hook is called from `DynamicForm.jsx` and automatically loads the appropriate watchers based on the `recordtypestring` prop.

## Creating a New Watcher

### 1. Create the watcher file

Create a new file named `{recordType}Watchers.js`:

```javascript
// resources/js/Form/watchers/timeEntryWatchers.js

export default function timeEntryWatchers({
    fieldValues,
    updateFieldValue,
    updateRecordField,
    allFieldDefinitions,
}) {
    // Your custom logic here
    // Watch for specific field changes and react accordingly
    
    // Example: Auto-populate duration based on start/end time
    const startTime = fieldValues.starttime;
    const endTime = fieldValues.endtime;
    
    if (startTime && endTime) {
        const duration = calculateDuration(startTime, endTime);
        updateFieldValue('duration', duration);
    }
    
    // Return cleanup function if needed
    return () => {
        // Cleanup code (optional)
    };
}
```

### 2. Register the watcher

Add your watcher to the `watcherModules` object in `useRecordTypeWatchers.js`:

```javascript
const watcherModules = {
    'workorder': () => import('./workOrderWatchers'),
    'timeentry': () => import('./timeEntryWatchers'), // Add your watcher here
};
```

### 3. Available Parameters

Your watcher function receives an object with these properties:

- **`fieldValues`**: Object containing all current field values
  - Access values: `fieldValues.fieldname` or `fieldValues['field'].custfield123`
  
- **`updateFieldValue(fieldId, value)`**: Function to update a field value
  - Use for simple fields like text, number, date, etc.
  
- **`updateRecordField(fieldId, value, displayText)`**: Function to update a record field
  - Use for record selector fields (e.g., customer, item)
  - The `displayText` is what shows in the UI
  
- **`allFieldDefinitions`**: Object containing all field metadata
  - Useful for checking field types, options, etc.

## Example: Manufacturing Work Order Watcher

The `workOrderWatchers.js` file demonstrates a complete workflow:

1. **Watch assembly field** - Detects when user selects an assembly
2. **Fetch BOM data** - Calls backend API to get BOM ID and current revision
3. **Populate fields** - Automatically fills in `billofmaterials` and `bomrevision` fields
4. **Check local DB** - Verifies if BOM/revision exist locally
5. **Import if needed** - Triggers import from NetSuite if records are missing

```javascript
// Workflow:
assemblyId → getBomForAssembly() → populate fields → checkBomExists() → importBom()
```

## Best Practices

### 1. Prevent Infinite Loops

Always track previous values to prevent re-triggering on every render:

```javascript
let previousValue = null;

export default function myWatchers({ fieldValues, updateFieldValue }) {
    const currentValue = fieldValues.myfield;
    
    if (currentValue !== previousValue) {
        previousValue = currentValue;
        // Do something
    }
}
```

### 2. Use Processing Flags

Prevent multiple simultaneous API calls:

```javascript
let isProcessing = false;

if (fieldChanged && !isProcessing) {
    isProcessing = true;
    
    await doSomethingAsync()
        .finally(() => {
            isProcessing = false;
        });
}
```

### 3. Handle Errors Gracefully

Always catch and log errors to prevent breaking the form:

```javascript
try {
    const result = await window.SuiteXApi.post('/api/endpoint', data);
    // Handle success
} catch (error) {
    console.error('Error:', error);
    // Optionally show user notification
}
```

### 4. Clear Dependent Fields

When a watched field is cleared, clear its dependent fields:

```javascript
if (!assemblyId && previousAssemblyId !== null) {
    previousAssemblyId = null;
    updateFieldValue('billofmaterials', '');
    updateFieldValue('bomrevision', '');
}
```

## Backend API Requirements

If your watcher needs to call backend APIs:

1. Create methods in the appropriate service (e.g., `App\Services\ManufacturingService`)
2. Add controller methods to expose the endpoints
3. Register routes in `routes/tenant.php`

Example:

```php
// Service
class ManufacturingService
{
    public static function getData($id): array
    {
        // Your logic
        return ['success' => true, 'data' => $data];
    }
}

// Controller
public function getData(Request $request)
{
    $result = ManufacturingService::getData($request->input('id'));
    return response()->json($result);
}

// Route
Route::post('manufacturing/get-data', [ManufacturingController::class, 'getData']);
```

## Debugging

Enable console logging to trace watcher behavior:

```javascript
console.log('🔧 Field changed:', fieldName, fieldValue);
console.log('✅ API call successful:', result);
console.error('❌ Error occurred:', error);
```

The emojis help quickly identify different types of log messages in the browser console.

## Performance Considerations

- Watchers run on every `fieldValues` change
- Keep logic lightweight and async
- Avoid expensive calculations in the watcher itself
- Use debouncing for frequently changing fields if needed

## Questions?

For more examples, see `workOrderWatchers.js` which implements a complete real-world workflow.
