# PRD: Helpdesk Ticket Settings — Bug Fixes
**Module:** Helpdesk Ticket Settings (Ticket Types)
**Date:** 2026-04-30
**Source:** `helpdesk-ticket-settings-failed.csv` — 13 failing QA test cases

---

## Overview

13 test cases fail across six functional areas of the Ticket Types tab in Helpdesk Ticket Settings:
pagination (HTS-TT-005/006/007/008), search (HTS-TT-009), submit-button disabled state (HTS-TT-019),
duplicate-name validation (HTS-TT-024), Default Subject/Description toggle fields (HTS-TT-036/037),
and Auto Assign dropdowns not populating (HTS-TT-044/045/046/047).

---

## Fix 1 — Ticket Type Listing: Pagination & Items-Per-Page
**Covers:** HTS-TT-005, HTS-TT-006, HTS-TT-007, HTS-TT-008

### Root Cause
`index()` calls `$typeModel->getListing($srch_params)` without an `offset` key, so
`getListing()` always returns a full `Collection` via `->get()`. The types partial has no
pagination controls, no "Showing X–Y of Z" display, and no per-page selector.

### Files to Change

#### `app/Http/Controllers/Manages/HelpdeskTicketSettingsController.php` — `index()`

Inside the `if ($selectedTab == 'types')` block, extract `per_page` and pass it as `offset`:

```php
if ($selectedTab == 'types') {
    $typeModel = new MasterHelpdeskTicketType();
    $srch_params['with'] = ['category'];
    $srch_params['orderBy'] = 'master_helpdesk_ticket_types__display_order';

    $allowedPerPage = [10, 25, 50];
    $perPage = in_array((int)$request->input('per_page'), $allowedPerPage)
        ? (int)$request->input('per_page')
        : 10;

    $srch_params['offset'] = $perPage;
    $dataList = $typeModel->getListing($srch_params);

    $this->_data['perPage']        = $perPage;
    $this->_data['allowedPerPage'] = $allowedPerPage;
}
```

#### `resources/views/admin/manages/helpdesk-ticket-settings/partials/types.blade.php`

Add above the `<table>`:

```blade
{{-- Items-per-page + count --}}
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
    <div class="d-flex align-items-center gap-2">
        <label class="mb-0">Items per page:</label>
        <select id="ticket-types-per-page" class="form-control form-control-sm" style="width:80px;">
            @foreach($allowedPerPage as $size)
                <option value="{{ $size }}" {{ $perPage == $size ? 'selected' : '' }}>{{ $size }}</option>
            @endforeach
        </select>
    </div>
    @if($data->total() > 0)
        <span class="text-muted small">
            Showing {{ $data->firstItem() }}–{{ $data->lastItem() }} of {{ $data->total() }} ticket types
        </span>
    @endif
</div>
```

Add below the `</table>`:

```blade
{{-- Pagination links --}}
<div class="d-flex justify-content-end mt-3" id="ticket-types-pagination">
    {{ $data->appends(['per_page' => $perPage, 'tab' => 'types', 'search_text' => request('search_text')])->links() }}
</div>
```

Add IIFE JS at the bottom of the partial (inside `@push('page_script')` or as inline script):

```js
(function () {
    var perPageSel = document.getElementById('ticket-types-per-page');
    if (perPageSel) {
        perPageSel.addEventListener('change', function () {
            var url = new URL(window.location.href);
            url.searchParams.set('per_page', this.value);
            url.searchParams.set('tab', 'types');
            window.location.href = url.toString();
        });
    }
}());
```

---

## Fix 2 — Ticket Type Listing: Search
**Covers:** HTS-TT-009

### Root Cause
Server-side search IS implemented correctly (`getListing()` filters on `search_text`).
`common.js` correctly preserves URL params including `tab` when the user presses Enter in the
search field. The test failure is most likely because:
1. Without pagination (pre-Fix 1), all records were returned and no "no results" message
   helped diagnose mismatches; OR
