8 Commits

Author SHA1 Message Date
Genex1s
091d60f1da fixing final untuk adminkredit, logic query sudah sesuai dengan requirement dan memunculkan hanya perubahan tipe dokumen untuk adminkredit 2026-01-05 18:25:31 +07:00
Genex1s
7a21698f02 Menambahkan logic untuk adminkredit agar query nya hanya menampilakn perubahan terhadap tipe dokumen 2026-01-05 17:19:31 +07:00
Genex1s
1f73feda22 Menghapus icon check dan cross di Audit Logs(karena inkonsistensi ukuran) 2025-12-31 16:52:54 +07:00
Genex1s
b610e46326 menambahkan permisssion untuk user auditor agar bisa membuka ADK Logs 2025-12-30 17:35:14 +07:00
Genex1s
c74ccfa44f Menambahkan role adminkredit di parent Logs module.json 2025-12-24 17:23:50 +07:00
Genex1s
9da61e862a Menambahkan dokumenLogs untuk modul adminKredit. Masih diperlukan fixing lebih lanjut agar adminKredit bisa mengakses menu tersebut melalui sidebar.
AuditLogsController: Menambahkan function untuk datatables dan index baru dengan logic query yang berbeda dari datatable yang default

new page: adminkredit.blade

web: menambahkan rute baru untuk page audit trail dokumen ADK
2025-12-24 17:09:46 +07:00
Daeng Deni Mardaeni
db364c5877 feat(logs): optimalkan middleware dan seeder izin log
- Memperbarui `AuditLogsController`:
  - Menambahkan middleware `auth` untuk melindungi akses kontrol.
  - Menggunakan closure pada middleware untuk menetapkan properti `$user` setelah otentikasi berhasil.
- Memperbarui `SystemLogsController`:
  - Menambahkan middleware `auth` untuk melindungi akses kontrol sistem log.
  - Menggunakan closure pada middleware untuk menetapkan properti `$user` setelah otentikasi berhasil.
- Memperbarui `PermissionSeeder`:
  - Menghapus logika pembuatan izin `crudActions` agar kode lebih sederhana dan efisien.
  - Menghapus metode `crudActions` untuk tindakan CRUD spesifik.
  - Menyederhanakan pembuatan grup izin hanya menggunakan daftar utama (`audit-logs`, `system-logs`).
2025-06-22 20:54:51 +07:00
Daeng Deni Mardaeni
cf120c035e feat(logs): tambahkan validasi akses pengguna dan seeder izin untuk Audit dan System Logs
- Memperbarui `AuditLogsController`:
  - Menambahkan validasi akses pengguna untuk memastikan pengguna hanya dapat melihat log audit jika memiliki izin 'audit-logs.read'.
  - Menambahkan properti `$user` dalam konstruktor untuk otorisasi.
- Memperbarui `SystemLogsController`:
  - Menambahkan validasi akses pengguna untuk memastikan pengguna hanya dapat melihat log sistem jika memiliki izin 'system-logs.read'.
  - Menambahkan properti `$user` dalam konstruktor untuk otorisasi.
- Menambahkan `PermissionSeeder`:
  - Membuat seeder untuk menginisialisasi izin terkait log, termasuk `system-logs` dan `audit-logs`.
  - Menyertakan izin CRUD lengkap (`create`, `read`, `update`, `delete`, dll.) untuk masing-masing grup.
- Memperbarui `LogsDatabaseSeeder`:
  - Mendaftarkan `PermissionSeeder` dalam daftar pemanggilan untuk migrasi izin saat seeding.

Signed-off-by: Daeng Deni Mardaeni <ddeni05@gmail.com>
2025-06-22 18:59:13 +07:00
8 changed files with 618 additions and 36 deletions

View File

