RolePermissionSeeder now automatically scans app/Modules/*/Permissions.php files and registers all permissions found. No manual registration required. - Added loadModulePermissions() method to scan module directories - Changed givePermissionTo() to syncPermissions() for idempotency - Updated Modules README to document auto-loading - Updated CLAUDE.md to reflect auto-loading behavior
783 lines
20 KiB
Markdown
783 lines
20 KiB
Markdown
# AI Agent Instructions for Laravel Module Development
|
|
|
|
> **CRITICAL**: This document provides EXACT specifications for building modules in this Laravel template.
|
|
> Follow these instructions PRECISELY. Do NOT deviate, guess, or improvise.
|
|
> When in doubt, READ THE EXISTING CODE before making changes.
|
|
|
|
---
|
|
|
|
## 🚨 BEFORE YOU START - MANDATORY STEPS
|
|
|
|
### 1. Always Check Existing Patterns First
|
|
```bash
|
|
# List existing modules to see patterns
|
|
ls -la src/app/Modules/
|
|
|
|
# Read an existing module's structure
|
|
find src/app/Modules/[ExistingModule]/ -type f -name "*.php"
|
|
```
|
|
|
|
### 2. Always Check Logs After Errors
|
|
```bash
|
|
docker-compose exec app tail -n 100 storage/logs/laravel.log
|
|
```
|
|
|
|
### 3. Never Guess at Solutions
|
|
- If you encounter an error, READ the full stack trace
|
|
- Find the EXACT file and line number causing the issue
|
|
- Understand what the code is trying to do before fixing
|
|
|
|
---
|
|
|
|
## 📁 PROJECT STRUCTURE - EXACT PATHS
|
|
|
|
```
|
|
Laravel-Docker-Dev-Template/
|
|
├── src/ # ← ALL Laravel code goes here
|
|
│ ├── app/
|
|
│ │ ├── Filament/
|
|
│ │ │ ├── Pages/
|
|
│ │ │ │ └── Settings.php # Site settings page
|
|
│ │ │ └── Resources/ # Core Filament resources
|
|
│ │ │ ├── UserResource.php
|
|
│ │ │ ├── RoleResource.php
|
|
│ │ │ └── PermissionResource.php
|
|
│ │ ├── Http/
|
|
│ │ │ ├── Controllers/
|
|
│ │ │ └── Middleware/
|
|
│ │ ├── Models/
|
|
│ │ │ ├── User.php
|
|
│ │ │ └── Setting.php
|
|
│ │ ├── Modules/ # ← ALL modules go here
|
|
│ │ │ └── [ModuleName]/ # ← Each module is a folder
|
|
│ │ ├── Providers/
|
|
│ │ │ └── AppServiceProvider.php
|
|
│ │ └── Traits/
|
|
│ │ └── ModuleAuditable.php
|
|
│ ├── bootstrap/
|
|
│ │ └── app.php # Middleware registration
|
|
│ ├── config/
|
|
│ ├── database/
|
|
│ │ ├── migrations/
|
|
│ │ └── seeders/
|
|
│ │ ├── DatabaseSeeder.php
|
|
│ │ └── RolePermissionSeeder.php
|
|
│ ├── resources/
|
|
│ │ └── views/
|
|
│ │ ├── layouts/
|
|
│ │ │ ├── app.blade.php # Authenticated layout
|
|
│ │ │ └── guest.blade.php # Guest layout
|
|
│ │ └── components/
|
|
│ └── routes/
|
|
│ ├── web.php
|
|
│ ├── api.php
|
|
│ └── auth.php
|
|
├── docker-compose.yml
|
|
├── setup.bat # Windows setup
|
|
└── setup.sh # Linux/Mac setup
|
|
```
|
|
|
|
---
|
|
|
|
## 🔧 MODULE STRUCTURE - EXACT SPECIFICATION
|
|
|
|
When creating a module named `[ModuleName]` (e.g., `StockManagement`):
|
|
|
|
```
|
|
src/app/Modules/[ModuleName]/
|
|
├── Config/
|
|
│ └── [module_name].php # snake_case filename
|
|
├── Database/
|
|
│ ├── Migrations/
|
|
│ │ └── YYYY_MM_DD_HHMMSS_create_[table_name]_table.php
|
|
│ └── Seeders/
|
|
│ └── [ModuleName]Seeder.php
|
|
├── Filament/
|
|
│ └── Resources/
|
|
│ ├── [ModelName]Resource.php
|
|
│ └── [ModelName]Resource/
|
|
│ └── Pages/
|
|
│ ├── List[ModelName]s.php
|
|
│ ├── Create[ModelName].php
|
|
│ └── Edit[ModelName].php
|
|
├── Http/
|
|
│ ├── Controllers/
|
|
│ │ └── [ModuleName]Controller.php
|
|
│ ├── Middleware/
|
|
│ └── Requests/
|
|
│ └── [ModelName]Request.php
|
|
├── Models/
|
|
│ └── [ModelName].php
|
|
├── Policies/
|
|
│ └── [ModelName]Policy.php
|
|
├── Services/
|
|
│ └── [ModelName]Service.php
|
|
├── Routes/
|
|
│ ├── web.php
|
|
│ └── api.php
|
|
├── Resources/
|
|
│ └── views/
|
|
│ ├── index.blade.php
|
|
│ └── layouts/
|
|
│ └── module.blade.php
|
|
├── Permissions.php
|
|
└── [ModuleName]ServiceProvider.php
|
|
```
|
|
|
|
---
|
|
|
|
## 📝 EXACT FILE TEMPLATES
|
|
|
|
### 1. Service Provider (REQUIRED)
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/[ModuleName]ServiceProvider.php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Modules\[ModuleName];
|
|
|
|
use Illuminate\Support\ServiceProvider;
|
|
use Illuminate\Support\Facades\Route;
|
|
|
|
class [ModuleName]ServiceProvider extends ServiceProvider
|
|
{
|
|
public function register(): void
|
|
{
|
|
$this->mergeConfigFrom(
|
|
__DIR__ . '/Config/[module_name].php',
|
|
'[module_name]'
|
|
);
|
|
}
|
|
|
|
public function boot(): void
|
|
{
|
|
$this->loadMigrationsFrom(__DIR__ . '/Database/Migrations');
|
|
$this->loadViewsFrom(__DIR__ . '/Resources/views', '[module-slug]');
|
|
|
|
$this->registerRoutes();
|
|
}
|
|
|
|
protected function registerRoutes(): void
|
|
{
|
|
Route::middleware(['web', 'auth'])
|
|
->prefix('[module-slug]')
|
|
->name('[module-slug].')
|
|
->group(__DIR__ . '/Routes/web.php');
|
|
|
|
if (file_exists(__DIR__ . '/Routes/api.php')) {
|
|
Route::middleware(['api', 'auth:sanctum'])
|
|
->prefix('api/[module-slug]')
|
|
->name('api.[module-slug].')
|
|
->group(__DIR__ . '/Routes/api.php');
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**NAMING RULES**:
|
|
- `[ModuleName]` = PascalCase (e.g., `StockManagement`)
|
|
- `[module_name]` = snake_case (e.g., `stock_management`)
|
|
- `[module-slug]` = kebab-case (e.g., `stock-management`)
|
|
|
|
### 2. Config File (REQUIRED)
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/Config/[module_name].php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
return [
|
|
'name' => '[Module Display Name]',
|
|
'slug' => '[module-slug]',
|
|
'version' => '1.0.0',
|
|
|
|
'audit' => [
|
|
'enabled' => true,
|
|
'strategy' => 'all', // 'all', 'include', 'exclude', 'none'
|
|
'include' => [],
|
|
'exclude' => [],
|
|
],
|
|
];
|
|
```
|
|
|
|
### 3. Model (REQUIRED for data modules)
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/Models/[ModelName].php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Modules\[ModuleName]\Models;
|
|
|
|
use App\Traits\ModuleAuditable;
|
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use OwenIt\Auditing\Contracts\Auditable;
|
|
|
|
class [ModelName] extends Model implements Auditable
|
|
{
|
|
use HasFactory, ModuleAuditable;
|
|
|
|
protected $fillable = [
|
|
// List ALL fields that can be mass-assigned
|
|
];
|
|
|
|
protected $casts = [
|
|
// Type casts for fields
|
|
];
|
|
|
|
// Define relationships here
|
|
}
|
|
```
|
|
|
|
### 4. Migration (REQUIRED for data modules)
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/Database/Migrations/YYYY_MM_DD_HHMMSS_create_[table_name]_table.php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
use Illuminate\Database\Migrations\Migration;
|
|
use Illuminate\Database\Schema\Blueprint;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
return new class extends Migration
|
|
{
|
|
public function up(): void
|
|
{
|
|
Schema::create('[table_name]', function (Blueprint $table) {
|
|
$table->id();
|
|
// Define ALL columns here
|
|
$table->timestamps();
|
|
});
|
|
}
|
|
|
|
public function down(): void
|
|
{
|
|
Schema::dropIfExists('[table_name]');
|
|
}
|
|
};
|
|
```
|
|
|
|
### 5. Permissions (REQUIRED) - AUTO-LOADED
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/Permissions.php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
return [
|
|
'[module_name].view' => 'View [Module Name]',
|
|
'[module_name].create' => 'Create [Module Name] records',
|
|
'[module_name].edit' => 'Edit [Module Name] records',
|
|
'[module_name].delete' => 'Delete [Module Name] records',
|
|
];
|
|
```
|
|
|
|
> **✅ AUTO-LOADED**: `RolePermissionSeeder` automatically scans all `app/Modules/*/Permissions.php`
|
|
> files and registers them. Just run `php artisan db:seed --class=RolePermissionSeeder`.
|
|
|
|
### 6. Filament Resource (REQUIRED for admin)
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/Filament/Resources/[ModelName]Resource.php`
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Modules\[ModuleName]\Filament\Resources;
|
|
|
|
use App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource\Pages;
|
|
use App\Modules\[ModuleName]\Models\[ModelName];
|
|
use Filament\Forms;
|
|
use Filament\Forms\Form;
|
|
use Filament\Resources\Resource;
|
|
use Filament\Tables;
|
|
use Filament\Tables\Table;
|
|
|
|
class [ModelName]Resource extends Resource
|
|
{
|
|
protected static ?string $model = [ModelName]::class;
|
|
|
|
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
|
|
|
protected static ?string $navigationGroup = '[Module Display Name]';
|
|
|
|
protected static ?int $navigationSort = 1;
|
|
|
|
public static function canAccess(): bool
|
|
{
|
|
return auth()->user()?->can('[module_name].view') ?? false;
|
|
}
|
|
|
|
public static function form(Form $form): Form
|
|
{
|
|
return $form
|
|
->schema([
|
|
Forms\Components\Section::make('[Model Name] Details')
|
|
->schema([
|
|
// Add form fields here
|
|
]),
|
|
]);
|
|
}
|
|
|
|
public static function table(Table $table): Table
|
|
{
|
|
return $table
|
|
->columns([
|
|
// Add table columns here
|
|
])
|
|
->filters([
|
|
//
|
|
])
|
|
->actions([
|
|
Tables\Actions\EditAction::make(),
|
|
Tables\Actions\DeleteAction::make(),
|
|
])
|
|
->bulkActions([
|
|
Tables\Actions\BulkActionGroup::make([
|
|
Tables\Actions\DeleteBulkAction::make(),
|
|
]),
|
|
]);
|
|
}
|
|
|
|
public static function getRelations(): array
|
|
{
|
|
return [
|
|
//
|
|
];
|
|
}
|
|
|
|
public static function getPages(): array
|
|
{
|
|
return [
|
|
'index' => Pages\List[ModelName]s::route('/'),
|
|
'create' => Pages\Create[ModelName]::route('/create'),
|
|
'edit' => Pages\Edit[ModelName]::route('/{record}/edit'),
|
|
];
|
|
}
|
|
}
|
|
```
|
|
|
|
### 7. Filament Resource Pages (REQUIRED)
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/Filament/Resources/[ModelName]Resource/Pages/List[ModelName]s.php`
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource\Pages;
|
|
|
|
use App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource;
|
|
use Filament\Actions;
|
|
use Filament\Resources\Pages\ListRecords;
|
|
|
|
class List[ModelName]s extends ListRecords
|
|
{
|
|
protected static string $resource = [ModelName]Resource::class;
|
|
|
|
protected function getHeaderActions(): array
|
|
{
|
|
return [
|
|
Actions\CreateAction::make(),
|
|
];
|
|
}
|
|
}
|
|
```
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/Filament/Resources/[ModelName]Resource/Pages/Create[ModelName].php`
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource\Pages;
|
|
|
|
use App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource;
|
|
use Filament\Resources\Pages\CreateRecord;
|
|
|
|
class Create[ModelName] extends CreateRecord
|
|
{
|
|
protected static string $resource = [ModelName]Resource::class;
|
|
}
|
|
```
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/Filament/Resources/[ModelName]Resource/Pages/Edit[ModelName].php`
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource\Pages;
|
|
|
|
use App\Modules\[ModuleName]\Filament\Resources\[ModelName]Resource;
|
|
use Filament\Actions;
|
|
use Filament\Resources\Pages\EditRecord;
|
|
|
|
class Edit[ModelName] extends EditRecord
|
|
{
|
|
protected static string $resource = [ModelName]Resource::class;
|
|
|
|
protected function getHeaderActions(): array
|
|
{
|
|
return [
|
|
Actions\DeleteAction::make(),
|
|
];
|
|
}
|
|
}
|
|
```
|
|
|
|
### 8. Routes (REQUIRED)
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/Routes/web.php`
|
|
```php
|
|
<?php
|
|
|
|
use App\Modules\[ModuleName]\Http\Controllers\[ModuleName]Controller;
|
|
use Illuminate\Support\Facades\Route;
|
|
|
|
Route::get('/', [[ModuleName]Controller::class, 'index'])->name('index');
|
|
```
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/Routes/api.php` (if API needed)
|
|
```php
|
|
<?php
|
|
|
|
use Illuminate\Support\Facades\Route;
|
|
|
|
Route::middleware('auth:sanctum')->group(function () {
|
|
// API routes here
|
|
});
|
|
```
|
|
|
|
### 9. Controller (REQUIRED)
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/Http/Controllers/[ModuleName]Controller.php`
|
|
```php
|
|
<?php
|
|
|
|
namespace App\Modules\[ModuleName]\Http\Controllers;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use Illuminate\Http\Request;
|
|
|
|
class [ModuleName]Controller extends Controller
|
|
{
|
|
public function index()
|
|
{
|
|
$this->authorize('[module_name].view');
|
|
|
|
return view('[module-slug]::index');
|
|
}
|
|
}
|
|
```
|
|
|
|
### 10. Index View (REQUIRED)
|
|
|
|
**File**: `src/app/Modules/[ModuleName]/Resources/views/index.blade.php`
|
|
```blade
|
|
<x-app-layout>
|
|
<x-slot name="header">
|
|
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
|
{{ __('[Module Display Name]') }}
|
|
</h2>
|
|
</x-slot>
|
|
|
|
<div class="py-12">
|
|
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
|
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
|
<div class="p-6 text-gray-900 dark:text-gray-100">
|
|
{{ __('[Module Display Name] content goes here.') }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</x-app-layout>
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ MODULE REGISTRATION
|
|
|
|
After creating all files, register the module in `src/config/app.php` providers array:
|
|
|
|
```php
|
|
'providers' => ServiceProvider::defaultProviders()->merge([
|
|
// ...existing providers...
|
|
App\Modules\[ModuleName]\[ModuleName]ServiceProvider::class,
|
|
])->toArray(),
|
|
```
|
|
|
|
Or add to `src/bootstrap/providers.php`:
|
|
```php
|
|
return [
|
|
App\Providers\AppServiceProvider::class,
|
|
// ...existing providers...
|
|
App\Modules\[ModuleName]\[ModuleName]ServiceProvider::class,
|
|
];
|
|
```
|
|
|
|
---
|
|
|
|
## 🔄 AFTER MODULE CREATION - REQUIRED COMMANDS
|
|
|
|
```bash
|
|
# 1. Run migrations
|
|
docker-compose exec app php artisan migrate
|
|
|
|
# 2. Clear all caches
|
|
docker-compose exec app php artisan optimize:clear
|
|
|
|
# 3. Seed permissions (if using permissions)
|
|
docker-compose exec app php artisan db:seed --class=RolePermissionSeeder
|
|
|
|
# 4. Reset permission cache
|
|
docker-compose exec app php artisan permission:cache-reset
|
|
```
|
|
|
|
---
|
|
|
|
## ⚠️ COMMON MISTAKES TO AVOID
|
|
|
|
### 1. Wrong Namespace
|
|
❌ `namespace App\Modules\StockManagement\Models;`
|
|
✅ `namespace App\Modules\StockManagement\Models;`
|
|
|
|
The namespace MUST match the folder structure EXACTLY.
|
|
|
|
### 2. Wrong View Namespace
|
|
❌ `return view('stockmanagement::index');`
|
|
❌ `return view('StockManagement::index');`
|
|
✅ `return view('stock-management::index');`
|
|
|
|
View namespace is ALWAYS kebab-case.
|
|
|
|
### 3. Wrong Permission Names
|
|
❌ `'StockManagement.view'`
|
|
❌ `'stock-management.view'`
|
|
✅ `'stock_management.view'`
|
|
|
|
Permissions are ALWAYS snake_case.
|
|
|
|
### 4. Forgetting to Register Provider
|
|
The module WILL NOT LOAD if you forget to add its ServiceProvider.
|
|
|
|
### 5. Wrong Import Paths
|
|
❌ `use App\Models\Product;`
|
|
✅ `use App\Modules\StockManagement\Models\Product;`
|
|
|
|
Module models are in the MODULE namespace, not the core App namespace.
|
|
|
|
### 6. Missing Fillable Array
|
|
❌ Empty `$fillable` array causes mass-assignment errors
|
|
✅ List ALL fields that should be mass-assignable
|
|
|
|
### 7. Forgetting to Run Migrations
|
|
Always run `php artisan migrate` after creating migrations.
|
|
|
|
---
|
|
|
|
## 🎨 FILAMENT FORM FIELD REFERENCE
|
|
|
|
```php
|
|
// Text
|
|
Forms\Components\TextInput::make('name')
|
|
->required()
|
|
->maxLength(255);
|
|
|
|
// Email
|
|
Forms\Components\TextInput::make('email')
|
|
->email()
|
|
->required();
|
|
|
|
// Password
|
|
Forms\Components\TextInput::make('password')
|
|
->password()
|
|
->required();
|
|
|
|
// Textarea
|
|
Forms\Components\Textarea::make('description')
|
|
->rows(3);
|
|
|
|
// Select
|
|
Forms\Components\Select::make('status')
|
|
->options([
|
|
'active' => 'Active',
|
|
'inactive' => 'Inactive',
|
|
])
|
|
->required();
|
|
|
|
// Checkbox
|
|
Forms\Components\Toggle::make('is_active')
|
|
->default(true);
|
|
|
|
// Date
|
|
Forms\Components\DatePicker::make('date');
|
|
|
|
// DateTime
|
|
Forms\Components\DateTimePicker::make('published_at');
|
|
|
|
// Number
|
|
Forms\Components\TextInput::make('price')
|
|
->numeric()
|
|
->prefix('$');
|
|
|
|
// File Upload
|
|
Forms\Components\FileUpload::make('image')
|
|
->image()
|
|
->directory('uploads');
|
|
|
|
// Relationship Select
|
|
Forms\Components\Select::make('category_id')
|
|
->relationship('category', 'name')
|
|
->searchable()
|
|
->preload();
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 FILAMENT TABLE COLUMN REFERENCE
|
|
|
|
```php
|
|
// Text
|
|
Tables\Columns\TextColumn::make('name')
|
|
->searchable()
|
|
->sortable();
|
|
|
|
// Badge
|
|
Tables\Columns\TextColumn::make('status')
|
|
->badge()
|
|
->color(fn (string $state): string => match ($state) {
|
|
'active' => 'success',
|
|
'inactive' => 'danger',
|
|
default => 'gray',
|
|
});
|
|
|
|
// Boolean Icon
|
|
Tables\Columns\IconColumn::make('is_active')
|
|
->boolean();
|
|
|
|
// Image
|
|
Tables\Columns\ImageColumn::make('avatar')
|
|
->circular();
|
|
|
|
// Date
|
|
Tables\Columns\TextColumn::make('created_at')
|
|
->dateTime()
|
|
->sortable();
|
|
|
|
// Money
|
|
Tables\Columns\TextColumn::make('price')
|
|
->money('USD');
|
|
```
|
|
|
|
---
|
|
|
|
## 🔐 EXISTING CORE RESOURCES
|
|
|
|
### User Management (already exists in template)
|
|
- `src/app/Filament/Resources/UserResource.php` - User CRUD with role assignment
|
|
- `src/app/Filament/Resources/RoleResource.php` - Role CRUD with permissions
|
|
- `src/app/Filament/Resources/PermissionResource.php` - Permission CRUD
|
|
|
|
### Settings (already exists)
|
|
- `src/app/Filament/Pages/Settings.php` - Site settings (name, logo, colors, registration toggle)
|
|
- `src/app/Models/Setting.php` - Settings model with get/set helpers
|
|
|
|
### Authentication (pre-installed)
|
|
- Laravel Breeze with Blade templates
|
|
- Login: `/login`
|
|
- Register: `/register` (controlled by enable_registration setting)
|
|
- Admin: `/admin`
|
|
|
|
---
|
|
|
|
## 🧪 TESTING MODULES
|
|
|
|
Create tests in `src/tests/Feature/Modules/[ModuleName]/`:
|
|
|
|
```php
|
|
<?php
|
|
|
|
namespace Tests\Feature\Modules\[ModuleName];
|
|
|
|
use App\Models\User;
|
|
use App\Modules\[ModuleName]\Models\[ModelName];
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class [ModelName]Test extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_user_can_view_[model_name]s(): void
|
|
{
|
|
$user = User::factory()->create();
|
|
$user->givePermissionTo('[module_name].view');
|
|
|
|
$this->actingAs($user)
|
|
->get('/[module-slug]')
|
|
->assertStatus(200);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📋 CHECKLIST FOR NEW MODULES
|
|
|
|
Before considering a module complete:
|
|
|
|
- [ ] ServiceProvider created and registered
|
|
- [ ] Config file created
|
|
- [ ] Model created with fillable and casts
|
|
- [ ] Migration created and run
|
|
- [ ] Permissions.php created
|
|
- [ ] Filament Resource created with all pages
|
|
- [ ] Controller created
|
|
- [ ] Routes (web.php and optionally api.php) created
|
|
- [ ] Index view created
|
|
- [ ] `php artisan migrate` run
|
|
- [ ] `php artisan optimize:clear` run
|
|
- [ ] `php artisan db:seed --class=RolePermissionSeeder` run
|
|
- [ ] Module accessible at `/[module-slug]`
|
|
- [ ] Admin panel shows module in navigation
|
|
- [ ] CRUD operations work in admin
|
|
|
|
---
|
|
|
|
## 🆘 DEBUGGING
|
|
|
|
### Module Not Loading
|
|
```bash
|
|
# Check if provider is registered
|
|
docker-compose exec app php artisan about
|
|
|
|
# Clear everything
|
|
docker-compose exec app php artisan optimize:clear
|
|
```
|
|
|
|
### Filament Resource Not Showing
|
|
```bash
|
|
# Clear Filament cache
|
|
docker-compose exec app php artisan filament:cache-components
|
|
|
|
# Check canAccess() method returns true
|
|
# Check user has required permission
|
|
```
|
|
|
|
### Permission Denied
|
|
```bash
|
|
# Reset permission cache
|
|
docker-compose exec app php artisan permission:cache-reset
|
|
|
|
# Verify permissions exist
|
|
docker-compose exec app php artisan tinker
|
|
>>> \Spatie\Permission\Models\Permission::pluck('name');
|
|
```
|
|
|
|
### View Not Found
|
|
```bash
|
|
# Verify view namespace (must be kebab-case)
|
|
# Check file exists in Resources/views/
|
|
```
|
|
|
|
---
|
|
|
|
**Remember**: When in doubt, look at existing code in the codebase. The patterns are already established - follow them exactly.
|