# Setting Package - Cursor Rules

This Laravel package provides system-wide settings management with database persistence, event-driven updates, and automatic cache management. Part of the Bongo monorepo (default/setting).

## Project Structure

```
src/
├── Actions/              # Executable actions (UpdateSettings, ClearCache, CompileCss)
├── Events/              # Domain events (SettingUpdated, CssUpdated, JsUpdated, SchemaUpdated)
├── Http/
│   ├── Controllers/Backend/  # Admin controllers for settings management
│   └── Requests/        # Form request validation
├── Interfaces/          # Constants for namespaces, groups, input types, data types
├── Listeners/           # Event listeners for module status updates
├── Migrations/          # Database schema for settings table
├── Models/              # Setting model with type traits
├── Routes/              # Backend routes (admin settings pages)
├── Seeders/             # DataSeeder for initial settings setup
├── Services/            # SettingManager service for settings access
├── Traits/              # HasType trait for input type checks
├── Translations/        # Language files
├── Views/backend/       # Blade templates for admin settings UI
├── helpers.php          # Global setting() helper function
└── SettingServiceProvider.php
```

## Architecture Patterns

### Service Provider Bootstrap
Extends `Bongo\Framework\Providers\AbstractServiceProvider`:
- `$module = 'setting'` for automatic resource registration
- Registers SettingManager as singleton: `app('setting_manager')`
- Registers Setting model alias
- Loads helpers.php with setting() function
- Overrides ReCaptcha config from settings
- Event listeners array for SettingUpdated, CssUpdated, JsUpdated, SchemaUpdated

### Settings Storage Model
**Key Pattern**: Settings use hierarchical keys: `namespace::group.key`
- **Namespaces**: client, package, estimate, theme, system (see Interfaces/Namespaces.php)
- **Groups**: company, social, marketing, office, registered, layout, color, logo, credentials, developer, misc, estimate, gallery, openai, post, project, review, page
- **Examples**:
  - `theme::color.primary` → primary theme colour
  - `client::company.phone` → company phone number
  - `system::credentials.openai_api_key` → OpenAI API key

### Setting Model (src/Models/Setting.php)
```php
// Fields
$fillable = ['name', 'key', 'value', 'rules', 'options', 'type', 'data']
$casts = ['options' => 'array']

// Implements interfaces
- DataTypes: BOOLEAN, DECIMAL, INTEGER, JSON, STRING
- Groups: COLOR, COMPANY, CREDENTIALS, DEVELOPER, etc.
- InputTypes: CHECKBOX, COLOR_PICKER, DATE_PICKER, DYNAMIC, INPUT, SELECT, TEXT
- Namespaces: CLIENT, PACKAGE, ESTIMATE_AREA, THEME, SYSTEM

// Key methods
getFullKeyAttribute(): string  // Returns "namespace::group.key"
getDynamicOptions(): array     // Fetches options from LayoutFacade for dynamic selects
isEnabled(): bool              // For checkbox settings
isDisabled(): bool             // For checkbox settings

// Uses traits
- HasKey (from bongo/framework)
- HasType (type checking: isCheckbox(), isColorPicker(), isDynamic(), etc.)
```

### SettingManager Service (src/Services/SettingManager.php)
Singleton service registered as `setting_manager`:
```php
// Core methods
has(string $key): bool
get(string $key, $default = null): mixed
all(): Collection
allByNamespace(): array
getByNamespace(string $namespace): array

// File management
getCustomCss(): ?string
setCustomCss(?string $css): void
getCustomJs(): ?string
setCustomJs(?string $js): void  // Also writes to public/js/custom.js
getCustomSchema(): ?string
setCustomSchema(?string $schema): void

// Theme helpers
hasTransparentHeader(): bool
hasStickyHeader(): bool
getHeaderClass(): string

// Business logic
getClientPhoneNumber(): ?string  // Returns marketing phone based on referrer
getRegisteredAddress(): ?string
getOfficeAddress(): ?string
isNoFollow(): bool
getEstimateAreas(): array
getOpenAIApiKey(): ?string
getReCaptchaSiteKey(): ?string
getReCaptchaSecretKey(): ?string
reCaptchaEnabled(): bool
```