@@ -6,19 +6,214 @@
use Illuminate\Http\Request;
use Modules\Usermanagement\Models\User;
use Spatie\Activitylog\Models\Activity;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Modules\Adk\Models\JenisBuktiKepemilikan;
class AuditLogsController extends Controller
{
protected $user;
public function __construct()
{
// Mengatur middleware auth
$this->middleware('auth');
// Mengatur user setelah middleware auth dijalankan
$this->middleware(function ($request, $next) {
$this->user = Auth::user();
return $next($request);
});
}
/**
* Display a listing of the resource.
*/
public function index()
{
// Check if the authenticated user has the required permission to view audit logs
if (is_null($this->user) || !$this->user->can('audit-logs.read')) {
abort(403, 'Sorry! You are not allowed to view audit logs.');
}
return view('logs::audit');
}
public function indexAdminKredit()
{
// Check if the authenticated user has the required permission to view audit logs
if (is_null($this->user) || !$this->user->can('audit-logs.read')) {
abort(403, 'Sorry! You are not allowed to view audit logs.');
}
return view('logs::adminkredit');
}
public function datatableAdminKredit(Request $request)
{
// Check if the authenticated user has the required permission to view audit logs
if (is_null($this->user) || !$this->user->can('audit-logs.read')) {
abort(403, 'Sorry! You are not allowed to view audit logs.');
}
// Retrieve data from the database
$query = Activity::query()
->where('log_name','ADK')
->where('subject_type', 'LIKE', '%Dokumen%');
// 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('log_name', 'LIKE', "%$search%")
->orWhere('description', 'LIKE', "%$search%")
->orWhere('subject_id', 'LIKE', "%$search%")
->orWhere('subject_type', 'LIKE', "%$search%")
->orWhere('causer_id', 'LIKE', "%$search%")
->orWhere('properties', 'LIKE', "%$search%");
});
}
// Apply sorting if provided
if ($request->has('sortOrder') && !empty($request->get('sortOrder'))) {
$order = $request->get('sortOrder');
$column = $request->get('sortField');
$query->orderBy($column, $order);
} else {
// Default sorting by created_at descending
$query->orderBy('created_at', 'desc');
}
// Get the total count of records before pagination
$totalRecords = Activity::count();
// Get the filtered count before pagination
$filteredRecords = $query->count();
// Apply pagination if provided
if ($request->has('page') && $request->has('size')) {
$page = $request->get('page');
$size = $request->get('size');
$offset = ($page - 1) * $size; // Calculate the offset
$query->skip($offset)->take($size);
}
// Get the data for the current page
$data = $query->get();
// Map causer_id to creator name
$data = $data->map(function ($item) {
// Map creator name first
if ($item->causer_id && $item->causer_type === 'Modules\\Usermanagement\\Models\\User') {
$user = User::find($item->causer_id);
$item->creator_name = $user ? $user->name : 'Unknown User';
} else {
$item->creator_name = 'System';
}
// Apply filtering ONLY for adminkredit role
if (auth()->user()->hasRole('adminkredit')) {
// For adminkredit, we ONLY care about specific field changes
// Handle DokumenJaminan - ONLY show if jenis_bukti_kepemilikan_id changed
if ($item->subject_type === 'Modules\\Adk\\Models\\DokumenJaminan') {
if ($item->properties) {
$props = json_decode($item->properties, true);
if (isset($props['old']['jenis_bukti_kepemilikan_id'],
$props['attributes']['jenis_bukti_kepemilikan_id'])) {
$oldValue = $props['old']['jenis_bukti_kepemilikan_id'];
$newValue = $props['attributes']['jenis_bukti_kepemilikan_id'];
// Only return if this specific field changed
if ($oldValue !== $newValue) {
$before = JenisBuktiKepemilikan::find($oldValue)?->name;
$after = JenisBuktiKepemilikan::find($newValue)?->name;
$props['old']['jenis_bukti_kepemilikan_id'] = $before;
$props['attributes']['jenis_bukti_kepemilikan_id'] = $after;
$item->properties = $props;
return $item; // ✅ Return only if field changed
}
}
}
// If jenis_bukti_kepemilikan_id didn't change, skip this item
return null;
}
// Handle DokumenLegal and DokumenPendukung - ONLY show if nama_dokumen changed
else if ($item->subject_type === 'Modules\\Adk\\Models\\DokumenLegal' ||
$item->subject_type === 'Modules\\Adk\\Models\\DokumenPendukung') {
$props = is_array($item->properties)
? $item->properties
: json_decode($item->properties, true);
if (!is_array($props)) {
return null; // Skip invalid data
}
// Only return if nama_dokumen changed
if (isset($props['old']['nama_dokumen'], $props['attributes']['nama_dokumen'])
&& $props['old']['nama_dokumen'] !== $props['attributes']['nama_dokumen']) {
$item->properties = $props;
return $item; // ✅ Return only if nama_dokumen changed (regardless of description)
}
// If nama_dokumen didn't change, skip this item
return null;
}
// For other document types that adminkredit can see
else {
// Skip 'updated' description for other types
if ($item->description === 'updated') {
return null;
}
return $item;
}
}
// For NON-adminkredit users (Auditor, etc.) - NO FILTERING, return everything
return $item;
});
// Remove null values and re-index (only affects adminkredit filtered items)
$data = $data->filter()->values();
// IMPORTANT: Recalculate filtered records count after filtering
$filteredRecords = $data->count();
// Calculate the page count based on NEW filtered count
$pageCount = ceil($filteredRecords / ($request->get('size') ?: 1));
// Calculate the current page number
$currentPage = $request->get('page') ?: 1;
Log::info("Data :", $data->pluck('id')->toArray());
// Return the response data as a JSON object
return response()->json([
'draw' => $request->get('draw'),
'recordsTotal' => $totalRecords,
'recordsFiltered' => $filteredRecords,
'pageCount' => $pageCount,
'page' => $currentPage,
'totalCount' => $filteredRecords,
'data' => $data,
]);
}
public function datatable(Request $request)
{
// Check if the authenticated user has the required permission to view audit logs
if (is_null($this->user) || !$this->user->can('audit-logs.read')) {
abort(403, 'Sorry! You are not allowed to view audit logs.');
}
// Retrieve data from the database
$query = Activity::query();
@@ -97,6 +292,5 @@
'totalCount' => $filteredRecords,
'data' => $data,
]);
}
}

