# Now.js Framework - Quick Reference

## 🚀 30 Second Cheat Sheet

### Create Simple Table
```html
<table data-table="myTable" 
       data-source="api/module/action"
       data-default-sort="id desc">
  <thead>
    <tr>
      <th data-field="id" data-sort="id">ID</th>
      <th data-field="name">Name</th>
    </tr>
  </thead>
  <tbody></tbody>
</table>
```

### Create Simple Form
```html
<form data-form="myForm"
      action="api/module/save"
      method="post"
      data-ajax-submit="true"
      data-load-api="api/module/get">
  
  <input type="text" name="name" data-attr="value:name" required>
  <button type="submit">Save</button>
  <input type="hidden" name="id" data-attr="value:id">
</form>
```

### Create Editable Table
```html
<form data-form="settings" 
      action="api/module/save" 
      method="post" 
      data-ajax-submit="true"
      data-load-api="api/module/get">
  
  <table data-table="items"
         data-editable-rows="true"
         data-attr="data:options"
         data-dynamic-columns="true">
    <tbody></tbody>
  </table>
  
  <button type="submit">Save</button>
</form>
```

---

## Common Patterns

### Pattern Selector

```
Question: Do I need to edit data inline?
├─ YES → Editable Table Pattern (categories.html)
└─ NO → Continue...

Question: Is data complex (many fields)?
├─ YES → Complex Form Pattern (profile.html)
└─ NO → Continue...

Question: Many rows of data?
├─ YES → Table + Modal Pattern (users.html)
└─ NO → Simple Table Pattern (users.html)
```

### Table Patterns Quick Reference

| Pattern | When | Example | data-editable-rows | Modal |
|---------|------|---------|-------------------|-------|
| **Simple Table** | Read-only data | users.html | ❌ | Edit button |
| **Editable Table** | Few columns, rows | categories.html | ✅ | ❌ |
| **Table + Modal** | Complex data | leave requests | ❌ | ✅ |
| **Filtered Table** | Need search/filter | languages.html | ❌ | Edit button |

### Form Patterns Quick Reference

| Pattern | When | Example | Complexity |
|---------|------|---------|-----------|
| **Simple Form** | Few fields | category edit | Low |
| **Complex Form** | Many fields, sections | profile.html | High |
| **Dynamic Form** | Based on data | language.html | Medium |

---

## Most Used Data Attributes

### Table Attributes
```html
data-table="name"              <!-- Unique table identifier -->
data-source="api/path"         <!-- API endpoint -->
data-default-sort="col desc"   <!-- Default sorting -->
data-page-size="25"            <!-- Rows per page -->
data-show-checkbox="true"      <!-- Show checkboxes -->
data-search-columns="col1,col2" <!-- Searchable columns -->
data-editable-rows="true"      <!-- Enable inline editing -->
data-dynamic-columns="true"    <!-- Headers from API -->
data-actions='{"delete":"..."}'  <!-- Bulk actions -->
data-row-actions='{...}'       <!-- Per-row buttons -->
```

### Column Attributes
```html
data-field="fieldName"         <!-- Database field -->
data-sort="columnName"         <!-- Sortable column -->
data-filter="true"             <!-- Show filter -->
data-format="datetime"         <!-- Format: datetime, number, lookup -->
data-template="<span>${name}</span>" <!-- Custom HTML -->
data-type="select"             <!-- Filter type: select, text -->
```

### Form Attributes
```html
data-form="formName"           <!-- Unique form identifier -->
data-validate="true"           <!-- Enable validation -->
data-ajax-submit="true"        <!-- Submit via AJAX -->
data-load-api="api/path"       <!-- Load data from API -->
data-load-query-params="true"  <!-- Pass URL query params -->
data-attr="value:fieldName"    <!-- Bind to form data -->
data-if="condition"            <!-- Show if condition true -->
data-options-key="optionName"  <!-- Get options from API -->
```

### Input Attributes
```html
data-attr="value:fieldName"    <!-- Bind input value -->
data-text="fieldName"          <!-- Bind textarea -->
data-element="tags"            <!-- Element type: tags, color, etc -->
data-options-key="name"        <!-- Populate select options -->
data-files="fieldName"         <!-- File upload -->
data-preview="true"            <!-- Show file preview -->
data-attr="value:data['nested'][0]" <!-- Nested data binding -->
```

