# Bongo Page Package - Cursor Rules

## Overview

This is a Laravel package that provides web page management functionality for the Bongo CMS. It allows creating, editing, and managing pages through both admin (backend) and public-facing (frontend) interfaces, with support for image attachments, SEO metadata, custom CSS/JS, and schema markup.

**Package:** `bongo/page` v3.0
**Namespace:** `Bongo\Page`
**Dependencies:** bongo/framework, bongo/image, bongo/menu, bongo/package, bongo/redirect, bongo/setting

## Project Structure

```
src/
├── Commands/
│   └── ExportPagesCommand.php          # Artisan command to export pages to CSV
├── Config/
│   └── page.php                        # Configuration (prefix: 'pages')
├── Events/
│   ├── PageCreated.php                 # Fired when page is created
│   ├── PageUpdated.php                 # Fired when page is updated (triggers menu cache clear, sitemap update)
│   └── PageDeleted.php                 # Fired when page is deleted (triggers menu cache clear, sitemap update)
├── Exports/
│   └── PageExport.php                  # Excel export implementation using Maatwebsite
├── Http/
│   ├── Controllers/
│   │   ├── Api/
│   │   │   └── PageController.php      # API endpoints (sanctum auth)
│   │   ├── Backend/
│   │   │   ├── PageController.php      # Admin CRUD operations
│   │   │   ├── PageDatatableController.php  # Datatable JSON endpoint
│   │   │   └── PageImageController.php # Backend image uploads
│   │   └── Frontend/
│   │       ├── PageController.php      # Public page display + builder editor
│   │       ├── PageImageController.php # Frontend image uploads
│   │       └── PageBackgroundImageController.php  # Background image uploads
│   ├── Requests/
│   │   ├── StorePageRequest.php        # Validation: name (required), slug (unique, max:75)
│   │   └── UpdatePageRequest.php       # Validation for updates
│   ├── Resources/
│   │   └── PageResource.php            # API JSON transformation
│   └── ViewComposers/
│       ├── PageMenuComposer.php        # Injects active menus into dropdown view
│       └── CreatePageComposer.php      # Data for create button partial
├── Migrations/
│   └── 11 migration files              # Database schema (pages table)
├── Models/
│   └── Page.php                        # Main page model with traits
├── Routes/
│   ├── api.php                         # API routes (prefix: api/pages, auth:sanctum)
│   ├── backend.php                     # Admin routes (prefix: /admin/pages)
│   └── frontend.php                    # Public routes (/, /{slug}) + builder routes
├── Seeders/
│   ├── DataSeeder.php                  # Demo page data
│   └── PackageSeeder.php               # Package configuration seeding
├── Views/
│   ├── backend/                        # Admin blade templates
│   ├── frontend/                       # Public-facing templates
│   ├── exports/                        # Export templates
│   └── stubs/                          # Pre-built page templates (privacy, cookies, etc.)
└── PageServiceProvider.php             # Service provider extending AbstractServiceProvider
```

## Architecture Patterns

### Service Provider Bootstrap

The package extends `Bongo\Framework\Providers\AbstractServiceProvider` which automatically:
- Loads config from `src/Config/page.php`
- Registers routes from `src/Routes/` with appropriate middleware
- Loads views from namespace `page::`
- Runs migrations from `src/Migrations/`
- Registers commands, composers, and event listeners defined in protected arrays

```php
// PageServiceProvider.php
protected string $module = 'page';

protected array $commands = [
    ExportPagesCommand::class,
];

protected array $composers = [
    PageMenuComposer::class => ['page::backend.partials.dropdowns.menu'],
    CreatePageComposer::class => ['page::backend.partials.btn_create'],
];

protected array $listeners = [
    PageCreated::class => [],
    PageUpdated::class => [ClearMenuCache::class, UpdateSitemap::class],
    PageDeleted::class => [ClearMenuCache::class, UpdateSitemap::class],
];
```

### Route Organisation

**API Routes** (`src/Routes/api.php`):
- Prefix: `api/pages`
- Named: `api.page.*`
- Middleware: `auth:sanctum`
- Example: `GET /api/pages` → `api.page.index`

**Backend Routes** (`src/Routes/backend.php`):
- Prefix: `/admin/pages` (configurable via `config('page.prefix')`)
- Named: `backend.page.*`
- Middleware: `auth`, `employee`
- CRUD operations: index, create, store, show, edit, update, destroy, duplicate
- Image upload: `POST /admin/pages/{page}/image`

**Frontend Routes** (`src/Routes/frontend.php`):
- Public routes: `/` (homepage), `/{slug}` (page detail)
- Builder routes: `GET /edit/{uuid}`, `POST /update/{uuid}` (employee only)
- Middleware: `hasRedirects`, `hasShortCodes`, `minifyHtml`
- Named: `frontend.index`, `frontend.page.show`, `frontend.page.edit`, etc.

### Page Model Architecture

