# Book Combo Component with Modular Architecture

A professional, reusable Laravel Blade component for searching and selecting books with autocomplete functionality, built with modern ES6 modules and service-based architecture.

## 📋 Table of Contents

- [Features](#features)
- [Architecture](#architecture)
- [Installation](#installation)
- [File Structure](#file-structure)
- [Usage](#usage)
- [Props](#props)
- [Events](#events)
- [Add Resource Service](#add-resource-service)
- [Examples](#examples)
- [Styling](#styling)
- [Security](#security)
- [Troubleshooting](#troubleshooting)

---

## ✨ Features

- **Modular Architecture** - ES6 modules with proper separation of concerns
- **Service-Based** - AddResourceService orchestrates the workflow
- **Autocomplete Search** - Real-time book search with debouncing
- **Keyboard Navigation** - Arrow keys, Enter, Escape support
- **Auto-fill Forms** - Automatically populate related fields
- **Custom Styling** - Bootstrap-compatible styling
- **Event-Driven** - Custom events for component communication
- **Accessible** - Proper labels and ARIA attributes
- **Server Validation** - Works with Laravel validation rules
- **Production Ready** - Professional code structure

---

## 🏗️ Architecture

### Three-Layer Structure

```
Layer 1: BLADE COMPONENT (Presentation)
├── book-combo.blade.php
└── book-combo.css

Layer 2: SERVICES (Business Logic)
├── AddResourceService
│   ├── Orchestrates workflow
│   ├── Manages state
│   └── Coordinates with component
└── Utilities
    ├── FormFiller (form operations)
    ├── APIClient (HTTP communication)
    ├── NotificationManager (user feedback)
    └── Helpers (StringHelper, DateHelper, ArrayHelper)

Layer 3: COMPONENTS (Reusable UI)
├── BookComboComponent
├── ComboHelper (API calls, string ops)
└── ComboValidator (input validation)
```

### How It Works

```
User types in book combo
    ↓
BookComboComponent.handleInput()
    ↓
Debounced search
    ↓
ComboHelper.search() (API call)
    ↓
Display results
    ↓
User selects book
    ↓
AddResourceService.handleBookSelected()
    ↓
Auto-fill form fields (FormFiller)
    ↓
Show notification (NotificationManager)
    ↓
Dispatch event
    ↓
Done!
```

---

## 📦 Installation

### Step 1: Create Directory Structure

```bash
mkdir -p public/assets/js/{components/book-combo/utils,services/{add-resource/utils/{form,api,ui},shared-utils/helpers}}
mkdir -p public/assets/css/components
mkdir -p resources/views/{layouts,components,resources}
```

### Step 2: Copy Files

Copy all JavaScript files to their respective locations in `public/assets/js/`:

```
Components:
- book-combo-component.js → components/book-combo/
- combo-helper.js → components/book-combo/utils/
- combo-validator.js → components/book-combo/utils/

Services:
- add-resource-service.js → services/add-resource/
- form-filler.js → services/add-resource/utils/form/
- api-client.js → services/add-resource/utils/api/
- notification-manager.js → services/add-resource/utils/ui/

Shared Utils:
- string-helper.js → services/shared-utils/helpers/
- date-helper.js → services/shared-utils/helpers/
- array-helper.js → services/shared-utils/helpers/
```

### Step 3: Create Index Files

Create all index.js files (re-export modules):

**components/book-combo/utils/index.js:**
```javascript
export { ComboHelper } from './combo-helper.js';
export { ComboValidator } from './combo-validator.js';
```

**components/book-combo/index.js:**
```javascript
import BookComboComponent from './book-combo-component.js';
export * from './utils/index.js';
export default BookComboComponent;
```

**services/add-resource/utils/form/index.js:**
```javascript
export { FormFiller } from './form-filler.js';
```

**services/add-resource/utils/api/index.js:**
```javascript
export { APIClient } from './api-client.js';
export { ENDPOINTS } from './api-client.js';
```

**services/add-resource/utils/ui/index.js:**
```javascript
export { NotificationManager } from './notification-manager.js';
```

**services/add-resource/utils/index.js:**
```javascript
export * from './form/index.js';
export * from './api/index.js';
export * from './ui/index.js';
```

**services/add-resource/index.js:**
```javascript
import AddResourceService from './add-resource-service.js';
export default AddResourceService;
```

**services/shared-utils/helpers/index.js:**
```javascript
export { StringHelper } from './string-helper.js';
export { DateHelper } from './date-helper.js';
export { ArrayHelper } from './array-helper.js';
```

**services/shared-utils/index.js:**
```javascript
export * from './helpers/index.js';
```

### Step 4: Create CSS File

Copy `book-combo.css` to `public/assets/css/components/`

### Step 5: Create App Entry Point

**public/assets/js/app.js:**
```javascript
import BookComboComponent from './components/book-combo/index.js';
import AddResourceService from './services/add-resource/index.js';
import { StringHelper } from './services/shared-utils/helpers/index.js';
import { DateHelper } from './services/shared-utils/helpers/index.js';
import { ArrayHelper } from './services/shared-utils/helpers/index.js';

window.BookComboComponent = BookComboComponent;
window.AddResourceService = AddResourceService;
window.StringHelper = StringHelper;
window.DateHelper = DateHelper;
window.ArrayHelper = ArrayHelper;

document.addEventListener('DOMContentLoaded', () => {
    if (document.querySelector('[data-book-combo]')) {
        window.addResourceService = new AddResourceService({
            comboName: 'title_name',
            formSelector: 'form',
            acquisitionSection: 'acquisitionSection'
        });
        console.log('✅ Add Resource Service initialized');
    }
});

console.log('✅ App loaded');
```

### Step 6: Update Layout

**resources/views/layouts/app.blade.php:**
```blade
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>@yield('title', 'My App')</title>
    
    <!-- Bootstrap -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <nav class="navbar navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="{{ route('home') }}">My App</a>
        </div>
    </nav>
    
    <main class="container mt-5">
        @yield('content')
    </main>
    
    <!-- Bootstrap JS -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    
    <!-- App Entry Point (type="module" enables ES6 imports) -->
    <script type="module" src="{{ asset('assets/js/app.js') }}"></script>
</body>
</html>
```

### Step 7: Create Blade Component

**resources/views/components/book-combo.blade.php:**
```blade
@props([
    'name' => 'title_name',
    'label' => 'Book',
    'placeholder' => 'Start typing...',
    'required' => false,
    'apiEndpoint' => '/api/reference/lr-details-p/search'
])

<div class="col-md-12 mt-3">
    <label for="{{ $name }}_display" class="form-label">
        {{ $label }}
        @if($required)<span class="text-danger"> * </span>@endif
    </label>

    <div class="book-combo-wrapper" data-book-combo="{{ $name }}">
        <input
            type="text"
            class="form-control book-combo-input"
            id="{{ $name }}_display"
            data-combo-input
            placeholder="{{ $placeholder }}"
            autocomplete="off"
            @if($required) required @endif
        />

        <input
            type="hidden"
            id="{{ $name }}"
            name="{{ $name }}"
            data-combo-value
            @if($required) required @endif
        />

        <div class="book-combo-dropdown" data-combo-dropdown>
            <div data-combo-results></div>
        </div>
    </div>
</div>

@once
    <link rel="stylesheet" href="{{ asset('assets/css/components/book-combo.css') }}">
@endonce
```

---

## 📁 File Structure

```
your-laravel-project/
│
├── public/assets/
│   ├── css/
│   │   └── components/
│   │       └── book-combo.css
│   │
│   └── js/
│       ├── app.js ★ ENTRY POINT
│       ├── components/
│       │   └── book-combo/
│       │       ├── book-combo-component.js
│       │       ├── utils/
│       │       │   ├── combo-helper.js
│       │       │   ├── combo-validator.js
│       │       │   └── index.js
│       │       └── index.js
│       │
│       └── services/
│           ├── add-resource/
│           │   ├── add-resource-service.js
│           │   ├── utils/
│           │   │   ├── form/
│           │   │   │   ├── form-filler.js
│           │   │   │   └── index.js
│           │   │   ├── api/
│           │   │   │   ├── api-client.js
│           │   │   │   └── index.js
│           │   │   ├── ui/
│           │   │   │   ├── notification-manager.js
│           │   │   │   └── index.js
│           │   │   └── index.js
│           │   └── index.js
│           │
│           └── shared-utils/
│               ├── helpers/
│               │   ├── string-helper.js
│               │   ├── date-helper.js
│               │   ├── array-helper.js
│               │   └── index.js
│               └── index.js
│
└── resources/views/
    ├── layouts/
    │   └── app.blade.php
    ├── components/
    │   └── book-combo.blade.php
    └── resources/
        └── add.blade.php (example page)
```

---

## 🚀 Usage

### Basic Usage

```blade
@extends('layouts.app')

@section('content')
<div class="container">
    <h1>Add Resource</h1>
    
    <form id="add-resource-form" method="POST" action="{{ route('resources.store') }}">
        @csrf
        
        <x-book-combo name="title_name" label="Select Book" required />
        
        <!-- Auto-filled fields -->
        <div class="row mt-4">
            <div class="col-md-6">
                <label class="form-label">Author</label>
                <input type="text" id="author" name="author" class="form-control" readonly>
            </div>
            <div class="col-md-6">
                <label class="form-label">Copyright Year</label>
                <input type="text" id="copyrightyear" name="copyrightyear" class="form-control" readonly>
            </div>
        </div>
        
        <button type="submit" class="btn btn-primary mt-5">Save</button>
    </form>
</div>
@endsection
```

---

## 📝 Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `name` | string | `'title_name'` | Input field name and HTML id |
| `label` | string | `'Book'` | Display label |
| `placeholder` | string | `'Start typing...'` | Input placeholder |
| `required` | boolean | `false` | Makes field required |
| `apiEndpoint` | string | `/api/reference/lr-details-p/search` | Search API endpoint |

---

## 🎯 Events

### `book-selected`

Fired when user selects a book.

```javascript
document.addEventListener('resource-book-loaded', (e) => {
    const book = e.detail.book;
    const fullData = e.detail.fullBookData;
    console.log('Book:', book.title, 'by', book.author);
});
```

### `resource-loading`

Fired when loading starts/stops.

```javascript
document.addEventListener('resource-loading', (e) => {
    console.log('Status:', e.detail.status); // 'loading' or 'loaded'
});
```

### `resource-error`

Fired when an error occurs.

```javascript
document.addEventListener('resource-error', (e) => {
    console.error('Error:', e.detail.error);
});
```

---

## 🔧 Add Resource Service

The `AddResourceService` orchestrates the entire workflow:

1. **Initializes** BookComboComponent
2. **Listens** for book selection
3. **Fetches** full book details from API
4. **Auto-fills** form fields (author, year, volume, edition, etc.)
5. **Shows** notifications (success/error)
6. **Scrolls** to acquisition section
7. **Dispatches** custom events

### API Endpoints

The service expects:

**Search endpoint:** `GET /api/reference/lr-details-p/search?q=query`
```json
{
    "books": [
        {
            "id": 1,
            "title": "Book Title",
            "author": "Author",
            "year": 2024
        }
    ]
}
```

**Details endpoint:** `GET /api/reference/lr-details-p/{id}`
```json
{
    "lrDetail": {
        "id": 1,
        "title": "Book Title",
        "author": "Author",
        "year": 2024,
        "volume": "1",
        "edition": "1st",
        "pages": 250,
        "isbn": "123-456",
        "publisher": "Publisher Name",
        "resource_type": "Book",
        "lrlanguage": "English"
    }
}
```

---

## 💡 Examples

### Example 1: Simple Form with Auto-fill

```blade
<form id="resource-form">
    @csrf
    
    <x-book-combo name="book_id" label="Select Book" required />
    
    <input type="text" id="author" name="author" class="form-control mt-3" readonly>
    <input type="text" id="volume" name="volume" class="form-control mt-3">
    
    <button type="submit" class="btn btn-primary mt-3">Submit</button>
</form>
```

### Example 2: Listen to Events

```blade
<script>
    document.addEventListener('resource-book-loaded', (e) => {
        console.log('Book loaded:', e.detail.book);
        // Book details available
    });
    
    document.addEventListener('resource-error', (e) => {
        console.error('Error:', e.detail.error);
        // Handle error
    });
</script>
```

### Example 3: Multiple Instances

```blade
<div class="row">
    <div class="col-md-6">
        <x-book-combo name="primary_book" label="Primary Book" />
    </div>
    <div class="col-md-6">
        <x-book-combo name="reference_book" label="Reference Book" />
    </div>
</div>
```

---

## 🎨 Styling

### CSS Classes

- `.book-combo-wrapper` - Container
- `.book-combo-input` - Search input
- `.book-combo-dropdown` - Dropdown container
- `.book-combo-dropdown.show` - Active dropdown
- `.book-combo-option` - Result item
- `.book-combo-option.active` - Selected item
- `.book-option-title` - Book title
- `.book-option-meta` - Author and year

### Customize Styles

```css
.book-combo-option.active {
    background-color: #e3f2fd;
}

.book-combo-dropdown {
    max-height: 500px;
}

.book-combo-input:focus {
    border-color: #0066ff;
}
```

---

## 🛡️ Security

### Server Validation

Always validate on the server:

```php
$validated = $request->validate([
    'title_name' => 'required|exists:books,id',
]);
```

### Input Sanitization

All titles and authors are HTML-escaped to prevent XSS.

### CSRF Protection

The service automatically includes CSRF tokens in API requests.

---

## 🐛 Troubleshooting

### "BookComboComponent not defined"

**Solution:** Make sure `app.js` is loaded in your layout with `type="module"`:
```blade
<script type="module" src="{{ asset('assets/js/app.js') }}"></script>
```

### Dropdown not showing

**Solution:** Verify:
1. CSS file is loaded: `public/assets/css/components/book-combo.css`
2. Check browser console for errors
3. Verify API endpoint is correct

### Auto-fill not working

**Solution:** Check:
1. API endpoint returns correct JSON format
2. Field IDs match (author, copyrightyear, volume, etc.)
3. Check browser console for service errors

### Search not returning results

**Solution:**
1. Verify API endpoint URL in component props
2. Check API response format matches expected structure
3. Verify API has data matching search query

---

## 📚 Module System

This project uses ES6 modules with a clean architecture:

**Entry Point:** `app.js` imports everything and exposes to `window`

**Components:** Reusable UI components
- `BookComboComponent` - Autocomplete input
- Component utilities for internal logic

**Services:** Business logic orchestrators
- `AddResourceService` - Manages entire workflow
- Service utilities for specific operations

**Shared Utils:** Available everywhere
- `StringHelper` - String manipulation
- `DateHelper` - Date operations
- `ArrayHelper` - Array utilities

---

## ✅ What's Included

✅ Modular ES6 code  
✅ Service-based architecture  
✅ Automatic form filling  
✅ Keyboard navigation  
✅ Custom events  
✅ Professional structure  
✅ Production ready  
✅ Security best practices  

---

## 📄 License

BSIT

---

## 🤝 Contributing

To improve this component:

1. Update JavaScript files in `public/assets/js/`
2. Update CSS in `public/assets/css/components/book-combo.css`
3. Update Blade template in `resources/views/components/book-combo.blade.php`
4. Update this README with changes

---

## 🚀 Quick Start Checklist

- [ ] Create directory structure
- [ ] Copy all JavaScript files
- [ ] Create all index.js files
- [ ] Copy CSS file
- [ ] Create app.js entry point
- [ ] Update layout with @vite or script tag
- [ ] Create Blade component
- [ ] Create API endpoints
- [ ] Test component
- [ ] Verify form auto-fill works

Done! You now have a professional, modular book component system! 🎉