2. Test data "Merchant Central Enrollment" was absent in the QA environment.

### Fix
No additional code change required beyond Fix 1. After Fix 1 is applied, search results will
display with proper pagination context. The `search_text` parameter is preserved in pagination
links by the `appends()` call added in Fix 1.

If, after Fix 1, search still fails, verify that `common.js` is included on the page (it is,
via `@push('page_script')` in `index.blade.php`) and that the search input has class
`module-search`.

---

## Fix 3 — Form: Add Button Disabled When Required Fields Are Empty
**Covers:** HTS-TT-019

### Root Cause
The Add/Update submit button rendered by the admin-form-wrapper has no disabled-state logic.
The form carries `custom-validation` class but no JS currently disables the button when
mandatory fields (specifically **Type Name**) are blank.

### Files to Change

#### `public/assets/js/helpdesk-ticket-type.js`

Add a block that disables the submit button when the title input is empty:

```js
(function () {
    function refreshSubmitBtn() {
        var titleVal = document.querySelector('input[name="title"]');
        var submitBtn = document.querySelector('.user-class-form [type="submit"]');
        if (!titleVal || !submitBtn) return;
        submitBtn.disabled = titleVal.value.trim() === '';
    }

    document.addEventListener('input', function (e) {
        if (e.target && e.target.name === 'title') {
            refreshSubmitBtn();
        }
    });

    // Initialise on modal open (form rendered inside modal)
    document.addEventListener('shown.bs.modal', function () {
        refreshSubmitBtn();
    });

    refreshSubmitBtn();
}());
```

---

## Fix 4 — Form Validation: Duplicate Ticket Type Name
**Covers:** HTS-TT-024

### Root Cause
`validateType()` validates format/presence only — no uniqueness check. `storeType()` and
`updateType()` both call `validateType()` then directly call `$model->store()` without checking
for an existing record with the same title.

### Files to Change

#### `app/Http/Controllers/Manages/HelpdeskTicketSettingsController.php`

In **`storeType()`**, add immediately after `$data = $this->validateType($request)`:

```php
$companyId = \Auth::user()->company_id;
$duplicate = \App\Models\Masters\MasterHelpdeskTicketType::whereRaw(
        'LOWER(title) = LOWER(?)', [$request->title]
    )
    ->where('company_id', $companyId)
    ->whereNull('deleted_at')
    ->exists();
if ($duplicate) {
    return back()->withInput()
        ->withErrors(['title' => 'A ticket type with this name already exists.']);
}
```

In **`updateType()`**, add immediately after `$data = $this->validateType($request)`:

```php
$duplicate = \App\Models\Masters\MasterHelpdeskTicketType::whereRaw(
        'LOWER(title) = LOWER(?)', [$request->title]
    )
    ->where('company_id', \Auth::user()->company_id)
    ->whereNull('deleted_at')
    ->where('id', '!=', $id)
    ->exists();
if ($duplicate) {
    return back()->withInput()
        ->withErrors(['title' => 'A ticket type with this name already exists.']);
}
```

---

## Fix 5 — Form Fields: Default Subject & Default Description Toggle
**Covers:** HTS-TT-036, HTS-TT-037

### Root Cause
Neither `default_subject_enabled`/`default_subject` nor `default_description_enabled`/
`default_description` exist in the DB, the model, the form definition, or the controller's
store/update logic.

### Files to Change

#### 1. Migration (new file)

`database/migrations/YYYY_MM_DD_HHMMSS_add_default_subject_description_to_master_helpdesk_ticket_types_table.php`

```php
public function up(): void
{
    Schema::table('master_helpdesk_ticket_types', function (Blueprint $table) {
        $table->boolean('default_subject_enabled')->default(false)->after('additional_information');
        $table->string('default_subject', 500)->nullable()->after('default_subject_enabled');
        $table->boolean('default_description_enabled')->default(false)->after('default_subject');
        $table->text('default_description')->nullable()->after('default_description_enabled');
    });
}

public function down(): void
{
    Schema::table('master_helpdesk_ticket_types', function (Blueprint $table) {
        $table->dropColumn(['default_subject_enabled','default_subject','default_description_enabled','default_description']);
    });
}
```