**Model:** `Bongo\Page\Models\Page`
**Extends:** `Bongo\Framework\Models\AbstractModel`
**Implements:** `Bongo\Image\Interfaces\Imageable`

**Traits Used:**
- `HasContent` - Provides content field handling
- `HasHeaderClass` - Header CSS class functionality
- `HasImages` - Polymorphic image attachments
- `HasKey` - Unique key generation (`formatKey()`)
- `HasSeo` - SEO metadata methods (`meta_title`, `meta_description`, etc.)
- `HasStatus` - Status management (`isActive()`, `isPending()`, etc.)
- `HasUUID` - UUID generation for public identification
- `SoftDeletes` - Soft deletion support

**Status Constants:**
```php
const PENDING = 'pending';
const ACTIVE = 'active';
const INACTIVE = 'inactive';
const INDEX = 'index';
const NO_INDEX = 'noindex';
const ENABLED = 1;
const DISABLED = 0;
```

**Key Methods:**
- `hasMenu(): bool` - Check if page has an associated menu
- `isHomePage(): bool` - Check if page is the homepage
- `duplicate(): self` - Create a duplicate page with new UUID/slug
- `scopeNotHomePage($query)` - Query scope to exclude homepage

**Relations:**
- `menu(): BelongsTo` - Belongs to Menu model (`bongo/menu`)

**Fillable Fields:**
```php
'menu_id', 'name', 'slug', 'content', 'status', 'is_home_page',
'meta_title', 'meta_description', 'meta_canonical', 'meta_index',
'css', 'js', 'schema'
```

### Builder Integration

The package integrates with `bongo/builder` (visual page editor) via:
- Frontend builder route: `GET /edit/{uuid}` → shows `page::frontend.builder` view
- Update endpoint: `POST /update/{uuid}` → processes builder HTML via `BuilderService`
- Image uploads during building: `POST /image/{uuid}`, `POST /background/{uuid}`

```php
// Frontend/PageController.php:update()
$builderService = new BuilderService($page, '/pages/');
$page->content = $builderService->process(); // Processes images in HTML
```

### Package Limit Enforcement

Pages have a configurable limit via `setting('package::page.number_of_pages')`:
- Checked in `create()`, `store()`, `duplicate()` methods
- Returns warning redirect if limit reached
- Prevents creating pages beyond licensed amount

### Event System

**Events fired:**
- `PageCreated` - After successful page creation
- `PageUpdated` - After page update (triggers menu cache clear, sitemap update)
- `PageDeleted` - After page deletion (triggers menu cache clear, sitemap update)

**Listeners registered:**
- `ClearMenuCache` (from `bongo/menu`) - Clears menu cache on update/delete
- `UpdateSitemap` (from `bongo/sitemap`) - Regenerates sitemap on update/delete

## Coding Conventions

### Naming Conventions
- Controllers: `{Entity}Controller`, `{Entity}DatatableController`
- Requests: `Store{Entity}Request`, `Update{Entity}Request`
- Events: `{Entity}{Action}` (PageCreated, PageUpdated, PageDeleted)
- Resources: `{Entity}Resource` (for API transformations)
- Commands: `{Action}{Entity}Command` (ExportPagesCommand)
- Routes: Named as `{section}.{resource}.{action}` (backend.page.show, frontend.page.edit)

### Controller Patterns
- Extend `Bongo\Framework\Http\Controllers\AbstractController`
- Inject model via constructor: `public function __construct(Page $page)`
- Use typed return hints: `: View`, `: RedirectResponse`
- Redirect with flash messages: `->success()`, `->error()`, `->warning()`
- Translations: `trans('page::backend.{key}')`

### Form Request Validation
```php
public function rules(): array
{
    return [
        'name' => 'required',
        'slug' => 'required|max:75|unique:pages,slug,NULL,id,deleted_at,NULL',
    ];
}
```

### View Organisation
- Namespace: `page::`
- Backend views: `page::backend.{view}` (index, create, edit, show)
- Frontend views: `page::frontend.{view}` (home, show, builder, preview)
- Partials: `page::backend.partials.{partial}`
- Stubs: `page::stubs.{stub}` (pre-built page templates)

### Translation Keys
Format: `page::backend.{key}` or `page::frontend.{key}`

Examples:
- `page::backend.store_success`
- `page::backend.update_success`
- `page::backend.delete_success`
- `page::backend.page_limit_reached`
- `page::backend.delete_home_page`

## Common Development Tasks

### Adding a New Controller Action

1. Add route in appropriate file (`backend.php`, `frontend.php`, `api.php`)
2. Create method in controller with proper return type
3. Create/update request class if validation needed
4. Create view if returning HTML
5. Add translation keys if needed

### Adding New Page Attributes

1. Create migration in `src/Migrations/` with timestamp prefix
2. Add field to `$fillable` array in `Page` model
3. Update form validation in `StorePageRequest`/`UpdatePageRequest`
4. Update views (`create.blade.php`, `edit.blade.php`)
5. Update `PageResource` if field should be in API

