# Bongo Project Package - Cursor Rules

## Project Overview

The **Bongo Project** package provides a complete project portfolio management system for Laravel applications, enabling administrators to create, manage, and display projects with categories, images, and custom content. This is a client-specific package within the Bongo framework ecosystem.

### Key Features
- Projects with categories (many-to-many relationship)
- Image management integration via `bongo/image`
- Frontend builder support for WYSIWYG content editing
- SEO metadata management (title, description, canonical, index/noindex)
- Custom CSS/JS per project and category
- Header customisation (transparent/sticky options)
- Soft deletes and audit trails
- Event-driven architecture with listeners
- Datatable support for admin listings
- API endpoints with resource transformations
- Related projects functionality
- Project duplication
- Schema.org structured data support

### Tech Stack
- **PHP**: 8.2+
- **Laravel**: 10+
- **Framework**: Extends `bongo/framework` AbstractServiceProvider
- **Dependencies**: `bongo/image`, `bongo/menu`, `bongo/package`, `bongo/setting`

## Architecture Patterns

### Service Provider
Located at `src/ProjectServiceProvider.php`, this class extends `Bongo\Framework\Providers\AbstractServiceProvider` which provides automatic bootstrapping.

**Key Properties:**
- `$module = 'project'` - Determines config namespace, view namespace, routes prefix
- `$composers` - View composers for dropdowns
- `$listeners` - Event listeners for project lifecycle

**Automatic Bootstrapping** (inherited from AbstractServiceProvider):
- Config from `src/Config/project.php`
- Routes from `src/Routes/` (api.php, backend.php, frontend.php)
- Views from `src/Views/project/`
- Migrations from `src/Migrations/`
- Translations from `src/Translations/`

### Route Middleware Patterns
- **api.php** → `api.*` prefix with `auth:sanctum` middleware
- **backend.php** → `backend.*` prefix with `auth` + `employee` middleware
- **frontend.php** → `frontend.*` named routes with custom middleware (`hasRedirects`, `hasShortCodes`, `minifyHtml`)

### Models

#### Project (`src/Models/Project.php`)
Extends `Bongo\Framework\Models\AbstractModel` and implements `Bongo\Image\Interfaces\Imageable`.

**Constants:**
```php
// Status
const PENDING = 'pending';
const ACTIVE = 'active';
const INACTIVE = 'inactive';

// Meta Index
const INDEX = 'index';
const NO_INDEX = 'noindex';

// Boolean values
const ENABLED = 1;
const DISABLED = 0;
```

**Traits:**
- `HasContent` - Content management (getContent, getSummary, truncateContent)
- `HasHeaderClass` - Header customisation (isTransparentHeader, isStickyHeader)
- `HasImages` - Image relationship and helpers
- `HasKey` - Unique key generation
- `HasRelated` - Related projects logic (getRelated, getRelatedByRandom, getPrevious, getNext)
- `HasSeo` - SEO metadata (getMetaTitle, getMetaDescription, getMetaCanonical)
- `HasStatus` - Status scopes (active(), inactive(), pending())
- `HasUUID` - UUID generation for frontend editing
- `SoftDeletes` - Laravel soft delete functionality

**Relationships:**
```php
categories(): BelongsToMany  // project_categories_pivot table
```

**Key Methods:**
- `duplicate(): self` - Creates a copy with new UUID, randomised name, pending status
- `getSummaryAttribute($dbValue): ?string` - Returns summary or auto-generates from content

#### ProjectCategory (`src/Models/ProjectCategory.php`)
Extends `Bongo\Framework\Models\AbstractModel` and implements `Bongo\Image\Interfaces\Imageable`.

Uses same traits as Project (except HasRelated) with same constants.

**Relationships:**
```php
projects(): BelongsToMany  // project_categories_pivot table
```

### Controllers

#### Backend Controllers
All extend `Bongo\Framework\Http\Controllers\AbstractController`.

**ProjectController** (`src/Http/Controllers/Backend/ProjectController.php`):
- Standard CRUD operations (index, create, store, show, edit, update, destroy)
- `duplicate(Project $project)` - Creates a copy of the project
- `syncProjectCategories($project)` - Syncs many-to-many relationships
- Fires events: ProjectCreated, ProjectUpdated, ProjectDeleted

**ProjectDatatableController** (`src/Http/Controllers/Backend/ProjectDatatableController.php`):
- Extends `AbstractDatatableController`
- Returns ProjectResource collection for datatables

**ProjectCategoryController** (`src/Http/Controllers/Backend/ProjectCategoryController.php`):
- Standard CRUD for categories
- Fires events: ProjectCategoryCreated, ProjectCategoryUpdated, ProjectCategoryDeleted