View File

@@ -6,14 +6,24 @@ use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Jackiedo\LogReader\Exceptions\UnableToRetrieveLogFilesException;
use Jackiedo\LogReader\LogReader;
use Illuminate\Support\Facades\Auth;
class SystemLogsController extends Controller
{
protected $reader;
protected $user;
public function __construct(LogReader $reader)
{
$this->reader = $reader;
// Mengatur middleware auth
$this->middleware('auth');
// Mengatur user setelah middleware auth dijalankan
$this->middleware(function ($request, $next) {
$this->user = Auth::user();
return $next($request);
});
}
/**
@@ -21,10 +31,20 @@ class SystemLogsController extends Controller
*/
public function index()
{
// Check if the authenticated user has the required permission to view system logs
if (is_null($this->user) || !$this->user->can('system-logs.read')) {
abort(403, 'Sorry! You are not allowed to view system logs.');
}
return view('logs::system');
}
public function datatable(Request $request){
// Check if the authenticated user has the required permission to view system logs
if (is_null($this->user) || !$this->user->can('system-logs.read')) {
abort(403, 'Sorry! You are not allowed to view system logs.');
}
$data = collect();
$this->reader->setLogPath(storage_path('logs'));
try {

View File

@@ -11,6 +11,8 @@ class LogsDatabaseSeeder extends Seeder
*/
public function run(): void
{
// $this->call([]);
$this->call([
PermissionSeeder::class
]);
}
}

View File

@@ -0,0 +1,33 @@
<?php
namespace Modules\Logs\Database\Seeders;
use Illuminate\Database\Seeder;
use Illuminate\Support\Str;
use Modules\Usermanagement\Models\PermissionGroup;
class PermissionSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run()
{
$data = $this->data();
foreach ($data as $value) {
PermissionGroup::updateOrCreate([
'name' => $value['name'],
'slug' => Str::slug($value['name'])
]);
}
}
public function data()
{
return [
['name' => 'system-logs'],
['name' => 'audit-logs'],
];
}
}

View File

@@ -21,7 +21,9 @@
"attributes": [],
"permission": "",
"roles": [
"administrator"
"administrator",
"adminkredit",
"auditor"
],
"sub": [
{
@@ -43,6 +45,19 @@
"roles": [
"administrator"
]
},
{
"title": "Dokumen ADK Logs",
"path": "logs.dokumen",
"classes": "",
"attributes": [],
"permission": "",
"roles": [
"administrator",
"adminkredit",
"auditor"
]
}
]
}

View File

