feat(branch): tambahkan fitur filter dan search pada eksport dan tabel cabang

- Menambahkan parameter `search` dan `parent_id` pada `BranchExport` untuk mendukung fitur filter.
- Memodifikasi method `collection` di `BranchExport` agar mendukung filter pencarian dan parent cabang.
- Memperbaiki issue pada method `collection` terkait penggunaan query `LOWER` untuk pencarian.
- Mengubah method `export` di `BranchController` agar menerima parameter filter dari request.
- Menambahkan logika filtering untuk `search` dan `parent_id` pada method index API `BranchController`.
- Menambahkan dropdown filter parent di tampilan `branch/index.blade.php`.
- Implementasi JavaScript di `branch/index.blade.php` untuk mendukung filter pencarian dan parent cabang.
  - Menambahkan logika sinkronisasi URL eksport dengan parameter filter.
  - Menambahkan event listener untuk filter pencarian dan dropdown parent.
  - Menambahkan validasi agar filter diterapkan ke datatable dan URL eksport secara dinamis.
- Memperbaiki penghitungan halaman pagination di datatable.
- Penyesuaian minor pada model Branch dan cara logging aktivitas di model Base.

Fitur ini memungkinkan pengguna melakukan filter data cabang berdasarkan pencarian dan parent cabang saat menampilkan tabel ataupun mengekspor data ke Excel.

Signed-off-by: Daeng Deni Mardaeni <ddeni05@gmail.com>
This commit is contained in:
Daeng Deni Mardaeni
2025-05-19 08:56:44 +07:00
parent 4a644c3b5d
commit 0bb12812a5
5 changed files with 119 additions and 18 deletions

View File

@@ -9,11 +9,38 @@
use Modules\Basicdata\Models\Branch;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
class BranchExport implements WithColumnFormatting, WithHeadings, FromCollection, withMapping
class BranchExport implements WithColumnFormatting, WithHeadings, FromCollection, WithMapping
{
protected $search;
public function __construct($search = null, $parent_id = null)
{
$this->search = $search;
$this->parent_id = $parent_id;
}
public function collection()
{
return Branch::all();
$query = Branch::query();
if (!empty($this->search)) {
$search = strtolower($this->search);
$query->where(function ($q) use ($search) {
$q->whereRaw('LOWER(code) LIKE ?', ['%' . $search . '%'])
->orWhereRaw('LOWER(name) LIKE ?', ['%' . $search . '%'])
->orWhereHas('parent', function ($q) use ($search) {
$q->whereRaw('LOWER(name) LIKE ?', ['%' . strtolower($search) . '%']);
});
});
}
// Apply parent filter if provided
if (isset($this->parent_id) && !empty($this->parent_id)) {
$parentId = $this->parent_id;
$query->where('parent_id', $parentId);
}
return $query->get();
}
public function map($row)
@@ -23,6 +50,7 @@
$row->id,
$row->code,
$row->name,
$row->parent ? $row->parent->name : '',
$row->created_at
];
}
@@ -34,6 +62,7 @@
'ID',
'Code',
'Name',
'Parent Branch',
'Created At'
];
}

View File

@@ -182,11 +182,24 @@
// Apply search filter if provided
if ($request->has('search') && !empty($request->get('search'))) {
$search = $request->get('search');
$query->where(function ($q) use ($search) {
$q->where('code', 'LIKE', "%$search%");
$q->orWhere('name', 'LIKE', "%$search%");
$search = json_decode($request->get('search'));
if(isset($search->search)) {
$search_ = strtolower($search->search);
$query->where(function ($q) use ($search_) {
$q->whereRaw('LOWER(code) LIKE ?', ['%' . strtolower($search_) . '%']);
$q->orWhereRaw('LOWER(name) LIKE ?', ['%' . strtolower($search_) . '%']);
$q->orWhereHas('parent', function ($q) use ($search_) {
$q->whereRaw('LOWER(name) LIKE ?', ['%' . strtolower($search_) . '%']);
});
});
}
// Apply parent filter if provided
if (isset($search->parent_id) && !empty($search->parent_id)) {
$parentId = $search->parent_id;
$query->where('parent_id', $parentId);
}
}
// Apply sorting if provided
@@ -227,7 +240,7 @@
$pageCount = ceil($totalRecords / $request->get('size'));
// Calculate the current page number
$currentPage = 0 + 1;
$currentPage = $request->get('page') ?: 1;
// Return the response data as a JSON object
@@ -242,13 +255,17 @@
]);
}
public function export()
public function export(Request $request)
{
// Check if the authenticated user has the required permission to export branches
if (is_null($this->user) || !$this->user->can('basic-data.export')) {
abort(403, 'Sorry! You are not allowed to export branches.');
}
return Excel::download(new BranchExport, 'branch.xlsx');
// Get search parameter from request
$search = $request->get('search');
$parentId = $request->get('parent_id');
return Excel::download(new BranchExport($search,$parentId), 'branch.xlsx');
}
}

