# Lead Distribution Configuration Plan

## Scope

This document covers only the first part of Lead Distribution: configuration storage and management through Blade views.

It does not include actual lead assignment logic, round robin logic, percentage assignment calculation, manual assignment screens, queues, jobs, audit logs, or scheduled processing.

## Confirmed Repo Context

- This is a Laravel CRM app.
- Web controllers are organized under `App\Http\Controllers\Dashboard`.
- Dashboard form requests are organized under `App\Http\Requests\Dashboard`.
- Blade pages use `resources/views/dashboard/...` and extend `components.dashboard.layout.layout`.
- Authenticated dashboard routes live inside the main middleware group in `routes/web.php`.
- Existing settings-style pages include:
  - `dashboard.email-settings.index`
  - `dashboard.leads-reminder-settings.index`
- The CRM model prefix helper exists in `App\Models\CrmModel`, but not every settings table uses it.
- `App\Models\Staff` uses table `sm_staffs`.
- Active staff are filtered by `Staff` global scope using `active_status != 0`.
- `App\Models\User` uses table `users` and has a `staff()` relation.
- Existing lead assignees are users, but percentage rules in this task should reference staff records, so `staff_id` should point to `sm_staffs.id`.

## Business Constraints

- Do not add online/offline status.
- Do not use online/offline logic.
- Do not use `Staff.shift_type`.
- Do not query or filter by `shift_emps` in this configuration task.
- Shift membership through `shift_emps` is only future business context.
- This task saves configuration only.
- Actual distribution behavior will be implemented later in a dedicated service.

## Database Schema

### `lead_distribution_settings`

Use a singleton-style table with one global active row.

Columns:

- `id`
- `distribution_method` enum/string with allowed values:
  - `automatic`
  - `percentage`
  - `manual`
- `morning_shift_capacity` nullable unsigned integer
- `evening_shift_capacity` nullable unsigned integer
- `created_by` nullable foreign key to `users.id`
- `updated_by` nullable foreign key to `users.id`
- `created_at`
- `updated_at`

Notes:

- Manual mode stores `distribution_method = manual` and sets both capacities to `null`.
- Automatic and percentage modes require both capacity fields.

### `lead_distribution_percentage_rules`

Columns:

- `id`
- `lead_distribution_setting_id` foreign key to `lead_distribution_settings.id`
- `staff_id` foreign key to `sm_staffs.id`
- `percentage` decimal or unsigned decimal, recommended `decimal(5,2)`
- `created_at`
- `updated_at`

Indexes:

- Unique index on `lead_distribution_setting_id` and `staff_id`.

Foreign key behavior:

- `lead_distribution_setting_id`: cascade on delete.
- `staff_id`: restrict or cascade should be reviewed; recommended `restrictOnDelete()` or `nullOnDelete()` is not possible unless `staff_id` is nullable. Since a rule is invalid without staff, use restrict unless the project usually cascades.

## Models

### `App\Models\LeadDistributionSetting`

Recommended:

- Extend `Model` unless the team wants CRM prefix behavior through `CrmModel`.
- Use explicit table name `lead_distribution_settings`.
- Guard only `id` or define fillable fields.
- Add constants for distribution methods.

Relations:

```php
public function percentageRules()
{
    return $this->hasMany(LeadDistributionPercentageRule::class);
}

public function creator()
{
    return $this->belongsTo(User::class, 'created_by');
}

public function updater()
{
    return $this->belongsTo(User::class, 'updated_by');
}
```

### `App\Models\LeadDistributionPercentageRule`

Recommended:

- Use explicit table name `lead_distribution_percentage_rules`.
- Guard only `id` or define fillable fields.
- Cast `percentage` as decimal/float according to project preference.

Relations:

```php
public function setting()
{
    return $this->belongsTo(LeadDistributionSetting::class, 'lead_distribution_setting_id');
}

public function staff()
{
    return $this->belongsTo(Staff::class);
}
```

## Service

Create:

`App\Services\LeadDistributionConfigService`

Responsibilities:

- Fetch current singleton config with percentage rules and staff.
- Save config inside a DB transaction.
- Create the singleton row when missing.
- Update existing singleton row when present.
- Keep all mode-specific business behavior out of the controller.

Methods:

```php
public function getConfig(): ?LeadDistributionSetting
public function saveConfig(array $data, ?User $user = null): LeadDistributionSetting
protected function savePercentageRules(LeadDistributionSetting $setting, array $rules): void
protected function validateTotalPercentage(array $rules): void
```

Behavior by method:

- `automatic`
  - Save method.
  - Save morning and evening capacities.
  - Delete old percentage rules.
- `percentage`
  - Save method.
  - Save morning and evening capacities.
  - Validate total percentage is not greater than 100.
  - Delete old percentage rules.
  - Recreate submitted rules.
- `manual`
  - Save method.
  - Set capacities to `null`.
  - Delete old percentage rules.

User columns:

- On create, set `created_by` from the authenticated user if available.
- On every save, set `updated_by` from the authenticated user if available.

## Form Request

Create:

`App\Http\Requests\Dashboard\LeadDistribution\StoreLeadDistributionConfigRequest`

Recommended base class:

- Extend `App\Http\Requests\Dashboard\CrmAdminRequest` to match dashboard request style.

Rules:

- `distribution_method`: required, in `automatic,percentage,manual`.
- Capacities:
  - required when method is `automatic` or `percentage`
  - integer
  - min `1`
- `percentage_rules`:
  - required when method is `percentage`
  - array
- `percentage_rules.*.staff_id`:
  - required when method is `percentage`
  - distinct
  - exists in `sm_staffs,id`
  - active staff validation should require `active_status != 0`
- `percentage_rules.*.percentage`:
  - required when method is `percentage`
  - numeric
  - greater than `0`
  - max `100`

After validation:

- Sum all submitted percentages.
- Fail if total percentage exceeds `100`.

Manual mode:

- Capacities and rules are not required.
- If submitted, service ignores and clears them.

## Controller

Create:

`App\Http\Controllers\Dashboard\LeadDistribution\LeadDistributionConfigController`

Methods:

```php
public function edit(LeadDistributionConfigService $service)
public function update(StoreLeadDistributionConfigRequest $request, LeadDistributionConfigService $service)
```

`edit()`:

- Fetch current config with `LeadDistributionConfigService::getConfig()`.
- Fetch active staff for percentage dropdown.
- Return `dashboard.lead-distribution.config`.

Staff query:

```php
$staff = Staff::query()
    ->orderBy('full_name')
    ->get();
```

Because `Staff` has a global active scope, this excludes `active_status = 0`. Do not add online/offline filters. Do not use `shift_type`.

`update()`:

- Use validated data from the Form Request.
- Pass authenticated user to the service.
- Redirect back with success message.

## Routes

Add inside the existing authenticated dashboard middleware group in `routes/web.php`.

Suggested routes:

```php
Route::get('/lead-distribution/config', [LeadDistributionConfigController::class, 'edit'])
    ->name('lead-distribution.config.edit');

Route::post('/lead-distribution/config', [LeadDistributionConfigController::class, 'update'])
    ->name('lead-distribution.config.update');
```

Access control:

- The route group already uses SSO/autologin/check-in middleware.
- Admin/Moderator restriction needs review against this repo's permission/tab system.
- Existing code uses Spatie roles and `CheckPermission`, while some dashboard requests currently authorize with `return true`.
- Recommended review decision:
  - either add a dedicated permission and middleware,
  - or guard inside the controller/request with roles accepted by the project, likely `Super admin` and moderator job title.

## Blade View

Create:

`resources/views/dashboard/lead-distribution/config.blade.php`

Layout:

- Extend `components.dashboard.layout.layout`.
- Include `components.alert`.
- Use existing classes such as `container-fluid app-bg`, `page-shell`, `x-page-title`, `asset-btn`, `asset-input`, `asset-label`, and `ui-card` where practical.

Fields:

- Distribution Method select:
  - `automatic` => Automatic Distribution
  - `percentage` => Percentage-Based Distribution
  - `manual` => Manual Distribution
- Morning Shift Capacity number input.
- Evening Shift Capacity number input.
- Percentage Rules section:
  - staff select
  - percentage number input
  - remove button
  - add rule button
- Manual note:
  - `In manual mode, leads will remain unassigned until an Admin or Moderator assigns them manually.`

Form behavior:

- Preserve `old()` input after validation errors.
- Load existing saved values on first render.
- Load existing saved percentage rules when the saved method is `percentage`.
- Show field-level validation errors.
- Show success message after save.

JavaScript:

- Use plain JS or jQuery.
- Show capacity fields for `automatic` and `percentage`.
- Hide capacity fields for `manual`.
- Show percentage rules only for `percentage`.
- Show manual note only for `manual`.
- Support adding/removing rule rows.
- Optionally disable already selected staff in other rows, but backend validation remains required.

## Review Checklist Before Implementation

- Confirm whether `lead_distribution_settings` should have no `crm_` prefix. Current requested table names have no prefix, and `leads_reminder_settings` also has no CRM prefix.
- Confirm whether percentage rules should reference `sm_staffs.id` rather than `users.id`. Based on the task language and `Staff` model, use `sm_staffs.id`.
- Confirm allowed Admin/Moderator access mechanism:
  - route middleware permission,
  - Spatie role names,
  - staff job title slug,
  - or existing sidebar tab permission.
- Confirm whether a navigation/sidebar tab must be added now or route-only access is enough.
- Confirm foreign key delete behavior for staff rules.
- Confirm whether migration should seed an initial manual setting or let the service create the singleton on first save.

## Implementation Order

1. Add migrations for settings and percentage rules.
2. Add `LeadDistributionSetting` and `LeadDistributionPercentageRule` models.
3. Add `LeadDistributionConfigService`.
4. Add `StoreLeadDistributionConfigRequest`.
5. Add `LeadDistributionConfigController`.
6. Add routes in the dashboard authenticated group.
7. Add Blade view and dynamic JS.
8. Run syntax checks and route/migration sanity checks.
9. Manually review validation paths:
   - automatic saves capacities and clears rules
   - percentage saves capacities and rules
   - duplicate staff fails
   - total percentage above 100 fails
   - manual clears capacities and rules