@@ -0,0 +1,345 @@
@extends('layouts.main')
@section('breadcrumbs')
{{ Breadcrumbs::render('logs.audit') }}
@endsection
@section('content')
<div class="grid">
<div class="card card-grid min-w-full" data-datatable="false" data-datatable-page-size="10"
data-datatable-state-save="false" id="audit-logs-table" data-api-url="{{ route('logs.audit.datatablesAdminKredit') }}">
<div class="card-header py-5 flex-wrap">
<h3 class="card-title">
Audit Logs
</h3>
<div class="flex flex-wrap gap-2 lg:gap-5">
<div class="flex">
<label class="input input-sm"> <i class="ki-filled ki-magnifier"> </i>
<input placeholder="Search Logs" id="search" type="text" value="">
</label>
</div>
</div>
</div>
<div class="card-body">
<div class="scrollable-x-auto">
<table class="table table-auto table-border align-middle text-gray-700 font-medium text-sm"
data-datatable-table="true">
<thead>
<tr>
<th class="min-w-[100px]" data-datatable-column="log_name">
<span class="sort"> <span class="sort-label"> Log Type </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[100px]" data-datatable-column="subject_type">
<span class="sort"> <span class="sort-label"> Subject Type </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="description">
<span class="sort"> <span class="sort-label"> Tipe Dokumen </span>
<span class="sort-icon"> </span> </span>
</th>
{{-- <th class="min-w-[150px]" data-datatable-column="properties">
<span class="sort"> <span class="sort-label"> Properties </span>
<span class="sort-icon"> </span> </span>
</th> --}}
<th class="min-w-[300px]" data-datatable-column="changes">
<span class="sort"> <span class="sort-label"> Perubahan </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[150px]" data-datatable-column="causer_type">
<span class="sort"> <span class="sort-label"> User Role </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[100px]" data-datatable-column="causer_id">
<span class="sort"> <span class="sort-label"> Username </span>
<span class="sort-icon"> </span> </span>
</th>
<th class="min-w-[100px]" data-datatable-column="created_at">
<span class="sort"> <span class="sort-label"> Date/Time </span>
<span class="sort-icon"> </span> </span>
</th>
</tr>
</thead>
</table>
</div>
<div
class="card-footer justify-center md:justify-between flex-col md:flex-row gap-3 text-gray-600 text-2sm font-medium">
<div class="flex items-center gap-2">
Show
<select class="select select-sm w-16" data-datatable-size="true" name="perpage"> </select> per page
</div>
<div class="flex items-center gap-4">
<span data-datatable-info="true"> </span>
<div class="pagination" data-datatable-pagination="true">
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script type="module">
const element = document.querySelector('#audit-logs-table');
const searchInput = document.getElementById('search');
const apiUrl = element.getAttribute('data-api-url');
const dataTableOptions = {
apiEndpoint: apiUrl,
pageSize: 10,
columns: {
log_name: {
title: 'Log Type',
render: (item, data) => {
return `<span class="badge badge-light-primary">${data.log_name || 'N/A'}</span>`;
}
},
description: {
title: 'Description',
},
subject_type: {
title: 'Subject Type',
render: (item, data) => {
if (!data.subject_type) return 'N/A';
return data.subject_type.split('\\').pop();
}
},
// properties:{
// title: 'Properties',
// render: (item, data) => {
// if (!data.properties) return 'N/A';
// // Generate a unique ID for this property
// const propertyId = `property-${data.id || Math.random().toString(36).substr(2, 9)}`;
// // Create a shortened preview (first 50 characters)
// const preview = JSON.stringify(data.properties).substring(0, 50) + (JSON.stringify(data.properties).length > 50 ? '...' : '');
// // Return HTML with expand/collapse functionality using Tailwind classes
// return `
// <div class="relative w-full">
// <div class="flex justify-between items-center w-full">
// <div class="max-w-[calc(100%-50px)] whitespace-nowrap overflow-hidden text-ellipsis" id="preview-${propertyId}">${preview}</div>
// <div class="hidden max-h-[300px] overflow-y-auto bg-gray-100 p-2.5 rounded mt-1.5 w-full" id="full-${propertyId}">
// <pre class="m-0 whitespace-pre-wrap break-words">${JSON.stringify(data.properties, null, 2)}</pre>
// </div>
// <div class="flex items-center ml-2.5">
// <button type="button" class="btn btn-sm btn-outline btn-icon btn-info expand-property"
// data-property-id="${propertyId}" id="expand-${propertyId}">
// <i class="ki-duotone ki-arrow-down fs-7"></i>
// </button>
// <button type="button" class="btn btn-sm btn-outline btn-icon btn-info collapse-property hidden"
// data-property-id="${propertyId}" id="collapse-${propertyId}">
// <i class="ki-duotone ki-arrow-up fs-7"></i>
// </button>
// </div>
// </div>
// </div>
// `;
// }
// },
changes: {
title: 'Changes',
render: (item, data) => {
// Check if properties exists and contains old/attributes
if (!data.properties) return 'N/A';
// Parse properties if it's a string, otherwise use it directly
let properties = data.properties;
if (typeof properties === 'string') {
try {
properties = JSON.parse(properties);
} catch (e) {
return 'N/A';
}
}
// The old and attributes are inside properties object
const oldData = properties.old || {};
const newData = properties.attributes || {};
// Check if both objects are empty
if (Object.keys(oldData).length === 0 && Object.keys(newData).length === 0) {
return 'N/A';
}
// Fields to exclude from changes display
const excludedFields = ['updated_at', 'file_path', 'file_name', 'file_size', 'file_type'];
// Compute only the changed fields (excluding specific fields)
const diffs = {};
Object.keys(newData).forEach(key => {
if (!excludedFields.includes(key) && JSON.stringify(oldData[key]) !== JSON.stringify(newData[key])) {
diffs[key] = { old: oldData[key], new: newData[key] };
}
});
if (Object.keys(diffs).length === 0) return 'No changes';
// Generate unique ID for expand/collapse
const changeId = `change-${data.id || Math.random().toString(36).substr(2, 9)}`;
// Short preview showing count of changes
const changeCount = Object.keys(diffs).length;
const firstKey = Object.keys(diffs)[0];
// const preview = `<span class="font-medium text-blue-700">${changeCount}</span> field${changeCount > 1 ? 's' : ''} changed: <span class="font-semibold">"${firstKey}"</span>${changeCount > 1 ? ', ...' : ''}`;
const preview = `<span class="font-medium text-blue-700">${changeCount}</span> data diubah: <span class="font-semibold">"${firstKey}"</span>${changeCount > 1 ? ', ...' : ''}`;
// Build Before/After HTML with better styling
let fullHtml = '<div class="grid grid-cols-2 gap-4">';
// Before column
fullHtml += '<div class="bg-red-50 dark:bg-red-900/20 rounded-lg p-3">';
fullHtml += '<div class="flex items-center gap-2 mb-3">';
fullHtml += '<strong class="text-red-700 dark:text-red-400 text-base">Before</strong>';
fullHtml += '</div>';
fullHtml += '<div class="space-y-2">';
Object.keys(diffs).forEach(key => {
fullHtml += `
<div class="bg-white dark:bg-gray-800 rounded p-2 border-l-4 border-red-500">
<div class="font-semibold text-sm text-gray-700 dark:text-gray-300 mb-1">${key}</div>
<pre class="m-0 whitespace-pre-wrap break-words text-sm text-gray-600 dark:text-gray-400">${JSON.stringify(diffs[key].old, null, 2)}</pre>
</div>
`;
});
fullHtml += '</div></div>';
// After column
fullHtml += '<div class="bg-green-50 dark:bg-green-900/20 rounded-lg p-3">';
fullHtml += '<div class="flex items-center gap-2 mb-3">';
fullHtml += '<strong class="text-green-700 dark:text-green-400 text-base">After</strong>';
fullHtml += '</div>';
fullHtml += '<div class="space-y-2">';
Object.keys(diffs).forEach(key => {
fullHtml += `
<div class="bg-white dark:bg-gray-800 rounded p-2 border-l-4 border-green-500">
<div class="font-semibold text-sm text-gray-700 dark:text-gray-300 mb-1">${key}</div>
<pre class="m-0 whitespace-pre-wrap break-words text-sm text-gray-600 dark:text-gray-400">${JSON.stringify(diffs[key].new, null, 2)}</pre>
</div>
`;
});
fullHtml += '</div></div>';
fullHtml += '</div>';
// Return the collapsible block with improved styling
return `
<div class="relative w-full">
<div class="flex justify-between items-center w-full gap-3">
<div class="flex-1 min-w-0 text-sm" id="preview-${changeId}">
${preview}
</div>
<div class="flex-shrink-0">
<button type="button" class="btn btn-sm btn-outline btn-icon btn-info expand-change"
data-change-id="${changeId}" id="expand-${changeId}">
<i class="ki-duotone ki-arrow-down fs-7"></i>
</button>
<button type="button" class="btn btn-sm btn-outline btn-icon btn-info collapse-change hidden"
data-change-id="${changeId}" id="collapse-${changeId}">
<i class="ki-duotone ki-arrow-up fs-7"></i>
</button>
</div>
</div>
<div class="hidden max-h-[400px] overflow-y-auto rounded-lg mt-3 shadow-sm border border-gray-200 dark:border-gray-700" id="full-${changeId}">
${fullHtml}
</div>
</div>
`;
}
},
causer_type: {
title: 'Causer Type',
render: (item, data) => {
if (!data.causer_type) return 'System';
return data.causer_type.split('\\').pop();
}
},
causer_id: {
title: 'Causer ID',
render: (item, data) => {
return data.creator_name || 'System';
}
},
created_at: {
title: 'Date/Time',
render: (item, data) => {
const date = new Date(data.created_at);
return window.formatTanggalWaktuIndonesia(date)
}
}
},
};
let dataTable = new KTDataTable(element, dataTableOptions);
// Add event delegation for expand/collapse buttons
document.querySelector('#audit-logs-table').addEventListener('click', function(e) {
// Handle expand button click
if (e.target.closest('.expand-property')) {
const button = e.target.closest('.expand-property');
const propertyId = button.getAttribute('data-property-id');
// Show full property and hide preview
document.getElementById(`preview-${propertyId}`).style.display = 'none';
document.getElementById(`full-${propertyId}`).style.display = 'block';
// Toggle buttons
document.getElementById(`expand-${propertyId}`).style.display = 'none';
document.getElementById(`collapse-${propertyId}`).style.display = 'inline-flex';
}
// Handle collapse button click
if (e.target.closest('.collapse-property')) {
const button = e.target.closest('.collapse-property');
const propertyId = button.getAttribute('data-property-id');
// Hide full property and show preview
document.getElementById(`preview-${propertyId}`).style.display = 'block';
document.getElementById(`full-${propertyId}`).style.display = 'none';
// Toggle buttons
document.getElementById(`expand-${propertyId}`).style.display = 'inline-flex';
document.getElementById(`collapse-${propertyId}`).style.display = 'none';
}
if (e.target.closest('.expand-change')) {
const button = e.target.closest('.expand-change');
const changeId = button.getAttribute('data-change-id');
// Show full change and hide preview
document.getElementById(`preview-${changeId}`).style.display = 'none';
document.getElementById(`full-${changeId}`).style.display = 'block';
// Toggle buttons
document.getElementById(`expand-${changeId}`).style.display = 'none';
document.getElementById(`collapse-${changeId}`).style.display = 'inline-flex';
}
if (e.target.closest('.collapse-change')) {
const button = e.target.closest('.collapse-change');
const changeId = button.getAttribute('data-change-id');
// Hide full change and show preview
document.getElementById(`preview-${changeId}`).style.display = 'block';
document.getElementById(`full-${changeId}`).style.display = 'none';
// Toggle buttons
document.getElementById(`expand-${changeId}`).style.display = 'inline-flex';
document.getElementById(`collapse-${changeId}`).style.display = 'none';
}
});
// Custom search functionality
searchInput.addEventListener('input', function() {
const searchValue = this.value.trim();
// Reset to page 1 when searching and then perform search
dataTable.goPage(1);
dataTable.search(searchValue, true);
});
window.dataTable = dataTable;
</script>
@endpush

