# Bongo Review Package - Cursor AI Instructions

## Overview

This is a Laravel package for managing customer reviews/testimonials. It provides both backend (admin) and frontend (public) interfaces for creating, managing, and displaying customer reviews with star ratings.

**Key Features:**
- Customer review submission with reCAPTCHA protection
- Admin review management with datatable interface
- Star rating system (1-5 stars)
- Email notifications (to customer and admin)
- Review moderation (pending/active/inactive status)
- Excel export functionality
- Cached statistics (average rating, review count, latest review)
- Honeypot spam protection
- UUID-based public URLs

## Package Information

- **Namespace:** `Bongo\Review`
- **Dependencies:**
  - `bongo/framework` ^3.0 (provides AbstractServiceProvider, AbstractModel, AbstractController)
  - `bongo/captcha` (for reCAPTCHA validation)
  - PHP 8.2+, Laravel 10+
- **Service Provider:** `Bongo\Review\ReviewServiceProvider`

## Project Structure

```
src/
├── Config/
│   └── review.php                           # Configuration (route prefix, reCAPTCHA settings)
├── Http/
│   ├── Controllers/
│   │   ├── Backend/
│   │   │   ├── ReviewController.php         # Admin CRUD operations
│   │   │   └── ReviewDatatableController.php # Datatable API
│   │   └── Frontend/
│   │       └── ReviewController.php         # Public review submission/display
│   ├── Requests/
│   │   ├── StoreFrontendReviewRequest.php   # Frontend validation with reCAPTCHA
│   │   ├── StoreReviewRequest.php           # Backend store validation
│   │   └── UpdateReviewRequest.php          # Backend update validation
│   └── ViewComposers/
│       ├── AverageRatingComposer.php        # Injects average rating into views
│       ├── LatestReviewComposer.php         # Injects latest review into views
│       └── NumberOfReviewsComposer.php      # Injects review count into views
├── Models/
│   └── Review.php                           # Review model with HasContent, HasStatus, HasUUID traits
├── Events/
│   ├── ReviewCreated.php                    # Fired when review created
│   ├── ReviewUpdated.php                    # Fired when review updated
│   └── ReviewDeleted.php                    # Fired when review deleted
├── Listeners/
│   └── ClearReviewCache.php                 # Clears cache when reviews change (queued)
├── Mailables/
│   ├── ReviewMailable.php                   # Email sent to customer
│   └── AdminReviewMailable.php              # Email sent to admin
├── Commands/
│   └── ExportReviewsCommand.php             # Export reviews to CSV
├── Exports/
│   └── ReviewExport.php                     # Excel export handler
├── Migrations/
│   ├── 2021_01_01_000001_create_reviews_table.php
│   ├── 2021_01_01_000002_add_title_column_to_reviews_table.php
│   ├── 2021_01_01_000003_add_email_column_to_reviews_table.php
│   └── 2021_01_01_000004_add_date_column_to_reviews_table.php
├── Views/
│   ├── backend/                             # Admin interface views
│   ├── frontend/                            # Public interface views
│   ├── mail/                                # Email templates (HTML + plain text)
│   └── exports/                             # Excel export template
├── Routes/
│   ├── backend.php                          # Admin routes (/admin/reviews)
│   └── frontend.php                         # Public routes (/reviews)
├── Seeders/
│   └── PackageSeeder.php                    # Seeds package metadata
├── Translations/
│   └── en/
│       ├── backend.php                      # Admin translations
│       └── frontend.php                     # Frontend translations
└── ReviewServiceProvider.php                # Service provider
```

## Architecture Patterns

### Service Provider Bootstrapping

The package extends `Bongo\Framework\Providers\AbstractServiceProvider` which automatically:
- Loads config from `src/Config/review.php`
- Registers routes from `src/Routes/backend.php` and `src/Routes/frontend.php`
- Loads views from `src/Views/review/`
- Runs migrations from `src/Migrations/`
- Registers commands, listeners, and view composers via arrays

