1401 lines
56 KiB
PHP
1401 lines
56 KiB
PHP
@extends('layouts.backapp')
|
|
|
|
@section('styles')
|
|
<style>
|
|
/* Loading overlay - Modified to only cover content section */
|
|
#loading-overlay {
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.7);
|
|
z-index: 9999;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
flex-direction: column;
|
|
backdrop-filter: blur(2px);
|
|
}
|
|
|
|
#loading-overlay .spinner-border {
|
|
animation: spin 1s linear infinite;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% { transform: rotate(0deg); }
|
|
100% { transform: rotate(360deg); }
|
|
}
|
|
|
|
/* Make content section relative for absolute positioning of overlay */
|
|
.kt-content {
|
|
position: relative;
|
|
}
|
|
|
|
/* Button loading state */
|
|
.btn.loading {
|
|
opacity: 0.7;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.btn:disabled {
|
|
opacity: 0.6;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* Table styling */
|
|
.table th {
|
|
background-color: #f8f9fa;
|
|
border-color: #dee2e6;
|
|
font-weight: 600;
|
|
color: #495057;
|
|
}
|
|
|
|
.table td {
|
|
vertical-align: middle;
|
|
}
|
|
|
|
/* Mechanic column styling */
|
|
.mechanic-column {
|
|
background-color: #f8f9fa;
|
|
min-width: 120px;
|
|
text-align: center;
|
|
}
|
|
|
|
/* Form label styling */
|
|
.form-label {
|
|
font-weight: 600;
|
|
color: #595d6e;
|
|
margin-bottom: 8px;
|
|
font-size: 13px;
|
|
text-transform: none;
|
|
letter-spacing: normal;
|
|
display: block;
|
|
}
|
|
|
|
/* Filter buttons styling */
|
|
.filter-buttons {
|
|
display: flex;
|
|
gap: 10px;
|
|
}
|
|
.filter-buttons .btn {
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* Datepicker styling */
|
|
.datepicker {
|
|
width: 100% !important;
|
|
max-width: 100%;
|
|
}
|
|
.datepicker-dropdown {
|
|
width: auto !important;
|
|
min-width: 250px;
|
|
max-width: 300px;
|
|
}
|
|
/* Ensure input field follows parent width */
|
|
input.datepicker {
|
|
width: 100% !important;
|
|
box-sizing: border-box;
|
|
}
|
|
</style>
|
|
@endsection
|
|
|
|
@section('content')
|
|
<div class="kt-content" id="kt_content">
|
|
<div class="kt-portlet kt-portlet--mobile" id="kt_blockui_datatable">
|
|
<div class="kt-portlet__head kt-portlet__head--lg">
|
|
<div class="kt-portlet__head-label">
|
|
<h3 class="kt-portlet__head-title">
|
|
Laporan Teknisi
|
|
</h3>
|
|
</div>
|
|
<div class="kt-portlet__head-toolbar">
|
|
<div class="kt-portlet__head-actions">
|
|
<button type="button" class="btn btn-bold btn-success btn--sm" id="btn-export-excel">
|
|
Export
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="kt-portlet__body">
|
|
<!-- Filter Section -->
|
|
<div class="row">
|
|
<div class="col-md-3">
|
|
<div class="form-group">
|
|
<label class="form-label">Tanggal Mulai</label>
|
|
<input type="text" class="form-control datepicker" id="filter-start-date" placeholder="Tanggal awal" value="{{ date('Y-m-d', strtotime('-30 days')) }}" readonly>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="form-group">
|
|
<label class="form-label">Tanggal Akhir</label>
|
|
<input type="text" class="form-control datepicker" id="filter-end-date" placeholder="Tanggal akhir" value="{{ date('Y-m-d') }}" readonly>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label class="form-label">Filter Dealer</label>
|
|
<select class="form-control select2" id="filter-dealer">
|
|
<option value="">Semua Dealer</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-12 filter-buttons">
|
|
<button type="button" class="btn btn-primary btn-sm" id="btn-filter">
|
|
Cari
|
|
</button>
|
|
<button type="button" class="btn btn-secondary btn-sm" id="btn-reset">
|
|
Reset
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="row mb-3">
|
|
<div class="col-md-12">
|
|
<small class="form-text text-muted" id="default-dealer-info">Filter diset ke "Semua Dealer" untuk melihat data lengkap. Pilih dealer untuk memfilter data.</small>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Table Section --}}
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-bordered" id="report-technician-table">
|
|
<thead id="table-header">
|
|
<tr>
|
|
<th>No</th>
|
|
<th>Nama Pekerjaan</th>
|
|
<th>Kode Pekerjaan</th>
|
|
<th>Kategori</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
<tfoot id="table-footer">
|
|
<tr>
|
|
<th colspan="4" class="text-center">Total</th>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endsection
|
|
|
|
@section('javascripts')
|
|
<script>
|
|
$(document).ready(function() {
|
|
// Check if jQuery and DataTable are available
|
|
if (typeof $ === 'undefined') {
|
|
console.error('jQuery is not loaded');
|
|
return;
|
|
}
|
|
|
|
if (typeof $.fn.DataTable === 'undefined') {
|
|
console.error('DataTable is not loaded');
|
|
// Show error message to user
|
|
$('body').append('<div class="alert alert-danger m-3">DataTable library tidak dimuat. Silakan refresh halaman.</div>');
|
|
return;
|
|
}
|
|
|
|
let dataTable;
|
|
let mechanics = [];
|
|
let isInitialized = false; // Flag to prevent double initialization
|
|
|
|
// Loading overlay functions - Modified to only cover content section
|
|
var showLoadingOverlay = function (message) {
|
|
const $content = $('#kt_content');
|
|
|
|
if ($("#loading-overlay").length === 0) {
|
|
$content.append(`
|
|
<div id="loading-overlay" style="
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
background: rgba(0, 0, 0, 0.7);
|
|
z-index: 9999;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
flex-direction: column;
|
|
">
|
|
<div class="spinner-border text-light" role="status" style="width: 3rem; height: 3rem;">
|
|
<span class="sr-only">Loading...</span>
|
|
</div>
|
|
<div class="text-light mt-3" style="font-size: 1.1rem; font-weight: 500;">${message}</div>
|
|
</div>
|
|
`);
|
|
} else {
|
|
$("#loading-overlay").show();
|
|
$("#loading-overlay .text-light:last").text(message);
|
|
}
|
|
};
|
|
|
|
var hideLoadingOverlay = function () {
|
|
$("#loading-overlay").hide();
|
|
};
|
|
|
|
// Initialize datepickers
|
|
$('.datepicker').datepicker({
|
|
format: 'yyyy-mm-dd',
|
|
autoclose: true,
|
|
todayHighlight: true
|
|
});
|
|
|
|
// Initialize Select2 for dealer dropdown
|
|
function initializeSelect2() {
|
|
console.log("Initializing Select2...");
|
|
|
|
if (typeof $.fn.select2 !== 'undefined') {
|
|
$('#filter-dealer').select2({
|
|
placeholder: 'Semua Dealer',
|
|
allowClear: true,
|
|
width: '100%'
|
|
});
|
|
} else {
|
|
console.warn('Select2 is not loaded, using default dropdown');
|
|
}
|
|
}
|
|
|
|
// Store original button text
|
|
$('#btn-filter').data('original-text', $('#btn-filter').html());
|
|
|
|
// Load dealers first
|
|
loadDealers();
|
|
|
|
function loadDealers() {
|
|
$.ajax({
|
|
url: '{{ route("reports.technician.dealers") }}',
|
|
type: 'GET',
|
|
success: function(response) {
|
|
console.log('Dealers response:', response);
|
|
if (response.status === 'success') {
|
|
let options = '<option value="">Semua Dealer</option>';
|
|
response.data.forEach(function(dealer) {
|
|
options += `<option value="${dealer.id}">${dealer.name} (${dealer.dealer_code})</option>`;
|
|
});
|
|
$('#filter-dealer').html(options);
|
|
|
|
console.log('Dealers loaded:', response.data.length);
|
|
|
|
// Initialize Select2 after populating options
|
|
initializeSelect2();
|
|
|
|
// Set default dealer if provided
|
|
if (response.default_dealer && response.data.length > 0) {
|
|
// Don't set default dealer - always show "Semua Dealer" on initial load
|
|
$('#default-dealer-info').text('Filter diset ke "Semua Dealer" untuk melihat data lengkap. Pilih dealer untuk memfilter data.');
|
|
|
|
// Load initial data without dealer filter (show all dealers)
|
|
if (!isInitialized) {
|
|
console.log('Default dealer available but loading with "Semua Dealer" filter');
|
|
loadInitialMechanics();
|
|
}
|
|
} else if (response.default_dealer === null && response.data.length > 0) {
|
|
// Admin without default dealer - show all dealers without selection
|
|
$('#default-dealer-info').text('Filter diset ke "Semua Dealer" untuk melihat data lengkap. Pilih dealer untuk memfilter data.');
|
|
|
|
// Load initial data without dealer filter
|
|
if (!isInitialized) {
|
|
console.log('Admin without default dealer, loading initial data');
|
|
loadInitialMechanics();
|
|
}
|
|
} else if (response.data.length > 0) {
|
|
$('#default-dealer-info').text('Filter diset ke "Semua Dealer" untuk melihat data lengkap. Pilih dealer untuk memfilter data.');
|
|
|
|
// Load initial data without dealer filter
|
|
if (!isInitialized) {
|
|
console.log('No default dealer, loading initial data');
|
|
loadInitialMechanics();
|
|
}
|
|
} else {
|
|
// No dealers available
|
|
$('#default-dealer-info').text('Tidak ada dealer yang tersedia untuk Anda.');
|
|
hideLoadingOverlay();
|
|
}
|
|
|
|
// Check if user has access to any dealers
|
|
if (response.data.length === 0) {
|
|
toastr.warning('Anda tidak memiliki akses ke dealer manapun. Silakan hubungi administrator.');
|
|
$('#default-dealer-info').text('Anda tidak memiliki akses ke dealer manapun.');
|
|
hideLoadingOverlay();
|
|
return;
|
|
}
|
|
} else {
|
|
toastr.error('Gagal memuat data dealer');
|
|
hideLoadingOverlay();
|
|
}
|
|
},
|
|
error: function() {
|
|
toastr.error('Gagal memuat data dealer');
|
|
hideLoadingOverlay();
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadDataWithDealer(dealerId) {
|
|
// Show loading overlay
|
|
showLoadingOverlay("Memuat data laporan teknisi...");
|
|
|
|
const startDate = $('#filter-start-date').val();
|
|
const endDate = $('#filter-end-date').val();
|
|
|
|
console.log('Loading data with dealer:', dealerId, 'date range:', { startDate, endDate });
|
|
|
|
// Get mechanics first, then initialize DataTable
|
|
$.ajax({
|
|
url: '{{ route("reports.technician.data") }}',
|
|
type: 'GET',
|
|
data: { dealer_id: dealerId, start_date: startDate, end_date: endDate },
|
|
success: function(response) {
|
|
console.log('Data response:', response);
|
|
|
|
if (response.status === 'success' && Array.isArray(response.mechanics)) {
|
|
mechanics = response.mechanics;
|
|
console.log('Data loaded with dealer:', dealerId, 'mechanics:', mechanics.length);
|
|
|
|
// Check if user has access to any data
|
|
if (response.data.length === 0 && response.mechanics.length === 0) {
|
|
console.log('No data available for dealer:', dealerId);
|
|
toastr.info('Tidak ada data yang tersedia untuk dealer yang dipilih. Silakan pilih dealer lain.');
|
|
$('#default-dealer-info').text('Tidak ada data yang tersedia untuk dealer yang dipilih.');
|
|
hideLoadingOverlay();
|
|
return;
|
|
}
|
|
|
|
// Now initialize DataTable with mechanics ready
|
|
updateTableStructureWithMechanics();
|
|
isInitialized = true;
|
|
} else {
|
|
console.warn('Invalid mechanics data, using fallback');
|
|
mechanics = [];
|
|
updateTableStructureWithMechanics();
|
|
isInitialized = true;
|
|
}
|
|
},
|
|
error: function(xhr, status, error) {
|
|
console.error('Error loading data with dealer:', error);
|
|
console.error('Response:', xhr.responseText);
|
|
|
|
toastr.error('Gagal memuat data untuk dealer yang dipilih. Silakan coba lagi.');
|
|
hideLoadingOverlay();
|
|
|
|
// Don't try fallback to avoid multiple notifications
|
|
mechanics = [];
|
|
updateTableStructureWithMechanics();
|
|
isInitialized = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadInitialMechanics() {
|
|
// Get initial filter values (empty dealer, default dates)
|
|
let dealerId = '';
|
|
const startDate = $('#filter-start-date').val();
|
|
const endDate = $('#filter-end-date').val();
|
|
|
|
console.log('Loading initial mechanics without dealer filter, date range:', { startDate, endDate });
|
|
|
|
// Show loading overlay
|
|
showLoadingOverlay("Memuat data laporan teknisi...");
|
|
|
|
// Get mechanics first, then initialize DataTable
|
|
$.ajax({
|
|
url: '{{ route("reports.technician.data") }}',
|
|
type: 'GET',
|
|
data: { dealer_id: dealerId, start_date: startDate, end_date: endDate },
|
|
success: function(response) {
|
|
console.log('Initial data response:', response);
|
|
|
|
if (response.status === 'success' && Array.isArray(response.mechanics)) {
|
|
mechanics = response.mechanics;
|
|
console.log('Initial mechanics loaded:', mechanics.length);
|
|
|
|
// Check if user has access to any data
|
|
if (response.data.length === 0 && response.mechanics.length === 0) {
|
|
console.log('No data available without dealer filter');
|
|
// Don't show notification for initial load without dealer filter
|
|
$('#default-dealer-info').text('Tidak ada data yang tersedia. Silakan pilih dealer untuk memfilter data.');
|
|
hideLoadingOverlay();
|
|
return;
|
|
}
|
|
|
|
// Now initialize DataTable with mechanics ready
|
|
updateTableStructureWithMechanics();
|
|
isInitialized = true;
|
|
} else {
|
|
console.warn('Invalid mechanics data, using fallback');
|
|
mechanics = [];
|
|
updateTableStructureWithMechanics();
|
|
isInitialized = true;
|
|
}
|
|
},
|
|
error: function(xhr, status, error) {
|
|
console.error('Error loading initial mechanics:', error);
|
|
console.error('Response:', xhr.responseText);
|
|
|
|
toastr.error('Gagal memuat data laporan teknisi. Silakan coba lagi.');
|
|
hideLoadingOverlay();
|
|
|
|
console.warn('Error loading initial mechanics, using fallback');
|
|
mechanics = [];
|
|
updateTableStructureWithMechanics();
|
|
isInitialized = true;
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadTechnicianData() {
|
|
console.log('loadTechnicianData called');
|
|
|
|
// Get dealer value from Select2 or regular select
|
|
let dealerId = '';
|
|
if (typeof $.fn.select2 !== 'undefined' && $('#filter-dealer').data('select2')) {
|
|
dealerId = $('#filter-dealer').select2('val') || '';
|
|
console.log('Using Select2 value:', dealerId);
|
|
} else {
|
|
dealerId = $('#filter-dealer').val() || '';
|
|
console.log('Using regular select value:', dealerId);
|
|
}
|
|
|
|
const startDate = $('#filter-start-date').val();
|
|
const endDate = $('#filter-end-date').val();
|
|
|
|
console.log('Date range:', { startDate, endDate });
|
|
|
|
const requestData = {
|
|
dealer_id: dealerId || '',
|
|
start_date: startDate,
|
|
end_date: endDate
|
|
};
|
|
|
|
console.log('Requesting data with params:', requestData);
|
|
|
|
$.ajax({
|
|
url: '{{ route("reports.technician.data") }}',
|
|
type: 'GET',
|
|
data: requestData,
|
|
success: function(response) {
|
|
|
|
if (response.status === 'success') {
|
|
// Validate response data
|
|
if (response.mechanics && Array.isArray(response.mechanics)) {
|
|
mechanics = response.mechanics;
|
|
} else {
|
|
console.warn('Invalid mechanics data:', response.mechanics);
|
|
mechanics = [];
|
|
}
|
|
|
|
// Check if user has access to any data
|
|
if (response.data.length === 0 && response.mechanics.length === 0) {
|
|
toastr.info('Tidak ada data yang tersedia untuk filter yang dipilih atau Anda tidak memiliki akses ke data tersebut.');
|
|
$('#default-dealer-info').text('Tidak ada data yang tersedia untuk filter yang dipilih.');
|
|
}
|
|
|
|
} else {
|
|
console.error('Server returned error status:', response);
|
|
toastr.error('Gagal memuat data laporan teknisi');
|
|
}
|
|
},
|
|
error: function(xhr, status, error) {
|
|
console.error('AJAX Error occurred:');
|
|
console.error('Status:', status);
|
|
console.error('Error:', error);
|
|
console.error('Response Text:', xhr.responseText);
|
|
console.error('Status Code:', xhr.status);
|
|
console.error('Status Text:', xhr.statusText);
|
|
|
|
let errorMessage = 'Gagal memuat data laporan teknisi';
|
|
if (xhr.status === 500) {
|
|
errorMessage = 'Server error: ' + (xhr.responseText || 'Unknown error');
|
|
} else if (xhr.status === 404) {
|
|
errorMessage = 'Endpoint tidak ditemukan';
|
|
} else if (xhr.status === 403) {
|
|
errorMessage = 'Akses ditolak - Anda tidak memiliki izin untuk mengakses data ini';
|
|
}
|
|
|
|
toastr.error(errorMessage);
|
|
},
|
|
complete: function() {
|
|
|
|
// Always reset button state and hide overlay
|
|
hideLoadingOverlay();
|
|
|
|
// Reset button state using stored original text
|
|
var $btnFilter = $('#btn-filter');
|
|
var storedText = $btnFilter.data('original-text') || 'Cari';
|
|
$btnFilter.html(storedText).prop('disabled', false).removeClass('loading');
|
|
}
|
|
});
|
|
}
|
|
|
|
function initializeDataTable() {
|
|
try {
|
|
// Destroy existing DataTable if exists and clean up
|
|
if (dataTable && typeof dataTable.destroy === 'function') {
|
|
dataTable.destroy();
|
|
dataTable = null;
|
|
}
|
|
|
|
// Clean up existing search inputs
|
|
$('#datatable-search').closest('.row').remove();
|
|
$('#simple-search').closest('.row').remove();
|
|
|
|
// Completely recreate table structure to avoid DOM conflicts
|
|
const $table = $('#report-technician-table');
|
|
$table.empty();
|
|
|
|
// Recreate basic table structure
|
|
$table.html(`
|
|
<thead id="table-header">
|
|
<tr>
|
|
<th>No</th>
|
|
<th>Nama Pekerjaan</th>
|
|
<th>Kode Pekerjaan</th>
|
|
<th>Kategori</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
<tfoot id="table-footer">
|
|
<tr>
|
|
<th colspan="4" class="text-center">Total</th>
|
|
</tr>
|
|
</tfoot>
|
|
`);
|
|
|
|
// Check if DataTable is available
|
|
if (typeof $.fn.DataTable === 'undefined') {
|
|
console.error('DataTable is not loaded');
|
|
return;
|
|
}
|
|
|
|
// Check if table element exists
|
|
if ($table.length === 0) {
|
|
console.error('Table element not found');
|
|
return;
|
|
}
|
|
|
|
// Get filter values
|
|
let dealerId = '';
|
|
if (typeof $.fn.select2 !== 'undefined' && $('#filter-dealer').data('select2')) {
|
|
dealerId = $('#filter-dealer').select2('val') || '';
|
|
} else {
|
|
dealerId = $('#filter-dealer').val() || '';
|
|
}
|
|
|
|
const startDate = $('#filter-start-date').val();
|
|
const endDate = $('#filter-end-date').val();
|
|
|
|
// Try to initialize DataTable with Yajra DataTable
|
|
try {
|
|
// If mechanics are already available, use the complete structure
|
|
if (Array.isArray(mechanics) && mechanics.length > 0) {
|
|
updateTableStructureWithMechanics();
|
|
return;
|
|
}
|
|
|
|
dataTable = $table.DataTable({
|
|
processing: true,
|
|
serverSide: false, // We'll handle server-side manually
|
|
ajax: {
|
|
url: '{{ route("reports.technician.datatable") }}',
|
|
type: 'GET',
|
|
data: function(d) {
|
|
d.dealer_id = dealerId;
|
|
d.start_date = startDate;
|
|
d.end_date = endDate;
|
|
return d;
|
|
},
|
|
dataSrc: function(json) {
|
|
console.log('DataTable response:', json);
|
|
|
|
// Check for error response
|
|
if (json.error) {
|
|
console.error('DataTable error:', json.error);
|
|
toastr.error('Error loading data: ' + json.error);
|
|
return [];
|
|
}
|
|
|
|
// Update mechanics from response
|
|
if (json.mechanics && Array.isArray(json.mechanics)) {
|
|
mechanics = json.mechanics;
|
|
console.log('Mechanics loaded:', mechanics.length);
|
|
|
|
// Update table structure with mechanic columns
|
|
setTimeout(function() {
|
|
updateTableStructureWithMechanics();
|
|
}, 100);
|
|
}
|
|
|
|
return json.data || [];
|
|
},
|
|
error: function(xhr, error, thrown) {
|
|
console.error('DataTable AJAX error:', {
|
|
xhr: xhr,
|
|
error: error,
|
|
thrown: thrown
|
|
});
|
|
|
|
let errorMessage = 'Gagal memuat data';
|
|
if (xhr.responseJSON && xhr.responseJSON.error) {
|
|
errorMessage = xhr.responseJSON.error;
|
|
} else if (xhr.status === 500) {
|
|
errorMessage = 'Server error: ' + (xhr.responseText || 'Unknown error');
|
|
} else if (xhr.status === 404) {
|
|
errorMessage = 'Endpoint tidak ditemukan';
|
|
} else if (xhr.status === 403) {
|
|
errorMessage = 'Akses ditolak';
|
|
}
|
|
|
|
toastr.error(errorMessage);
|
|
hideLoadingOverlay();
|
|
}
|
|
},
|
|
columns: [
|
|
{ data: 'DT_RowIndex', name: 'DT_RowIndex', orderable: false, searchable: false, width: '50px', className: 'text-center' },
|
|
{ data: 'work_name', name: 'work_name', orderable: true, searchable: true },
|
|
{ data: 'work_code', name: 'work_code', orderable: true, searchable: true, width: '120px' },
|
|
{ data: 'category_name', name: 'category_name', orderable: true, searchable: true, width: '150px' }
|
|
],
|
|
pageLength: -1,
|
|
lengthMenu: [],
|
|
order: [[1, 'asc']],
|
|
responsive: false,
|
|
dom: '<"row"<"col-sm-12"f>>' +
|
|
'<"row"<"col-sm-12"tr>>' +
|
|
'<"row"<"col-sm-12"i>>',
|
|
initComplete: function() {
|
|
try {
|
|
var api = this.api();
|
|
console.log('DataTable initialization complete');
|
|
console.log('Data rows:', api.data().length);
|
|
console.log('Mechanics count:', mechanics.length);
|
|
|
|
if (api.data().length === 0) {
|
|
toastr.info('Tidak ada data yang ditemukan untuk filter yang dipilih. Silakan coba filter yang berbeda.');
|
|
}
|
|
|
|
// Enable filter button di sini!
|
|
var $btnFilter = $('#btn-filter');
|
|
var storedText = $btnFilter.data('original-text') || 'Cari';
|
|
$btnFilter.html(storedText).prop('disabled', false).removeClass('loading');
|
|
hideLoadingOverlay();
|
|
} catch (error) {
|
|
console.error('Error in initComplete:', error);
|
|
hideLoadingOverlay();
|
|
}
|
|
},
|
|
drawCallback: function() {
|
|
try {
|
|
// Update totals in footer
|
|
updateTableTotals();
|
|
} catch (error) {
|
|
console.error('Error in drawCallback:', error);
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log('DataTable initialized successfully');
|
|
|
|
} catch (dataTableError) {
|
|
console.error('DataTable initialization failed:', dataTableError);
|
|
toastr.error('Gagal memuat tabel data');
|
|
hideLoadingOverlay();
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Error in initializeDataTable:', error);
|
|
toastr.error('Gagal memuat tabel data');
|
|
hideLoadingOverlay();
|
|
}
|
|
}
|
|
|
|
function createFallbackTable(data) {
|
|
try {
|
|
// Clean up existing search inputs
|
|
$('#datatable-search').closest('.row').remove();
|
|
$('#simple-search').closest('.row').remove();
|
|
|
|
// Ensure DataTable is destroyed
|
|
if (dataTable && typeof dataTable.destroy === 'function') {
|
|
dataTable.destroy();
|
|
dataTable = null;
|
|
}
|
|
|
|
const $table = $('#report-technician-table');
|
|
$table.empty();
|
|
|
|
if (data.length > 0) {
|
|
let tableHtml = '<thead><tr>';
|
|
tableHtml += '<th>No</th><th>Nama Pekerjaan</th><th>Kode Pekerjaan</th><th>Kategori</th>';
|
|
if (Array.isArray(mechanics) && mechanics.length > 0) {
|
|
mechanics.forEach(function(mechanic) {
|
|
if (mechanic && mechanic.name) {
|
|
tableHtml += `<th>${mechanic.name}</th>`;
|
|
}
|
|
});
|
|
}
|
|
tableHtml += '</tr></thead><tbody>';
|
|
|
|
data.forEach(function(item, index) {
|
|
tableHtml += `<tr>
|
|
<td>${index + 1}</td>
|
|
<td>${item.work_name || ''}</td>
|
|
<td>${item.work_code || ''}</td>
|
|
<td>${item.category_name || ''}</td>
|
|
`;
|
|
if (Array.isArray(mechanics) && mechanics.length > 0) {
|
|
mechanics.forEach(function(mechanic) {
|
|
if (mechanic && mechanic.id) {
|
|
tableHtml += `<td>${item[`mechanic_${mechanic.id}_total`] || 0}</td>`;
|
|
}
|
|
});
|
|
}
|
|
tableHtml += '</tr>';
|
|
});
|
|
tableHtml += '</tbody>';
|
|
$table.html(tableHtml);
|
|
|
|
// Add simple search functionality to fallback table
|
|
addSimpleSearch($table);
|
|
|
|
} else {
|
|
$table.html('<tbody><tr><td colspan="100%" class="text-center">Tidak ada data yang tersedia untuk filter yang dipilih</td></tr></tbody>');
|
|
}
|
|
console.log('Fallback table created successfully');
|
|
hideLoadingOverlay();
|
|
} catch (fallbackError) {
|
|
console.error('Error creating fallback table:', fallbackError);
|
|
toastr.error('Gagal memuat tabel data. Silakan refresh halaman.');
|
|
hideLoadingOverlay();
|
|
}
|
|
}
|
|
|
|
function addMechanicColumns(api) {
|
|
if (!Array.isArray(mechanics) || mechanics.length === 0) {
|
|
console.log('No mechanics to add columns for');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
console.log('Adding mechanic columns for', mechanics.length, 'mechanics');
|
|
|
|
// Instead of adding columns dynamically, we'll update the table structure
|
|
// and reinitialize the DataTable with the complete column configuration
|
|
updateTableStructureWithMechanics();
|
|
|
|
} catch (error) {
|
|
console.error('Error adding mechanic columns:', error);
|
|
}
|
|
}
|
|
|
|
function updateTableStructureWithMechanics() {
|
|
try {
|
|
// Destroy existing DataTable
|
|
if (dataTable && typeof dataTable.destroy === 'function') {
|
|
dataTable.destroy();
|
|
dataTable = null;
|
|
}
|
|
|
|
const $table = $('#report-technician-table');
|
|
$table.empty();
|
|
|
|
// Build header rows
|
|
let headerRow1 = `
|
|
<tr>
|
|
<th>No</th>
|
|
<th>Nama Pekerjaan</th>
|
|
<th>Kode Pekerjaan</th>
|
|
<th>Kategori</th>
|
|
`;
|
|
if (Array.isArray(mechanics) && mechanics.length > 0) {
|
|
mechanics.forEach(function(mechanic) {
|
|
headerRow1 += `<th class="mechanic-column" style="text-align: center; background-color: #f8f9fa;"><strong>${mechanic.name}</strong></th>`;
|
|
});
|
|
}
|
|
headerRow1 += `</tr>`;
|
|
|
|
// Set thead
|
|
$table.append(`<thead id="table-header">${headerRow1}</thead>`);
|
|
$table.append('<tbody></tbody>');
|
|
|
|
// Build footer
|
|
let footerHtml = '<tr><th colspan="4" class="text-center"><strong>Total</strong></th>';
|
|
if (Array.isArray(mechanics) && mechanics.length > 0) {
|
|
mechanics.forEach(function() {
|
|
footerHtml += `<th class="text-center mechanic-column"><strong>0</strong></th>`;
|
|
});
|
|
}
|
|
footerHtml += '</tr>';
|
|
$table.append(`<tfoot id="table-footer">${footerHtml}</tfoot>`);
|
|
|
|
// Reinitialize DataTable
|
|
initializeDataTableWithMechanics();
|
|
} catch (error) {
|
|
console.error('Error updating table structure:', error);
|
|
hideLoadingOverlay();
|
|
}
|
|
}
|
|
|
|
function initializeDataTableWithMechanics() {
|
|
try {
|
|
// Get filter values
|
|
let dealerId = '';
|
|
if (typeof $.fn.select2 !== 'undefined' && $('#filter-dealer').data('select2')) {
|
|
dealerId = $('#filter-dealer').select2('val') || '';
|
|
} else {
|
|
dealerId = $('#filter-dealer').val() || '';
|
|
}
|
|
|
|
const startDate = $('#filter-start-date').val();
|
|
const endDate = $('#filter-end-date').val();
|
|
|
|
// Build complete column configuration
|
|
const columns = [
|
|
{ data: 'DT_RowIndex', name: 'DT_RowIndex', orderable: false, searchable: false, width: '50px', className: 'text-center' },
|
|
{ data: 'work_name', name: 'work_name', orderable: true, searchable: true },
|
|
{ data: 'work_code', name: 'work_code', orderable: true, searchable: true, width: '120px' },
|
|
{ data: 'category_name', name: 'category_name', orderable: true, searchable: true, width: '150px' }
|
|
];
|
|
|
|
// Add mechanic columns
|
|
if (Array.isArray(mechanics) && mechanics.length > 0) {
|
|
mechanics.forEach(function(mechanic) {
|
|
if (mechanic && mechanic.id) {
|
|
columns.push({
|
|
data: `mechanic_${mechanic.id}_total`,
|
|
name: `mechanic_${mechanic.id}_total`,
|
|
title: `${mechanic.name || 'Unknown'}`,
|
|
orderable: false,
|
|
searchable: false,
|
|
className: 'text-center mechanic-column',
|
|
defaultContent: '0'
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// Initialize DataTable with complete configuration
|
|
dataTable = $('#report-technician-table').DataTable({
|
|
processing: true,
|
|
serverSide: false,
|
|
ajax: {
|
|
url: '{{ route("reports.technician.datatable") }}',
|
|
type: 'GET',
|
|
data: function(d) {
|
|
d.dealer_id = dealerId;
|
|
d.start_date = startDate;
|
|
d.end_date = endDate;
|
|
return d;
|
|
},
|
|
dataSrc: function(json) {
|
|
console.log('DataTable response:', json);
|
|
|
|
// Check for error response
|
|
if (json.error) {
|
|
console.error('DataTable error:', json.error);
|
|
toastr.error('Error loading data: ' + json.error);
|
|
return [];
|
|
}
|
|
|
|
// Don't update mechanics from datatable response - use existing mechanics
|
|
// if (json.mechanics && Array.isArray(json.mechanics)) {
|
|
// mechanics = json.mechanics;
|
|
// }
|
|
|
|
return json.data || [];
|
|
},
|
|
error: function(xhr, error, thrown) {
|
|
console.error('DataTable AJAX error:', {
|
|
xhr: xhr,
|
|
error: error,
|
|
thrown: thrown
|
|
});
|
|
|
|
let errorMessage = 'Gagal memuat data';
|
|
if (xhr.responseJSON && xhr.responseJSON.error) {
|
|
errorMessage = xhr.responseJSON.error;
|
|
} else if (xhr.status === 500) {
|
|
errorMessage = 'Server error: ' + (xhr.responseText || 'Unknown error');
|
|
} else if (xhr.status === 404) {
|
|
errorMessage = 'Endpoint tidak ditemukan';
|
|
} else if (xhr.status === 403) {
|
|
errorMessage = 'Akses ditolak';
|
|
}
|
|
|
|
toastr.error(errorMessage);
|
|
hideLoadingOverlay();
|
|
}
|
|
},
|
|
columns: columns,
|
|
pageLength: -1,
|
|
lengthMenu: [],
|
|
order: [[1, 'asc']],
|
|
responsive: false,
|
|
dom: '<"row"<"col-sm-12"f>>' +
|
|
'<"row"<"col-sm-12"tr>>' +
|
|
'<"row"<"col-sm-12"i>>',
|
|
initComplete: function() {
|
|
try {
|
|
var api = this.api();
|
|
console.log('DataTable with mechanics initialization complete');
|
|
console.log('Data rows:', api.data().length);
|
|
console.log('Mechanics count:', mechanics.length);
|
|
|
|
if (api.data().length === 0) {
|
|
toastr.info('Tidak ada data yang ditemukan untuk filter yang dipilih. Silakan coba filter yang berbeda.');
|
|
} else {
|
|
// Update totals after initialization
|
|
setTimeout(function() {
|
|
updateTableTotals();
|
|
}, 200);
|
|
}
|
|
|
|
// Enable filter button di sini!
|
|
var $btnFilter = $('#btn-filter');
|
|
var storedText = $btnFilter.data('original-text') || 'Cari';
|
|
$btnFilter.html(storedText).prop('disabled', false).removeClass('loading');
|
|
hideLoadingOverlay();
|
|
} catch (error) {
|
|
console.error('Error in initComplete:', error);
|
|
hideLoadingOverlay();
|
|
}
|
|
},
|
|
drawCallback: function() {
|
|
try {
|
|
console.log('DataTable drawCallback triggered');
|
|
// Add a small delay to ensure data is fully rendered
|
|
setTimeout(function() {
|
|
updateTableTotals();
|
|
}, 100);
|
|
} catch (error) {
|
|
console.error('Error in drawCallback:', error);
|
|
}
|
|
}
|
|
});
|
|
|
|
console.log('DataTable with mechanics initialized successfully');
|
|
|
|
} catch (error) {
|
|
console.error('Error initializing DataTable with mechanics:', error);
|
|
toastr.error('Gagal memuat tabel data');
|
|
hideLoadingOverlay();
|
|
}
|
|
}
|
|
|
|
function updateTableTotals() {
|
|
if (!dataTable) {
|
|
console.log('DataTable not initialized');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
// Get DataTable API - try multiple methods
|
|
let api = null;
|
|
|
|
// Method 1: Direct API access
|
|
if (dataTable && typeof dataTable.api === 'function') {
|
|
api = dataTable.api();
|
|
console.log('Using dataTable.api() method');
|
|
}
|
|
// Method 2: jQuery DataTable API
|
|
else if (dataTable && typeof dataTable.dataTable === 'function') {
|
|
api = dataTable.dataTable().api();
|
|
console.log('Using dataTable.dataTable().api() method');
|
|
}
|
|
// Method 3: Direct access to DataTable instance
|
|
else if (dataTable && dataTable.api) {
|
|
api = dataTable.api;
|
|
console.log('Using direct dataTable.api access');
|
|
}
|
|
// Method 4: Try to get from jQuery element
|
|
else if ($('#report-technician-table').dataTable) {
|
|
api = $('#report-technician-table').dataTable().api();
|
|
console.log('Using jQuery element dataTable().api() method');
|
|
}
|
|
|
|
if (!api) {
|
|
console.warn('Cannot access DataTable API - trying alternative method');
|
|
// Alternative: Get data directly from table rows
|
|
updateTableTotalsFromDOM();
|
|
return;
|
|
}
|
|
|
|
console.log('DataTable API accessed successfully');
|
|
const data = api.data().toArray();
|
|
console.log('Data rows found:', data.length);
|
|
|
|
if (data.length === 0) {
|
|
console.log('No data rows found');
|
|
return;
|
|
}
|
|
|
|
// Calculate totals for each mechanic
|
|
const totals = {};
|
|
|
|
if (Array.isArray(mechanics) && mechanics.length > 0) {
|
|
mechanics.forEach(function(mechanic) {
|
|
if (mechanic && mechanic.id) {
|
|
totals[`mechanic_${mechanic.id}_total`] = 0;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Sum up values from all rows
|
|
data.forEach(function(row, index) {
|
|
if (Array.isArray(mechanics) && mechanics.length > 0) {
|
|
mechanics.forEach(function(mechanic) {
|
|
if (mechanic && mechanic.id) {
|
|
const value = parseInt(row[`mechanic_${mechanic.id}_total`] || 0);
|
|
totals[`mechanic_${mechanic.id}_total`] += value;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
console.log('Calculated totals:', totals);
|
|
|
|
// Update footer with totals
|
|
const $footer = $('#table-footer tr');
|
|
if ($footer.length > 0) {
|
|
let footerHtml = '<th colspan="4" class="text-center"><strong>Total</strong></th>';
|
|
|
|
if (Array.isArray(mechanics) && mechanics.length > 0) {
|
|
mechanics.forEach(function(mechanic) {
|
|
if (mechanic && mechanic.id) {
|
|
const total = totals[`mechanic_${mechanic.id}_total`] || 0;
|
|
footerHtml += `<th class="text-center mechanic-column"><strong>${total}</strong></th>`;
|
|
}
|
|
});
|
|
}
|
|
|
|
$footer.html(footerHtml);
|
|
console.log('Footer updated successfully');
|
|
} else {
|
|
console.warn('Footer row not found');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error updating table totals:', error);
|
|
// Fallback to DOM method
|
|
updateTableTotalsFromDOM();
|
|
}
|
|
}
|
|
|
|
function updateTableTotalsFromDOM() {
|
|
try {
|
|
console.log('Using DOM method to calculate totals');
|
|
|
|
// Calculate totals for each mechanic
|
|
const totals = {};
|
|
|
|
if (Array.isArray(mechanics) && mechanics.length > 0) {
|
|
mechanics.forEach(function(mechanic) {
|
|
if (mechanic && mechanic.id) {
|
|
totals[`mechanic_${mechanic.id}_total`] = 0;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Get data from table rows directly
|
|
const $rows = $('#report-technician-table tbody tr');
|
|
console.log('Found table rows:', $rows.length);
|
|
|
|
$rows.each(function(index) {
|
|
const $row = $(this);
|
|
const $cells = $row.find('td');
|
|
|
|
if (Array.isArray(mechanics) && mechanics.length > 0) {
|
|
mechanics.forEach(function(mechanic, mechanicIndex) {
|
|
if (mechanic && mechanic.id) {
|
|
// Skip first 4 columns (No, Nama Pekerjaan, Kode Pekerjaan, Kategori)
|
|
const cellIndex = 4 + mechanicIndex;
|
|
const $cell = $cells.eq(cellIndex);
|
|
const value = parseInt($cell.text() || 0);
|
|
totals[`mechanic_${mechanic.id}_total`] += value;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
console.log('DOM calculated totals:', totals);
|
|
|
|
// Update footer with totals
|
|
const $footer = $('#table-footer tr');
|
|
if ($footer.length > 0) {
|
|
let footerHtml = '<th colspan="4" class="text-center"><strong>Total</strong></th>';
|
|
|
|
if (Array.isArray(mechanics) && mechanics.length > 0) {
|
|
mechanics.forEach(function(mechanic) {
|
|
if (mechanic && mechanic.id) {
|
|
const total = totals[`mechanic_${mechanic.id}_total`] || 0;
|
|
footerHtml += `<th class="text-center mechanic-column"><strong>${total}</strong></th>`;
|
|
}
|
|
});
|
|
}
|
|
|
|
$footer.html(footerHtml);
|
|
console.log('DOM Footer updated successfully');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error in DOM method:', error);
|
|
}
|
|
}
|
|
|
|
function addSimpleSearch($table) {
|
|
// Add search input above table
|
|
const searchHtml = `
|
|
<div class="row mb-3">
|
|
<div class="col-sm-12">
|
|
<div class="input-group">
|
|
<div class="input-group-prepend">
|
|
<span class="input-group-text">
|
|
<i class="fa fa-search"></i>
|
|
</span>
|
|
</div>
|
|
<input type="text" class="form-control" id="simple-search" placeholder="Cari data...">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
$table.before(searchHtml);
|
|
|
|
// Add search functionality
|
|
$('#simple-search').on('keyup', function() {
|
|
const searchTerm = $(this).val().toLowerCase();
|
|
$table.find('tbody tr').each(function() {
|
|
const rowText = $(this).text().toLowerCase();
|
|
if (rowText.includes(searchTerm)) {
|
|
$(this).show();
|
|
} else {
|
|
$(this).hide();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// Filter button click - Fixed button state management
|
|
$('#btn-filter').on('click', function() {
|
|
console.log('Filter button clicked');
|
|
|
|
// Prevent multiple clicks
|
|
var filterButton = $('#btn-filter');
|
|
if (filterButton.hasClass('loading') || filterButton.prop('disabled')) {
|
|
console.log('Button already loading or disabled, ignoring click');
|
|
return; // Already processing
|
|
}
|
|
|
|
// Get dealer value from Select2 or regular select
|
|
let selectedDealer = '';
|
|
if (typeof $.fn.select2 !== 'undefined' && $('#filter-dealer').data('select2')) {
|
|
selectedDealer = $('#filter-dealer').select2('val') || '';
|
|
} else {
|
|
selectedDealer = $('#filter-dealer').val();
|
|
}
|
|
|
|
console.log('Selected dealer:', selectedDealer);
|
|
|
|
// Show warning if user selects "Semua Dealer"
|
|
if (selectedDealer === '') {
|
|
if (!confirm('Memilih "Semua Dealer" akan memuat data yang lebih besar dan mungkin memakan waktu lebih lama. Lanjutkan?')) {
|
|
return; // User cancelled
|
|
}
|
|
}
|
|
|
|
console.log('Filter button clicked - starting data load');
|
|
|
|
// Store the original button text before changing it
|
|
var originalText = filterButton.html();
|
|
filterButton.data('original-text', originalText);
|
|
|
|
// Show loading state
|
|
filterButton.addClass('loading').prop('disabled', true);
|
|
filterButton.html('<i class="fa fa-spinner fa-spin"></i> Mencari...');
|
|
|
|
console.log('Button loading state set, original text:', originalText);
|
|
console.log('Button disabled state:', filterButton.prop('disabled'));
|
|
console.log('Button loading class:', filterButton.hasClass('loading'));
|
|
|
|
// Show loading overlay
|
|
showLoadingOverlay("Memuat data laporan teknisi...");
|
|
|
|
// Reload DataTable
|
|
if (dataTable && typeof dataTable.destroy === 'function') {
|
|
dataTable.destroy();
|
|
dataTable = null;
|
|
}
|
|
|
|
// Reset initialization flag to allow re-initialization
|
|
isInitialized = false;
|
|
|
|
// Get mechanics first, then initialize DataTable
|
|
let dealerId = '';
|
|
if (typeof $.fn.select2 !== 'undefined' && $('#filter-dealer').data('select2')) {
|
|
dealerId = $('#filter-dealer').select2('val') || '';
|
|
} else {
|
|
dealerId = $('#filter-dealer').val();
|
|
}
|
|
const startDate = $('#filter-start-date').val();
|
|
const endDate = $('#filter-end-date').val();
|
|
|
|
$.ajax({
|
|
url: '{{ route("reports.technician.data") }}',
|
|
type: 'GET',
|
|
data: { dealer_id: dealerId, start_date: startDate, end_date: endDate },
|
|
success: function(response) {
|
|
if (response.status === 'success' && Array.isArray(response.mechanics)) {
|
|
mechanics = response.mechanics;
|
|
console.log('Filter mechanics loaded:', mechanics.length);
|
|
updateTableStructureWithMechanics();
|
|
} else {
|
|
console.warn('Invalid mechanics data, using fallback');
|
|
mechanics = [];
|
|
updateTableStructureWithMechanics();
|
|
}
|
|
},
|
|
error: function() {
|
|
console.warn('Error loading mechanics, using fallback');
|
|
mechanics = [];
|
|
updateTableStructureWithMechanics();
|
|
}
|
|
});
|
|
});
|
|
|
|
// Export button click
|
|
$('#btn-export-excel').on('click', function() {
|
|
exportToExcel();
|
|
});
|
|
|
|
// Reset button click
|
|
$('#btn-reset').on('click', function() {
|
|
console.log('Reset button clicked');
|
|
|
|
// Reset dealer selection to "Semua Dealer"
|
|
if (typeof $.fn.select2 !== 'undefined' && $('#filter-dealer').data('select2')) {
|
|
$('#filter-dealer').val('').trigger('change');
|
|
} else {
|
|
$('#filter-dealer').val('');
|
|
}
|
|
|
|
// Reset date inputs
|
|
$('#filter-start-date').val('{{ date('Y-m-d', strtotime('-30 days')) }}');
|
|
$('#filter-end-date').val('{{ date('Y-m-d') }}');
|
|
|
|
console.log('Filters reset to "Semua Dealer", reloading DataTable...');
|
|
|
|
// Reset initialization flag
|
|
isInitialized = false;
|
|
// Show loading overlay
|
|
showLoadingOverlay("Memuat data laporan teknisi...");
|
|
if (dataTable && typeof dataTable.destroy === 'function') {
|
|
dataTable.destroy();
|
|
dataTable = null;
|
|
}
|
|
|
|
// Get mechanics first, then initialize DataTable
|
|
let dealerId = '';
|
|
const startDate = $('#filter-start-date').val();
|
|
const endDate = $('#filter-end-date').val();
|
|
|
|
$.ajax({
|
|
url: '{{ route("reports.technician.data") }}',
|
|
type: 'GET',
|
|
data: { dealer_id: dealerId, start_date: startDate, end_date: endDate },
|
|
success: function(response) {
|
|
if (response.status === 'success' && Array.isArray(response.mechanics)) {
|
|
mechanics = response.mechanics;
|
|
console.log('Reset mechanics loaded:', mechanics.length);
|
|
updateTableStructureWithMechanics();
|
|
} else {
|
|
console.warn('Invalid mechanics data, using fallback');
|
|
mechanics = [];
|
|
updateTableStructureWithMechanics();
|
|
}
|
|
},
|
|
error: function() {
|
|
console.warn('Error loading mechanics, using fallback');
|
|
mechanics = [];
|
|
updateTableStructureWithMechanics();
|
|
}
|
|
});
|
|
});
|
|
|
|
function exportToExcel() {
|
|
// Prevent multiple clicks
|
|
var exportButton = $('#btn-export-excel');
|
|
if (exportButton.hasClass('loading')) {
|
|
return; // Already processing
|
|
}
|
|
|
|
// Get dealer value from Select2 or regular select
|
|
let dealerId = '';
|
|
if (typeof $.fn.select2 !== 'undefined' && $('#filter-dealer').data('select2')) {
|
|
dealerId = $('#filter-dealer').select2('val') || '';
|
|
} else {
|
|
dealerId = $('#filter-dealer').val();
|
|
}
|
|
const startDate = $('#filter-start-date').val();
|
|
const endDate = $('#filter-end-date').val();
|
|
|
|
let url = '{{ route("reports.technician.export") }}';
|
|
let params = [];
|
|
|
|
if (dealerId) {
|
|
params.push('dealer_id=' + dealerId);
|
|
}
|
|
if (startDate) {
|
|
params.push('start_date=' + startDate);
|
|
}
|
|
if (endDate) {
|
|
params.push('end_date=' + endDate);
|
|
}
|
|
|
|
if (params.length > 0) {
|
|
url += '?' + params.join('&');
|
|
}
|
|
|
|
// Show loading state
|
|
exportButton.addClass('loading').prop('disabled', true);
|
|
var originalHtml = exportButton.html();
|
|
exportButton.html('<i class="fa fa-spinner fa-spin"></i> Exporting...');
|
|
|
|
// Open in new window
|
|
let newWindow = window.open(url, '_blank');
|
|
|
|
// Reset button and hide overlay after a delay
|
|
setTimeout(function() {
|
|
exportButton.removeClass('loading').prop('disabled', false).html(originalHtml);
|
|
hideLoadingOverlay();
|
|
}, 2000);
|
|
}
|
|
|
|
// Make exportToExcel available globally
|
|
window.exportToExcel = exportToExcel;
|
|
|
|
// Cleanup loading overlay on page unload
|
|
$(window).on('beforeunload', function() {
|
|
hideLoadingOverlay();
|
|
});
|
|
|
|
// Cleanup Select2 on page unload
|
|
$(window).on('beforeunload', function() {
|
|
if (typeof $.fn.select2 !== 'undefined' && $('#filter-dealer').data('select2')) {
|
|
$('#filter-dealer').select2('destroy');
|
|
}
|
|
});
|
|
|
|
// Fallback for toastr if not available
|
|
if (typeof toastr === 'undefined') {
|
|
window.toastr = {
|
|
success: function(msg) { console.log('SUCCESS:', msg); },
|
|
error: function(msg) { console.log('ERROR:', msg); },
|
|
warning: function(msg) { console.log('WARNING:', msg); },
|
|
info: function(msg) { console.log('INFO:', msg); }
|
|
};
|
|
}
|
|
});
|
|
</script>
|
|
@endsection |