### Creating New Events

1. Create event class in `src/Events/`
2. Fire event in controller: `event(new YourEvent($page))`
3. Register listeners in `PageServiceProvider::$listeners` array

### Adding View Composers

1. Create composer class in `src/Http/ViewComposers/`
2. Implement `compose(View $view)` method
3. Register in `PageServiceProvider::$composers` array:
```php
protected array $composers = [
    YourComposer::class => ['page::view.name'],
];
```

### Extending Page Functionality with Traits

To add new functionality, create trait in `bongo/framework` or use existing:
1. Use trait in `Page` model
2. Traits provide methods (e.g., `HasSeo` adds `getMetaTitle()`, `getMetaDescription()`)
3. May require migration for additional columns

## Testing

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

### Test Configuration
- Bootstrap: `vendor/autoload.php`
- Test suite in `tests/` directory
- Uses Orchestra Testbench for Laravel package testing
- Database connection: `testing` (in-memory SQLite)

### Code Style (Laravel Pint)
```bash
# Check for style issues
vendor/bin/pint --test

# Fix style issues automatically
vendor/bin/pint
```

## Commands

### Export Pages to CSV
```bash
php artisan page:export
```
Exports all pages to `storage/pages.csv` using Maatwebsite Excel package.

## Key Dependencies

- **bongo/framework** - Base abstracts, traits, service provider
- **bongo/image** - Image handling, builder service, `Imageable` interface
- **bongo/menu** - Menu association, cache clearing listener
- **bongo/package** - Package settings/licensing
- **bongo/redirect** - Redirect middleware for routes
- **bongo/setting** - Settings retrieval (`setting()` helper)

## Important Notes

- Homepage is enforced as unique: `is_home_page = 1`
- Homepage cannot be deleted (validation in `destroy()` method)
- Slugs must be unique (validated with soft deletes consideration)
- Frontend pages redirect to homepage if `is_home_page = 1` but accessed via slug
- Builder uses UUID for public URLs (more secure than sequential IDs)
- Middleware on frontend routes: `hasRedirects`, `hasShortCodes`, `minifyHtml`
- Image uploads integrated with `bongo/image` package
- Package limits enforced via `bongo/package` settings

## Database Schema Key Points

**Main Table:** `pages`

**Key Columns:**
- `id` - Primary key
- `uuid` - Public identifier for builder routes
- `menu_id` - Foreign key to menus table (nullable)
- `key` - Unique internal key (from `HasKey` trait)
- `name` - Page title
- `slug` - URL slug (unique, max 75 chars)
- `content` - HTML content
- `status` - enum: pending, active, inactive
- `is_home_page` - boolean (only one allowed)
- `meta_title`, `meta_description`, `meta_canonical`, `meta_index` - SEO fields
- `css`, `js` - Custom CSS/JS for individual pages
- `schema` - JSON-LD schema markup
- `deleted_at` - Soft delete timestamp

**Indexes:**
- Unique on `slug` (excluding soft deleted)
- Unique on `is_home_page` (ensuring single homepage)

## Quick Reference: Controllers and Actions

| Controller | Route Name | Method | Purpose |
|------------|------------|--------|---------|
| Backend\PageController | backend.page.index | GET | Admin list view |
| Backend\PageController | backend.page.create | GET | Show create form |
| Backend\PageController | backend.page.store | POST | Create new page |
| Backend\PageController | backend.page.show | GET | Show page details |
| Backend\PageController | backend.page.edit | GET | Show edit form |
| Backend\PageController | backend.page.update | POST | Update page |
| Backend\PageController | backend.page.destroy | DELETE | Delete page |
| Backend\PageController | backend.page.duplicate | GET | Duplicate page |
| Backend\PageDatatableController | backend.page.datatable | GET | JSON for datatables |
| Backend\PageImageController | backend.page.image | POST | Upload image |
| Frontend\PageController | frontend.index | GET | Homepage |
| Frontend\PageController | frontend.page.show | GET | Page by slug |
| Frontend\PageController | frontend.page.edit | GET | Builder editor |
| Frontend\PageController | frontend.page.update | POST | Save builder changes |
| Frontend\PageImageController | frontend.page.image | POST | Upload image (builder) |
| Frontend\PageBackgroundImageController | frontend.page.background_image | POST | Upload bg image |
| Api\PageController | api.page.index | GET | API list endpoint |

## Code Style Summary

- PHP 8.2+ with strict types: `declare(strict_types=1);`
- Use typed properties and return types where possible
- Follow PSR-12 via Laravel Pint
- Use Laravel framework features (validation, resources, events, etc.)
- Document complex methods with PHPDoc
- Use constants for magic values (status, relations, booleans)
- Controller methods should be concise (extract to services if complex)
- Always fire events for create/update/delete actions