Run: `php artisan migrate`

#### 2. Model — `app/Models/Masters/MasterHelpdeskTicketType.php`

Add to `$fillable`:

```php
'default_subject_enabled', 'default_subject', 'default_description_enabled', 'default_description',
```

#### 3. Controller — `__formUiGenerationType()` — add fields to the `title_section` group's `fields`

After the `count_business_days` field definition, add:

```php
'default_subject_enabled' => [
    'type'    => 'checkbox',
    'label'   => '',
    'width'   => 'col-md-12',
    'options' => ['1' => 'Default Subject'],
    'value'   => !empty($id) && isset($data) && $data->default_subject_enabled ? ['1'] : [],
    'attributes' => ['name' => 'default_subject_enabled'],
],
'default_subject' => [
    'type'   => 'text',
    'label'  => 'Default Subject Text',
    'width'  => 'col-md-12',
    'value'  => !empty($id) && isset($data) ? ($data->default_subject ?? '') : '',
    'attributes' => [
        'placeholder'  => 'Enter default subject',
        'autocomplete' => 'off',
        'maxlength'    => 500,
    ],
],
'default_description_enabled' => [
    'type'    => 'checkbox',
    'label'   => '',
    'width'   => 'col-md-12',
    'options' => ['1' => 'Default Description'],
    'value'   => !empty($id) && isset($data) && $data->default_description_enabled ? ['1'] : [],
    'attributes' => ['name' => 'default_description_enabled'],
],
'default_description' => [
    'type'   => 'textarea',
    'label'  => 'Default Description Text',
    'width'  => 'col-md-12',
    'value'  => !empty($id) && isset($data) ? ($data->default_description ?? '') : '',
    'attributes' => [
        'placeholder' => 'Enter default description',
        'rows'        => 3,
    ],
],
```

#### 4. Controller — `validateType()` — add validation rules

```php
'default_subject_enabled'     => 'nullable|in:1',
'default_subject'             => 'nullable|string|max:500',
'default_description_enabled' => 'nullable|in:1',
'default_description'         => 'nullable|string',
```

#### 5. Controller — `storeType()` and `updateType()` — add to `$input` array

```php
'default_subject_enabled'     => $request->boolean('default_subject_enabled') ? 1 : 0,
'default_subject'             => $request->input('default_subject'),
'default_description_enabled' => $request->boolean('default_description_enabled') ? 1 : 0,
'default_description'         => $request->input('default_description'),
```

#### 6. JavaScript — `public/assets/js/helpdesk-ticket-settings.js`

Add toggle logic (the admin-form-wrapper convention gives checkboxes an `id` of `{name}-1`
and wraps each field row in an element with `id="row-{name}"`):

```js
// Default Subject toggle
$('#default_subject_enabled-1').on('change', function () {
    if ($(this).is(':checked')) {
        $('#row-default_subject').show();
    } else {
        $('#row-default_subject').hide();
        $('#default_subject').val('');
    }
});
if (!$('#default_subject_enabled-1').is(':checked')) {
    $('#row-default_subject').hide();
}

// Default Description toggle
$('#default_description_enabled-1').on('change', function () {
    if ($(this).is(':checked')) {
        $('#row-default_description').show();
    } else {
        $('#row-default_description').hide();
        $('#default_description').val('');
    }
});
if (!$('#default_description_enabled-1').is(':checked')) {
    $('#row-default_description').hide();
}
```

---

## Fix 6 — Auto Assign Dropdowns Populate Without Requiring Permission Roles First
**Covers:** HTS-TT-044, HTS-TT-045, HTS-TT-046, HTS-TT-047