```php
class ReviewServiceProvider extends AbstractServiceProvider
{
    protected string $module = 'review';

    protected array $commands = [ExportReviewsCommand::class];

    protected array $composers = [
        NumberOfReviewsComposer::class => ['review::frontend.partials.summary'],
        LatestReviewComposer::class => ['review::frontend.partials.summary'],
        AverageRatingComposer::class => ['review::frontend.partials.summary'],
    ];

    protected array $listeners = [
        ReviewCreated::class => [ClearReviewCache::class],
        ReviewUpdated::class => [ClearReviewCache::class, UpdateSitemap::class],
    ];
}
```

### Route Structure

**Backend routes** (`src/Routes/backend.php`):
- Prefix: `/admin/{config('review.prefix')}` (default: `/admin/reviews`)
- Named: `backend.review.*`
- Middleware: `auth` + `employee` (automatically applied by AbstractServiceProvider)
- Routes: index, create, store, show, edit, update, destroy, datatable

**Frontend routes** (`src/Routes/frontend.php`):
- Prefix: `/{config('review.prefix')}` (default: `/reviews`)
- Named: `frontend.review.*`
- Middleware: Standard web middleware
- Routes: index, show (by UUID), store (with honeypot protection)

### Model Architecture

**Review Model** (`src/Models/Review.php`):
```php
class Review extends AbstractModel
{
    use HasContent;   // Provides content field handling
    use HasStatus;    // Provides status scopes (active(), pending(), inactive())
    use HasUUID;      // Auto-generates UUID on creation
    use SoftDeletes;  // Soft deletion support

    const PENDING = 'pending';
    const ACTIVE = 'active';
    const INACTIVE = 'inactive';

    protected $fillable = ['date', 'title', 'content', 'rating', 'status', 'name', 'email'];
    protected $casts = ['date' => Date::class];
}
```

### Event-Driven Architecture

**Events:**
- `ReviewCreated` - Fired after review creation (both frontend and backend)
- `ReviewUpdated` - Fired after review update
- `ReviewDeleted` - Fired after review deletion

**Listeners:**
- `ClearReviewCache` - Implements `ShouldQueue`, clears three cache keys:
  - `no_of_reviews`
  - `latest_review`
  - `average_rating`

### Caching Strategy

View composers use Laravel's cache to optimize statistics:

```php
// Example from AverageRatingComposer
$averageRating = Cache::remember('average_rating', config('settings.cache_default'), function () {
    return ceil(Review::active()->average('rating'));
});
```

Cache is automatically cleared by `ClearReviewCache` listener on review changes.

### Email Workflow

When a customer submits a review via frontend:
1. Review is created with 'pending' status
2. `ReviewCreated` event fired
3. Two emails sent:
   - `ReviewMailable` → customer email (confirmation)
   - `AdminReviewMailable` → admin email (notification)
4. Redirect to `config('review.success_url')`

## Coding Conventions

### Naming Conventions
- **Models:** Singular (Review)
- **Controllers:** Singular + Controller suffix (ReviewController)
- **Events:** Past tense (ReviewCreated, ReviewUpdated)
- **Listeners:** Action-based (ClearReviewCache)
- **Requests:** Action + Request suffix (StoreFrontendReviewRequest)
- **View Composers:** Purpose + Composer suffix (AverageRatingComposer)

### Return Types
- Controller methods return `View` or `RedirectResponse` explicitly
- Model scopes return `Builder` (inherited from HasStatus trait)
- Event listeners return `void`
- View composers return `void`

### Controller Patterns

**Backend Controller:**
```php
public function store(StoreReviewRequest $request): RedirectResponse
{
    $review = $this->review->create($request->all());
    event(new ReviewCreated($review));

    return redirect()
        ->route('backend.review.show', $review->id)
        ->success(trans('review::backend.store_success'));
}
```