All settings cached forever: `Cache::rememberForever("settings", ...)`

### Helper Function (src/helpers.php)
```php
setting()              // Returns SettingManager instance
setting('key')         // Returns setting value
setting('key', 'default')  // Returns setting value or default
```

### Event-Driven Architecture
**SettingUpdated Event** → Triggers listeners:
- UpdateEstimateModuleStatus
- UpdateRealGreenModuleStatus
- UpdateGalleryModuleStatus
- UpdateOpenAIModuleStatus
- UpdatePostModuleStatus
- UpdateProjectModuleStatus
- UpdateReviewModuleStatus
- ClearMenuCache (from bongo/menu)

**CssUpdated Event** → UpdateFrontendCss listener
**JsUpdated Event** → UpdateFrontendJs listener
**SchemaUpdated Event** → UpdateCustomSchema listener

### Actions Pattern
All actions in `src/Actions/`:

**UpdateSettings::execute(array $settings)**: Updates settings by ID, fires SettingUpdated events
```php
UpdateSettings::execute($request->get('settings'));
```

**ClearCache::execute()**: Clears all Laravel caches
```php
ClearCache::execute();  // Clears event, view, cache, route, config, compiled
```

**CompileCss->execute()**: Compiles SCSS to CSS
```php
// Injects theme variables (colors, logo heights, skin)
// Compiles: skins → bootstrap → vendors → components → custom.scss
// Outputs: public/css/frontend.css
```

## Coding Conventions

### Controllers
All controllers extend `Bongo\Framework\Http\Controllers\AbstractController`:
```php
class ClientController extends AbstractController
{
    public function index(): View
    {
        return view('setting::backend.client.index');
    }

    public function update(Request $request): RedirectResponse
    {
        UpdateSettings::execute($request->get('settings'));
        ClearCache::execute();
        return redirect()
            ->route('backend.setting.client.index')
            ->success(trans('setting::backend.update_success'));
    }
}
```

### View Namespacing
Views use `setting::` namespace: `view('setting::backend.client.index')`

### Route Organization (src/Routes/backend.php)
All routes prefixed with `settings` and named `backend.setting.*`:
- `GET /settings` → SettingController::index (redirects to environment or client)
- Settings pages (require developer middleware):
  - `/settings/environment` → EnvironmentController
  - `/settings/package` → PackageController
  - `/settings/estimate` → EstimateController
  - `/settings/system` → SystemController
  - `/settings/theme` → ThemeController
  - `/settings/site-css` → SiteCssController
  - `/settings/site-js` → SiteJsController
  - `/settings/site-schema` → SiteSchemaController
- `/settings/client` → ClientController (requires manager middleware)
- Utility routes:
  - `GET /settings/clear-cache` → CacheController
  - `GET /settings/compile-css` → CssController
  - `GET /settings/generate-sitemap` → SitemapController

### Middleware Usage
- All routes have `auth` + `employee` middleware (from AbstractServiceProvider backend.php registration)
- Additional middleware:
  - `developer` → Developer-only sections
  - `manager` → Manager-level sections

### Database Conventions
Settings table schema:
```
id, namespace, group, name, key, value, rules, options (json), type, data, sort_order, created_at, updated_at
```

Index on `full_key` (computed from namespace::group.key)

### Seeding Pattern (src/Seeders/DataSeeder.php)
Creates default settings grouped by category:
- Company details (client::company)
- Social links (client::social)
- Marketing phone numbers (client::marketing)
- Addresses (client::office, client::registered)
- Theme settings (theme::logo, theme::color, theme::layout)
- System credentials (system::credentials)
- Package toggles (package::estimate, package::gallery, etc.)

Use `firstOrCreateSetting()` to avoid duplicates.

## Common Tasks