**ProjectImageController** (`src/Http/Controllers/Backend/ProjectImageController.php`):
- Handles image uploads for projects

#### Frontend Controllers
All extend `Bongo\Framework\Http\Controllers\AbstractController`.

**ProjectController** (`src/Http/Controllers/Frontend/ProjectController.php`):
- `index()` - Redirects to category index
- `show($slug)` - Displays project detail, sets selected category in session
- `edit($uuid)` - Frontend builder view (requires auth + employee)
- `update($uuid)` - Processes builder content with BuilderService
- `image($uuid)` - WYSIWYG image uploads

**ProjectCategoryController** (`src/Http/Controllers/Frontend/ProjectCategoryController.php`):
- `index()` - Lists categories
- `show($slug)` - Displays category with projects, sets selected category in session

#### API Controllers
**ProjectController** (`src/Http/Controllers/Api/ProjectController.php`):
- `index()` - Returns ProjectResource collection

**ProjectCategoryController** (`src/Http/Controllers/Api/ProjectCategoryController.php`):
- `index()` - Returns ProjectCategoryResource collection

### Form Requests
Located in `src/Http/Requests/`:

**StoreProjectRequest**:
```php
'name' => "required|unique:{$projectTable},slug,NULL,id,deleted_at,NULL|max:75"
```

**UpdateProjectRequest**:
```php
'name' => "required|unique:{$projectTable},slug,{$project->id},id,deleted_at,NULL|max:75"
```

Similar pattern for ProjectCategory requests.

### Events & Listeners

**Events** (`src/Events/`):
- ProjectCreated, ProjectUpdated, ProjectDeleted
- ProjectCategoryCreated, ProjectCategoryUpdated, ProjectCategoryDeleted

All events accept the model instance and use `Dispatchable`, `InteractsWithSockets`, `SerializesModels` traits.

**Listeners** (`src/Listeners/`):
- `SetProjectDate` (implements ShouldQueue) - Sets date on creation if empty
- `SetProjectUser` (implements ShouldQueue) - Sets user_id on creation
- External listeners: `ClearMenuCache`, `UpdateSitemap` (from other packages)

### View Composers
Located in `src/Http/ViewComposers/`:

**ProjectComposer** - Provides project dropdown data
**ProjectCategoryComposer** - Provides category dropdown data
**RecentProjectsComposer** - Provides recent projects for widgets

### Custom Trait: HasRelated
Located at `src/Traits/HasRelated.php`, provides:

```php
getRelated(): Builder                        // Base query for related projects
getRelatedByRandom(int $limit = 4): Collection  // Random related projects
getPrimaryCategory(): ?ProjectCategory        // First category
getPrevious(): ?Project                       // Previous in category
getNext(): ?Project                           // Next in category
```

Uses session storage for `selected_project_category` to maintain context across requests.

## Database Schema

### projects table
```php
id (increments)
uuid (uuid, indexed)
user_id (unsignedInteger, nullable, indexed)
date (timestamp, nullable)
name (string)
key (string, nullable, indexed)
slug (string, indexed)
content (longText, nullable)
summary (mediumText, nullable)
status (enum: pending, active, inactive)
transparent_header (tinyInteger, default: 1)
sticky_header (tinyInteger, default: 1)
css (text, nullable)
js (text, nullable)
schema (text, nullable) - Added in v3.0
meta_title (string, nullable)
meta_description (text, nullable)
meta_index (enum: index, noindex)
meta_canonical (string, nullable)
created_by, updated_by, deleted_by (unsignedInteger, nullable, indexed)
timestamps, softDeletes
```

### project_categories table
Similar structure to projects, without date, user_id, and schema fields.

### project_categories_pivot table
```php
project_id (unsignedInteger)
project_category_id (unsignedInteger)
```

## Coding Conventions

### Naming Conventions
- **Models**: Singular PascalCase (Project, ProjectCategory)
- **Controllers**: PascalCase with Controller suffix
- **Events**: PascalCase with past tense (ProjectCreated, ProjectUpdated)
- **Listeners**: PascalCase with imperative (SetProjectDate, SetProjectUser)
- **Routes**: Snake_case with dot notation (backend.project.index, frontend.project.show)
- **Views**: Lowercase with dot notation (project::backend.index)
- **Config**: Lowercase (project.prefix, project.category_prefix)

### Return Types
- Controllers return `View`, `RedirectResponse`, or `JsonResponse`
- Models use nullable return types (`?string`, `?ProjectCategory`)
- Requests return `array` from rules()
- Resources return `array` from toArray()

### Form Validation
- Use FormRequest classes for validation
- Validation rules include soft-delete awareness with `deleted_at,NULL`
- Max length for names: 75 characters
- Slugs auto-generated from names