**Frontend Controller:**
```php
public function store(StoreFrontendReviewRequest $request): RedirectResponse
{
    $review = Review::create(Arr::except($request->validated(), ['captcha-response']));
    event(new ReviewCreated($review));

    Mail::to($review->email)->send(new ReviewMailable($review));
    Mail::to(setting("client::company.email"))->send(new AdminReviewMailable($review));

    return redirect()
        ->to(config('review.success_url'))
        ->success(trans('review::frontend.store_success'));
}
```

### Validation Patterns

Frontend requests include reCAPTCHA validation using `Bongo\Captcha\Rules\Captcha`:

```php
public function rules(): array
{
    $recaptchaEnabled = setting()->reCaptchaEnabled() && config('review.recaptcha.enabled');
    $recaptchaMinScore = config('review.recaptcha.min_score', 0.5);

    return [
        'title' => 'required',
        'content' => 'required',
        'rating' => 'required',
        'name' => 'required',
        'email' => 'required|string|email:rfc,dns|max:50',
        'captcha-response' => new Captcha(
            action: 'review',
            minScore: $recaptchaMinScore,
            enabled: $recaptchaEnabled,
        ),
    ];
}
```

## Common Tasks

### Adding a New Review Field

1. **Create migration:**
   ```php
   Schema::table('reviews', function (Blueprint $table) {
       $table->string('new_field')->nullable();
   });
   ```

2. **Add to model fillable:**
   ```php
   protected $fillable = [..., 'new_field'];
   ```

3. **Update form requests:**
   - `StoreReviewRequest::rules()`
   - `StoreFrontendReviewRequest::rules()`
   - `UpdateReviewRequest::rules()`

4. **Update views:**
   - Backend form: `src/Views/backend/partials/form/details.blade.php`
   - Frontend form: `src/Views/frontend/partials/form.blade.php`
   - Display views: `src/Views/backend/show.blade.php`, `src/Views/frontend/show.blade.php`

5. **Update export template:**
   - `src/Views/exports/reviews.blade.php`

### Modifying Cache Behaviour

Cache keys are defined in `ClearReviewCache` listener:
```php
Cache::forget('no_of_reviews');
Cache::forget('latest_review');
Cache::forget('average_rating');
```

To add new cached data:
1. Create new ViewComposer in `src/Http/ViewComposers/`
2. Register in `ReviewServiceProvider::$composers`
3. Add cache key to `ClearReviewCache::handle()`

### Customising Email Templates

Email templates are located in `src/Views/mail/`:
- `review.blade.php` - HTML version for customer
- `review_plain.blade.php` - Plain text version for customer
- `admin_review.blade.php` - HTML version for admin
- `admin_review_plain.blade.php` - Plain text version for admin

Mailable classes:
- `ReviewMailable` - Uses `review` and `review_plain` templates
- `AdminReviewMailable` - Uses `admin_review` and `admin_review_plain` templates

### Adjusting Review Moderation

Review status constants are defined in `Review` model:
```php
const PENDING = 'pending';
const ACTIVE = 'active';
const INACTIVE = 'inactive';
```

Frontend submissions default to 'pending' (set in database migration).

To auto-approve reviews, modify `Frontend/ReviewController::store()`:
```php
$review = Review::create(array_merge(
    Arr::except($request->validated(), ['captcha-response']),
    ['status' => Review::ACTIVE]
));
```

### Working with reCAPTCHA

reCAPTCHA configuration in `src/Config/review.php`:
```php
'recaptcha' => [
    'enabled' => true,      // Enable/disable reCAPTCHA
    'min_score' => 0.5,     // Score threshold (0.1 - 1.0)
],
```

The `Captcha` rule checks both global and package-level settings:
```php
$recaptchaEnabled = setting()->reCaptchaEnabled() && config('review.recaptcha.enabled');
```

## Testing

### Running Tests
```bash
vendor/bin/phpunit
```