---

## API Response Format

### Success Response (with data + options)
```json
{
  "success": true,
  "message": "Operation successful",
  "data": {
    "id": 1,
    "name": "John",
    "department": "Sales"
  },
  "options": {
    "department": [
      {"value": "1", "text": "Sales"},
      {"value": "2", "text": "IT"}
    ]
  }
}
```

### Success Response (for list/table)
```json
{
  "success": true,
  "message": "Data retrieved",
  "data": [
    {"id": 1, "name": "John"},
    {"id": 2, "name": "Jane"}
  ]
}
```

### Error Response
```json
{
  "success": false,
  "message": "Validation error",
  "errors": {
    "name": "Name is required",
    "email": "Invalid email"
  }
}
```

### Table Response (with pagination)
```json
{
  "success": true,
  "message": "Data loaded",
  "data": [...],
  "pagination": {
    "page": 1,
    "pageSize": 25,
    "total": 100,
    "totalPage": 4
  }
}
```

---

## Controller Template

### Basic Controller
```php
<?php
namespace Module\MyModule;

use Gcms\Api as ApiController;
use Kotchasan\Http\Request;

class Controller extends \Gcms\Table
{
    protected $allowedSortColumns = ['id', 'name'];

    protected function checkAuthorization(Request $request, $login)
    {
        if (!ApiController::hasPermission($login, ['can_manage_module'])) {
            return $this->errorResponse('Forbidden', 403);
        }
        return true;
    }

    protected function getCustomParams(Request $request, $login): array
    {
        return [
            'filter' => $request->get('filter')->topic()
        ];
    }

    protected function toDataTable($params, $login = null)
    {
        $where = [];
        if ($params['filter'] !== '') {
            $where[] = ['filter_field', $params['filter']];
        }
        
        return \Module\MyModule\Model::toDataTable($where);
    }
}
```

### Save Endpoint
```php
public function save(Request $request)
{
    try {
        ApiController::validateMethod($request, 'POST');
        $this->validateCsrfToken($request);

        $login = $this->authenticateRequest($request);
        if (!$login) {
            return $this->errorResponse('Unauthorized', 401);
        }

        // Get form data
        $data = [
            'name' => $request->post('name')->text(),
            'email' => $request->post('email')->email(),
        ];

        // Validate
        $errors = [];
        if (empty($data['name'])) {
            $errors['name'] = 'Name is required';
        }
        if (empty($data['email'])) {
            $errors['email'] = 'Email is required';
        }

        if (!empty($errors)) {
            return $this->errorResponse('Validation error', 400, null, $errors);
        }

        // Save
        $id = \Module\MyModule\Model::save(
            $request->post('id')->toInt(),
            $data
        );

        return $this->successResponse(
            ['id' => $id],
            'Saved successfully'
        );

    } catch (\Exception $e) {
        return $this->errorResponse($e->getMessage(), 500, $e);
    }
}
```

---

## Model Template

```php
<?php
namespace Module\MyModule;

class Model extends \Kotchasan\Model
{
    public static function toDataTable($where = [])
    {
        return static::createQuery()
            ->select('M.id', 'M.name', 'M.email', 'M.created_at')
            ->from('my_table M')
            ->where($where)
            ->orderBy('M.id DESC');
    }

    public static function get($id)
    {
        return static::createQuery()
            ->from('my_table')
            ->where(['id', $id])
            ->first();
    }

    public static function save($id, $data)
    {
        $db = \Kotchasan\DB::create();

        if ($id) {
            $db->update('my_table', ['id' => $id], $data);
        } else {
            $data['created_at'] = date('Y-m-d H:i:s');
            $db->insert('my_table', $data);
            $id = $db->getInsertId();
        }

        return $id;
    }

    public static function remove($ids)
    {
        return \Kotchasan\DB::create()->delete('my_table', ['id', $ids], 0);
    }
}
```

---

## Common JavaScript Events