### Event Handling
- Fire events AFTER model changes (create, update, delete)
- Listeners should implement `ShouldQueue` for async processing
- Listeners should define `$tries` property for retry logic

### Image Handling
- Projects implement `Imageable` interface
- Images stored in `config('image.public_path').'projects/'.$project->id.'/'`
- WYSIWYG images saved with type `Image::WYSIWYG`
- Image titles default to project name

## Configuration

### Config File (`src/Config/project.php`)
```php
'prefix' => 'projects'                  // Frontend/backend route prefix
'category_prefix' => 'project-categories'  // Category route prefix
```

### Route Prefixes
- Backend: `/admin/projects`, `/admin/project-categories`
- Frontend: `/projects`, `/project-categories`
- API: `/api/projects`, `/api/project-categories`

## Common Tasks

### Creating a New Project
```php
$project = Project::create([
    'name' => 'Project Name',
    'content' => '<p>Content</p>',
    'status' => Project::ACTIVE,
]);
event(new ProjectCreated($project));
```

### Querying Active Projects
```php
$projects = Project::active()->with('categories', 'images')->get();
```

### Getting Related Projects
```php
$related = $project->getRelatedByRandom(4);
```

### Syncing Categories
```php
$project->categories()->sync([1, 2, 3]);
```

### Duplicating a Project
```php
$newProject = $project->duplicate();
```

### Using Frontend Builder
- Access via UUID route: `/projects/edit/{uuid}`
- Requires `auth` and `employee` middleware
- Uses `BuilderService` to process images and content
- Updates trigger ProjectUpdated event

## Testing

Run tests with:
```bash
vendor/bin/phpunit
```

Test structure:
- `tests/Unit/` - Unit tests
- `tests/Feature/` - Feature tests
- `tests/TestCase.php` - Base test case

## Code Quality

### Laravel Pint
```bash
# Check code style
vendor/bin/pint --test

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

### PHPStan
```bash
vendor/bin/phpstan analyse
```

## Views

### Backend Views (`src/Views/backend/`)
- `index.blade.php` - Datatable listing
- `create.blade.php` - Create form
- `edit.blade.php` - Edit form
- `show.blade.php` - Detail view
- `partials/form/details.blade.php` - Form fields
- `partials/dropdowns/project.blade.php` - Project dropdown
- `category/*` - Category management views

### Frontend Views (`src/Views/frontend/`)
- `show.blade.php` - Project detail page
- `builder.blade.php` - WYSIWYG builder interface
- `category/index.blade.php` - Category listing
- `category/show.blade.php` - Category detail with projects
- `partials/project.blade.php` - Project card/listing partial
- `partials/category.blade.php` - Category card/listing partial
- `partials/related_projects.blade.php` - Related projects widget
- `partials/next_previous.blade.php` - Navigation between projects

## Dependencies

### Required Packages
- `bongo/framework` - Base framework with AbstractServiceProvider, AbstractModel, AbstractController
- `bongo/image` - Image management (Imageable interface, HasImages trait, BuilderService)
- `bongo/menu` - Menu integration (ClearMenuCache listener)
- `bongo/package` - Package management
- `bongo/setting` - Settings management

### External Integrations
- `bongo/sitemap` - UpdateSitemap listener fired on project/category changes
- Session storage - Used for maintaining selected category context

## Best Practices

1. **Always fire events** after CRUD operations to maintain cache and sitemap integrity
2. **Use scopes** for status filtering (`active()`, `inactive()`, `pending()`)
3. **Eager load relationships** to avoid N+1 queries (`with('categories', 'images')`)
4. **Check user authentication** in frontend show methods for pending/inactive projects
5. **Sync categories** separately after create/update using `syncProjectCategories()`
6. **Use UUID** for frontend editing routes to prevent URL prediction
7. **Set selected category** in session for related project context
8. **Process builder content** with BuilderService to handle image paths
9. **Use form requests** for validation to keep controllers lean
10. **Return resource collections** from API endpoints for consistent structure

## Important Notes

- Projects are soft-deleted (use `withTrashed()`, `onlyTrashed()` for queries)
- Slugs are auto-generated from names but can be customised
- Keys are auto-generated for API/integration purposes
- Header classes (transparent/sticky) are stored as tinyIntegers (0/1)
- Date field uses custom `Date` cast from framework
- Summary auto-generates from content if not provided
- Project duplication creates a pending copy with randomised name
- Session state affects related/previous/next queries
- Builder routes require both auth and employee middleware
- Schema field supports JSON-LD structured data for SEO

## Links to Documentation

- See `ARCHITECTURE.md` for detailed architecture diagrams and flows
- See `CLAUDE.md` for quick reference and key files
- See `README.md` for installation and overview