View File

@@ -47,8 +47,6 @@
public function getActivitylogOptions()
: LogOptions
{
//CauserResolver::setCauser(auth()->user());
return LogOptions::defaults()->logAll()->useLogName('Basic Data');
}
}

View File

@@ -2,7 +2,6 @@
namespace Modules\Basicdata\Models;
class Branch extends Base
{
protected $table = 'branches';

View File

@@ -12,15 +12,21 @@
Daftar Cabang
</h3>
<div class="flex flex-wrap gap-2 lg:gap-5">
<div class="flex">
<div class="flex gap-2 lg:gap-5">
<label class="input input-sm"> <i class="ki-filled ki-magnifier"> </i>
<input placeholder="Search Branch" id="search" type="text" value="">
</label>
<select class="select select-sm" id="parent-filter" data-datatable-filter-column="parent_id">
<option value="">All Parent Branches</option>
@foreach(\Modules\Basicdata\Models\Branch::orderBy('name')->get() as $branch)
<option value="{{ $branch->id }}">{{ $branch->name }}</option>
@endforeach
</select>
</div>
<div class="flex flex-wrap gap-2.5">
<div class="flex flex-wrap gap-2 lg:gap-5">
<div class="h-[24px] border border-r-gray-200"></div>
@can('basic-data.export')
<a class="btn btn-sm btn-light" href="{{ route('basicdata.branch.export') }}"> Export to Excel </a>
<a id="export-btn" class="btn btn-sm btn-light" href="{{ route('basicdata.branch.export') }}"> Export to Excel </a>
@endcan
@can('basic-data.create')
<a class="btn btn-sm btn-primary" href="{{ route('basicdata.branch.create') }}"> Tambah Cabang </a>
@@ -149,7 +155,9 @@
<script type="module">
const element = document.querySelector('#branch-table');
const searchInput = document.getElementById('search');
const parentFilter = document.getElementById('parent-filter');
const deleteSelectedButton = document.getElementById('deleteSelected');
const exportBtn = document.getElementById('export-btn');
const apiUrl = element.getAttribute('data-api-url');
const dataTableOptions = {
@@ -200,12 +208,62 @@
};
let dataTable = new KTDataTable(element, dataTableOptions);
// Function to apply all filters
function applyFilters() {
let filters = {};
if (searchInput.value) {
filters.search = searchInput.value;
}
if (parentFilter.value) {
filters.parent_id = parentFilter.value;
}
dataTable.search(filters);
}
// Update export URL with filters
function updateExportUrl() {
let url = new URL(exportBtn.href);
if (parentFilter.value) {
url.searchParams.set('parent_id', parentFilter.value);
} else {
url.searchParams.delete('parent_id');
}
if (searchInput.value) {
url.searchParams.set('search', searchInput.value);
} else {
url.searchParams.delete('search');
}
exportBtn.href = url.toString();
}
// Custom search functionality
searchInput.addEventListener('input', function () {
const searchValue = this.value.trim();
dataTable.search(searchValue, true);
dataTable.goPage(1);
// Update export URL with search parameter
applyFilters();
updateExportUrl();
});
// Parent branch filter functionality
parentFilter.addEventListener('change', function() {
updateExportUrl();
applyFilters();
});
exportBtn.addEventListener('click', function() {
console.log('Exporting data...');
updateExportUrl();
applyFilters();
})
function updateDeleteButtonVisibility() {
const selectedCheckboxes = document.querySelectorAll('input[data-datatable-row-check="true"]:checked');
if (selectedCheckboxes.length > 0) {