View File

@@ -1,29 +0,0 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Logs Module - {{ config('app.name', 'Laravel') }}</title>
<meta name="description" content="{{ $description ?? '' }}">
<meta name="keywords" content="{{ $keywords ?? '' }}">
<meta name="author" content="{{ $author ?? '' }}">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
{{-- Vite CSS --}}
{{-- {{ module_vite('build-logs', 'resources/assets/sass/app.scss') }} --}}
</head>
<body>
@yield('content')
{{-- Vite JS --}}
{{-- {{ module_vite('build-logs', 'resources/assets/js/app.js') }} --}}
</body>

View File

@@ -1,11 +1,11 @@
<?php
use Illuminate\Support\Facades\Route;
use Modules\Logs\Http\Controllers\AuditLogsController;
use Modules\Logs\Http\Controllers\LogsController;
use Modules\Logs\Http\Controllers\SystemLogsController;
use Modules\Logs\Http\Controllers\AuditLogsController;
use Modules\Logs\Http\Controllers\LogsController;
use Modules\Logs\Http\Controllers\SystemLogsController;
/*
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
@@ -18,8 +18,10 @@ use Illuminate\Support\Facades\Route;
Route::group([], function () {
Route::name('logs.')->prefix('logs')->group(function () {
Route::get('dokumen', [AuditLogsController::class, 'indexAdminKredit'])->name('dokumen.index');
Route::name('audit.')->prefix('audit')->group(function () {
Route::get('datatables', [AuditLogsController::class, 'datatable'])->name('datatables');
Route::get('datatablesAdminKredit', [AuditLogsController::class, 'datatableAdminKredit'])->name('datatablesAdminKredit');
});
Route::resource('audit', AuditLogsController::class)->only(['index', 'delete']);