### Root Cause
`helpdesk-ticket-type-auto-assign.js` builds the four Auto Assign selects by calling
`buildUserSelect(el, allowed)` and `buildClassSelect(el, allowed)` where `allowed` is the list
of **checked permission-role checkboxes**. When no checkboxes are checked, `allowed = []`,
`buildUserSelect` and `buildClassSelect` iterate an empty array → all four dropdowns are
completely empty. QA expects users and classes to be selectable without first checking
permission roles.

### File to Change

#### `public/assets/js/helpdesk-ticket-type-auto-assign.js` — `update()` function

Replace the body of `update()`:

```js
function update(){
    var allowed = getAllowedRoleIds();
    // When no permission roles are selected, expose all roles/users in the auto-assign selects
    var allRoleIds = Object.keys(roles);
    var effectiveAllowed = allowed.length > 0 ? allowed : allRoleIds;

    buildUserSelect(userEvery, effectiveAllowed);
    buildUserSelect(userRR, effectiveAllowed);
    buildClassSelect(classEvery, effectiveAllowed);
    buildClassSelect(classRR, effectiveAllowed);

    // Only prune previous selections when specific roles are actively chosen
    if (allowed.length > 0) {
        prune(userEvery, true, allowed);
        prune(userRR, true, allowed);
        prune(classEvery, false, allowed);
        prune(classRR, false, allowed);
    }

    applyInitialSelections();
}
```

**Behaviour after fix:**
- No permission roles checked → all users/classes appear in all four auto-assign dropdowns; any selection is accepted.
- One or more permission roles checked → only users/classes in those roles appear; previous selections outside those roles are pruned.

---

## Implementation Order

| # | Fix | Files Changed |
|---|-----|---------------|
| 1 | Migration (Default Subject/Desc) | new migration file |
| 2 | Model (fillable) | `MasterHelpdeskTicketType.php` |
| 3 | Controller — index() pagination | `HelpdeskTicketSettingsController.php` |
| 4 | Controller — validateType() rules | `HelpdeskTicketSettingsController.php` |
| 5 | Controller — storeType() duplicate check | `HelpdeskTicketSettingsController.php` |
| 6 | Controller — updateType() duplicate check | `HelpdeskTicketSettingsController.php` |
| 7 | Controller — storeType() / updateType() save subject/desc | `HelpdeskTicketSettingsController.php` |
| 8 | Controller — __formUiGenerationType() new fields | `HelpdeskTicketSettingsController.php` |
| 9 | Partial — types.blade.php pagination UI | `partials/types.blade.php` |
| 10 | JS — helpdesk-ticket-type.js submit-btn disabled | `helpdesk-ticket-type.js` |
| 11 | JS — helpdesk-ticket-settings.js subject/desc toggle | `helpdesk-ticket-settings.js` |
| 12 | JS — helpdesk-ticket-type-auto-assign.js update() | `helpdesk-ticket-type-auto-assign.js` |
| 13 | Run `php artisan migrate` | — |

---

## Test-Case Coverage Map

| Test Case | Fix Group | Change Type |
|-----------|-----------|-------------|
| HTS-TT-005 | Fix 1 | Controller + Blade partial |
| HTS-TT-006 | Fix 1 | Controller + Blade partial |
| HTS-TT-007 | Fix 1 | Controller + Blade partial |
| HTS-TT-008 | Fix 1 | Controller + Blade partial |
| HTS-TT-009 | Fix 2 | No code change (resolved by Fix 1) |
| HTS-TT-019 | Fix 3 | JS |
| HTS-TT-024 | Fix 4 | Controller (PHP) |
| HTS-TT-036 | Fix 5 | Migration + Model + Controller + JS |
| HTS-TT-037 | Fix 5 | Migration + Model + Controller + JS |
| HTS-TT-044 | Fix 6 | JS |
| HTS-TT-045 | Fix 6 | JS |
| HTS-TT-046 | Fix 6 | JS |
| HTS-TT-047 | Fix 6 | JS |