### Add a New Setting
1. Add to appropriate method in `src/Seeders/DataSeeder.php`:
```php
$rows = [
    ['name' => 'New Setting', 'value' => 'default', 'sort_order' => 10],
];
foreach ($rows as $row) {
    $this->firstOrCreateSetting(array_merge([
        'namespace' => Namespaces::CLIENT,
        'group' => Groups::COMPANY,
        'type' => InputTypes::INPUT,
        'data' => DataTypes::STRING,
    ], $row));
}
```
2. Add getter method to SettingManager if needed
3. Run seeder to create setting
4. Update views to display setting

### Add a New Settings Page
1. Create controller in `src/Http/Controllers/Backend/`:
```php
class NewController extends AbstractController
{
    public function index(): View { return view('setting::backend.new.index'); }
    public function update(Request $request): RedirectResponse
    {
        UpdateSettings::execute($request->get('settings'));
        ClearCache::execute();
        return redirect()->route('backend.setting.new.index')->success(...);
    }
}
```
2. Add routes to `src/Routes/backend.php`:
```php
Route::as('new.')
    ->middleware('developer')
    ->prefix('new')
    ->group(function () {
        Route::get('/', [NewController::class, 'index'])->name('index');
        Route::post('update', [NewController::class, 'update'])->name('update');
    });
```
3. Create view in `src/Views/backend/new/index.blade.php`

### Create a New Listener
1. Create listener class in `src/Listeners/`:
```php
class UpdateModuleStatus
{
    public function handle(SettingUpdated $event): void
    {
        $setting = $event->setting;
        if ($setting->full_key === 'package::module.module_name') {
            Package::where('key', 'module')->update(['is_active' => $setting->isEnabled()]);
        }
    }
}
```
2. Register in SettingServiceProvider:
```php
protected array $listeners = [
    SettingUpdated::class => [
        UpdateModuleStatus::class,
    ],
];
```

### Access Settings in Code
```php
// Get setting value
$phone = setting('client::company.phone');
$primary = setting('theme::color.primary', '#BED62F');

// Get SettingManager instance
$manager = setting();
$enabled = $manager->reCaptchaEnabled();
$areas = $manager->getEstimateAreas();

// Check if setting exists
if (setting()->has('theme::layout.header')) { }

// Get all settings
$all = setting()->all();
$byNamespace = setting()->getByNamespace('theme');
```

### Compile CSS with Settings
```php
use Bongo\Setting\Actions\CompileCss;

$action = new CompileCss();
$action->execute();  // Compiles SCSS with theme variables to public/css/frontend.css
```

## Testing

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

Test configuration in `phpunit.xml` with DB_CONNECTION=testing.

## Code Style

Use Laravel Pint for formatting:
```bash
vendor/bin/pint --test  # Check issues
vendor/bin/pint         # Fix issues
```

## Dependencies

- **bongo/framework** (^3.0): Base AbstractServiceProvider, AbstractModel, AbstractController
- **scssphp/scssphp** (^1.11): SCSS compilation for theme CSS
- Depends on: bongo/menu (ClearMenuCache listener), bongo/package (Package model), bongo/referrer (Referrer service), bongo/builder (LayoutFacade)

## Key Files Reference

| File | Purpose |
|------|---------|
| `src/SettingServiceProvider.php` | Registers service, event listeners, helper |
| `src/Models/Setting.php` | Setting model with hierarchical keys |
| `src/Services/SettingManager.php` | Singleton service for accessing settings |
| `src/helpers.php` | Global setting() helper function |
| `src/Actions/UpdateSettings.php` | Updates settings and fires events |
| `src/Actions/CompileCss.php` | Compiles SCSS with theme variables |
| `src/Seeders/DataSeeder.php` | Seeds default settings |
| `src/Routes/backend.php` | Admin settings routes |

## Extension Points

1. **Add new namespaces**: Update `src/Interfaces/Namespaces.php`
2. **Add new groups**: Update `src/Interfaces/Groups.php`
3. **Add new input types**: Update `src/Interfaces/InputTypes.php` and add trait methods to HasType.php
4. **Listen to setting changes**: Add listeners to SettingServiceProvider $listeners array
5. **Add business logic**: Extend SettingManager with new helper methods
6. **Custom CSS/JS**: Use setCustomCss(), setCustomJs() to write files programmatically