```javascript
// When form loads data
document.addEventListener('form:load', (e) => {
  if (e.detail.form === 'myForm') {
    console.log('Form data loaded:', e.detail.data);
  }
});

// When form submits
document.addEventListener('form:submit', (e) => {
  if (e.detail.form === 'myForm') {
    console.log('Form submitted');
  }
});

// When table loads
document.addEventListener('table:load', (e) => {
  if (e.detail.table === 'myTable') {
    console.log('Table loaded:', e.detail.data);
  }
});

// When row action clicked
document.addEventListener('table:rowAction', (e) => {
  if (e.detail.table === 'myTable') {
    console.log('Action:', e.detail.action, 'Row ID:', e.detail.rowId);
  }
});
```

---

## Utility Functions

### Format Data
```php
// In PHP
$text = \Kotchasan\Text::topic($input);  // Sanitize text
$email = \Kotchasan\Validator::email($input);  // Validate email
$date = \Kotchasan\Date::format($timestamp);  // Format date
```

### Query Database
```php
// Select
$result = \Kotchasan\DB::create()
    ->select('*')
    ->from('table')
    ->where(['status', 'active'])
    ->fetch();  // First row
    // .fetchAll();  // All rows

// Insert
\Kotchasan\DB::create()->insert('table', ['name' => 'John']);

// Update
\Kotchasan\DB::create()->update('table', ['id' => 1], ['name' => 'Jane']);

// Delete
\Kotchasan\DB::create()->delete('table', ['id', [1,2,3]], 0);
```

---

## Troubleshooting Quick Guide

### Issue: Form doesn't load data
```
❌ Check: data-load-api endpoint returns correct format
❌ Check: URL has ?id=1 parameter
❌ Check: data-attr="value:fieldName" matches response field name
```

### Issue: Table doesn't show data
```
❌ Check: data-source endpoint returns correct format
❌ Check: data-field="xyz" matches response field name
❌ Check: Browser console for errors
```

### Issue: Form validation doesn't work
```
❌ Check: data-validate="true" is set
❌ Check: input has required attribute
❌ Check: Server returns errors in correct format
```

### Issue: API returns data but form shows old data
```
❌ Check: API response.options has correct structure
❌ Check: Cache headers aren't preventing new data
❌ Check: Browser DevTools Network tab for actual response
```

---

## File Locations Reference

```
Project Structure:
├── modules/{module}/
│   ├── controllers/
│   │   ├── init.php           ← Add menu/permissions
│   │   ├── myaction.php       ← API endpoints
│   │   └── ...
│   └── models/
│       ├── myaction.php       ← Database queries
│       └── ...
├── templates/
│   └── {module}/
│       └── mypage.html        ← HTML template
├── Now/
│   ├── Now.js                 ← Main framework
│   └── js/                    ← Managers
├── Gcms/
│   ├── Controller.php         ← Base functions
│   └── Api.php                ← API base
└── Kotchasan/
    └── Model.php              ← DB base
```

---

## Common Gotchas

1. **data-attr with nested arrays**
   ```html
   <!-- ❌ Wrong -->
   <input data-attr="value:metas.department">
   
   <!-- ✅ Right -->
   <input data-attr="value:metas['department'][0]">
   ```

2. **API response field names case-sensitive**
   ```
   API returns: {"firstName": "John"}
   HTML needs: data-attr="value:firstName"  (not firstName or first_name)
   ```

3. **Table data-field must match API response**
   ```php
   // API returns CONCAT(first_name, last_name) as full_name
   // HTML needs: <th data-field="full_name">Name</th>
   ```

4. **Form data-load-query-params requires URL param**
   ```
   ✅ /edit?id=1  → data-load-api will receive ?id=1
   ❌ /edit       → No param, form won't load data
   ```

5. **CSRF token required for POST**
   ```php
   // Controller must validate
   $this->validateCsrfToken($request);
   ```

---

## Keyboard Shortcuts (if configured)

```
Ctrl+S      Save current form
Ctrl+F      Focus table search
Esc         Close modal
Enter       Submit form
```

---

**Last Updated:** 2026-06-28  
**Framework Version:** Now.js 1.0.0