### Test Structure
- Base test case: `tests/TestCase.php`
- Database connection: `testing` (configured in phpunit.xml)

### Writing Tests

Create tests in `tests/` directory:
```php
namespace Bongo\Review\Tests;

class ReviewTest extends TestCase
{
    public function test_can_create_review()
    {
        $review = Review::factory()->create([
            'name' => 'John Doe',
            'rating' => 5,
        ]);

        $this->assertDatabaseHas('reviews', [
            'name' => 'John Doe',
            'rating' => 5,
        ]);
    }
}
```

## Artisan Commands

### Export Reviews
```bash
php artisan review:export
```
Exports all reviews to `storage/app/reviews.csv` using the `ReviewExport` class and the `review::exports.reviews` Blade template.

### Code Quality
```bash
# Check code style
vendor/bin/pint --test

# Fix code style
vendor/bin/pint
```

## Configuration

### Config File (`src/Config/review.php`)

```php
return [
    'prefix' => 'reviews',                       // Route prefix
    'success_url' => '/thank-you-for-your-review', // Post-submission redirect
    'recaptcha' => [
        'enabled' => true,                       // Package-level reCAPTCHA toggle
        'min_score' => 0.5,                      // Score threshold (0.1 - 1.0)
    ],
];
```

### Publishing Config
```bash
php artisan vendor:publish --provider="Bongo\Review\ReviewServiceProvider"
```

## Dependencies on Bongo Framework

This package relies on `bongo/framework` for:

### Base Classes
- `AbstractServiceProvider` - Auto-bootstrapping service provider
- `AbstractModel` - Base model with audit fields (created_by, updated_by, deleted_by)
- `AbstractController` - Base controller

### Traits
- `HasContent` - Provides content field handling
- `HasStatus` - Provides status scopes (active(), pending(), inactive())
- `HasUUID` - Auto-generates UUID on model creation

### Casts
- `Date` - Custom date cast used for review date field

### Helpers
- `setting()` - Global settings helper
- `console_print()` - Console output helper

## View Namespacing

Views are namespaced as `review::` and organized by context:
- `review::backend.*` - Admin interface views
- `review::frontend.*` - Public interface views
- `review::mail.*` - Email templates
- `review::exports.*` - Export templates

## Translation Keys

Translations are namespaced as `review::` and organized by context:
- `review::backend.*` - Admin interface translations
- `review::frontend.*` - Frontend interface translations

## Security Features

1. **reCAPTCHA v3** - Prevents automated spam submissions
2. **Honeypot** - Additional spam protection via `Spatie\Honeypot`
3. **Email Validation** - RFC and DNS validation
4. **Route Middleware** - Admin routes protected by `auth` + `employee`
5. **Status Moderation** - Reviews default to 'pending' status
6. **UUID URLs** - Public review URLs use UUID instead of ID

## Extension Points

### Adding Custom Review Types

Create additional status constants or enum types:
```php
const FEATURED = 'featured';
```

Add migration to update enum:
```php
DB::statement("ALTER TABLE reviews MODIFY COLUMN status ENUM('pending', 'active', 'inactive', 'featured')");
```

Add scope to model:
```php
public function scopeFeatured($query)
{
    return $query->where('status', self::FEATURED);
}
```

### Integrating with Other Packages

The package listens for `ReviewUpdated` and fires `UpdateSitemap` (from `bongo/sitemap` package) to keep sitemaps synchronized.

To add custom listeners:
```php
protected array $listeners = [
    ReviewCreated::class => [
        ClearReviewCache::class,
        YourCustomListener::class,
    ],
];
```

### Customising Datatable

Override `ReviewDatatableController` to customize columns, filters, or sorting:
```php
namespace App\Http\Controllers\Backend;

use Bongo\Review\Http\Controllers\Backend\ReviewDatatableController as BaseController;

class ReviewDatatableController extends BaseController
{
    // Override methods here
}
```

Update route in your application routes file to use custom controller.
