2024-12-16 17:01:55 +07:00
@ extends ( 'layouts.app' )
@ section ( 'title' )
2025-06-19 19:39:20 +07:00
Dashboard
2024-12-16 17:01:55 +07:00
@ endsection
@ section ( 'admin-content' )
2024-12-20 11:07:07 +07:00
< script src = " https://cdn.jsdelivr.net/npm/autonumeric@4.6.0/dist/autoNumeric.min.js " ></ script >
2026-06-01 15:01:03 +07:00
< script src = " https://cdn.jsdelivr.net/npm/sweetalert2@11 " ></ script >
2024-12-16 17:01:55 +07:00
< section class = " content-header " >
< div class = " container-fluid " >
< div class = " row mb-2 " >
< div class = " col-sm-6 " >
< h1 > Edit Expense Vehicle Running Cost </ h1 >
</ div >
< div class = " col-sm-6 " >
< ol class = " breadcrumb float-sm-right " >
< li class = " breadcrumb-item " >< a href = " { { route('dashboard.index') }} " > Dashboard </ a ></ li >
</ ol >
</ div >
</ div >
</ div >
</ section >
2025-01-17 16:39:37 +07:00
< style >
#loading-spinner-overlay {
position : fixed ;
top : 0 ;
left : 0 ;
width : 100 % ;
height : 100 % ;
2026-06-01 15:01:03 +07:00
background - color : rgba ( 0 , 0 , 0 , 0.5 );
z - index : 9999 ;
2025-01-17 16:39:37 +07:00
display : flex ;
justify - content : center ;
align - items : center ;
}
. spinner - wrapper {
text - align : center ;
color : white ;
}
</ style >
2026-06-01 15:01:03 +07:00
@ php
$attachmentCategoryLabels = $attachmentCategoryLabels ? ? ( $vehicleAttachmentCategories ? ? []);
$attachmentCategories = $attachmentCategories ? ? ( $vehicleAttachmentCategoryKeys ? ? array_keys ( $attachmentCategoryLabels ));
@ endphp
2024-12-16 17:01:55 +07:00
< section class = " content " >
< div class = " container-fluid " >
< div class = " card card-primary card-outline " >
< div class = " card-body " >
2025-06-19 19:39:20 +07:00
< form id = " expense-form " method = " POST " action = " { { route('forms.vehicle.update', $form->id ) }} " enctype = " multipart/form-data " >
2024-12-16 17:01:55 +07:00
@ csrf
@ method ( 'PUT' )
@ include ( 'backend.layouts.partials.messages' )
< div class = " row " >
< div class = " col-lg-6 " >
< div class = " mb-3 " >
2025-01-06 16:00:51 +07:00
< label class = " form-label " > Tanggal < span class = " font-italic font-weight-normal " > ( required ) </ span ></ label >
2026-06-01 15:01:03 +07:00
{{ -- Konversi Cerdas Format Input Tipe 'date' -- }}
< input type = " date " class = " form-control " name = " tanggal " required
value = " { { old('tanggal', $form->tanggal ? \ Carbon \ Carbon::parse( $form->tanggal )->format('Y-m-d') : '') }} " >
2024-12-16 17:01:55 +07:00
</ div >
< div class = " mb-3 " >
2025-06-19 19:39:20 +07:00
< label class = " form-label " > Tipe Pengeluaran < span class = " font-italic font-weight-normal " > ( required ) </ span ></ label >
< select class = " form-control " name = " type " id = " expense_type " required >
< option value = " gasoline " {{ old ( 'type' , $form -> type ) == 'gasoline' ? 'selected' : '' }} > Gasoline </ option >
< option value = " parking_toll " {{ old ( 'type' , $form -> type ) == 'parking_toll' ? 'selected' : '' }} > Parking / Toll </ option >
</ select >
</ div >
< div class = " mb-3 gasoline-fields " >
2025-01-06 16:00:51 +07:00
< label class = " form-label " > Liter Bensin < span class = " font-italic font-weight-normal " > ( required ) </ span ></ label >
2026-06-01 15:01:03 +07:00
< input type = " number " step = " 0.01 " class = " form-control " name = " liter " id = " liter " value = " { { old('liter', $form->liter ) }} " >
2024-12-16 17:01:55 +07:00
</ div >
< div class = " mb-3 " >
2025-06-19 19:39:20 +07:00
< label class = " form-label " > Total Harga < span class = " font-italic font-weight-normal " > ( required ) </ span ></ label >
2026-06-01 15:01:03 +07:00
< input type = " text " class = " form-control " name = " total " id = " total " required value = " { { old('total', $form->total ) }} " >
2024-12-16 17:01:55 +07:00
</ div >
</ div >
< div class = " col-lg-6 " >
2025-06-19 19:39:20 +07:00
< div class = " mb-3 gasoline-fields " >
2025-05-25 15:14:23 +07:00
< label class = " form-label " > Km ( Odometer ) < span class = " font-italic font-weight-normal " > ( required ) </ span ></ label >
2025-06-19 19:39:20 +07:00
< input type = " number " class = " form-control " name = " jarak " id = " jarak " value = " { { old('jarak', $form->jarak ) }} " >
2025-01-06 16:00:51 +07:00
</ div >
2025-06-19 19:39:20 +07:00
< div class = " mb-3 gasoline-fields " >
2025-01-06 16:00:51 +07:00
< label class = " form-label " > Tipe Bensin < span class = " font-italic font-weight-normal " > ( required ) </ span ></ label >
2025-06-19 19:39:20 +07:00
< select class = " form-control " name = " tipe_bensin " id = " tipe_bensin " >
2026-06-01 15:01:03 +07:00
< option value = " " disabled selected > Pilih Tipe </ option >
2025-06-19 19:39:20 +07:00
< option value = " pertamax " {{ old ( 'tipe_bensin' , $form -> tipe_bensin ) == 'pertamax' ? 'selected' : '' }} > Pertamax </ option >
< option value = " pertalite " {{ old ( 'tipe_bensin' , $form -> tipe_bensin ) == 'pertalite' ? 'selected' : '' }} > Pertalite </ option >
2024-12-16 17:01:55 +07:00
</ select >
</ div >
2025-06-19 19:39:20 +07:00
< div class = " mb-3 gasoline-fields " >
2025-01-06 16:00:51 +07:00
< label class = " form-label " > Nomor Polisi < span class = " font-italic font-weight-normal " > ( required ) </ span ></ label >
2025-06-19 19:39:20 +07:00
< input type = " text " class = " form-control " name = " nopol " id = " nopol " value = " { { old('nopol', $form->nopol ) }} " >
2024-12-16 17:01:55 +07:00
</ div >
< div class = " mb-3 " >
2025-01-06 16:00:51 +07:00
< label class = " form-label " > Keterangan < span class = " font-italic font-weight-normal " > ( required ) </ span ></ label >
2025-06-19 19:39:20 +07:00
< textarea class = " form-control " name = " keterangan " id = " keterangan " required > {{ old ( 'keterangan' , $form -> keterangan ) }} </ textarea >
2024-12-16 17:01:55 +07:00
</ div >
</ div >
2026-06-01 15:01:03 +07:00
{{ -- BAGIAN LAMPIRAN TERSIMPAN DATABASE -- }}
< div class = " col-lg-12 " >
2025-10-13 15:51:53 +07:00
< div class = " mb-4 " >
2026-06-01 15:01:03 +07:00
< label class = " form-label mb-0 " > Lampiran Tersimpan </ label >
< p class = " text-muted small mt-1 mb-2 " > Pratinjau , unduh , atau hapus lampiran yang telah diunggah sebelumnya .</ p >
2025-10-13 15:51:53 +07:00
< div class = " table-responsive " >
2026-06-01 15:01:03 +07:00
< table class = " table table-bordered align-middle " id = " existing-attachments-table " >
2025-10-13 15:51:53 +07:00
< thead class = " table-light " >
< tr >
2026-06-01 15:01:03 +07:00
< th style = " width: 30% " > Kategori Lampiran </ th >
< th style = " width: 30% " > Nama File </ th >
< th style = " width: 15% " class = " text-center " > Pratinjau </ th >
< th style = " width: 15% " class = " text-center " > Unduh </ th >
< th style = " width: 10% " class = " text-center " > Aksi </ th >
2025-10-13 15:51:53 +07:00
</ tr >
</ thead >
< tbody >
@ forelse ( $attachments as $attachment )
2026-06-01 15:01:03 +07:00
@ php
$categoryValue = $attachment [ 'file_category' ] ? ? null ;
$categoryLabel = $categoryValue
? ( $attachmentCategoryLabels [ $categoryValue ] ? ? ucwords ( str_replace ( '_' , ' ' , $categoryValue )))
: '-' ;
@ endphp
< tr data - attachment - id = " { { $attachment['id'] }} " >
< td > {{ $categoryLabel }} </ td >
2025-10-13 15:51:53 +07:00
< td > {{ $attachment [ 'filename' ] ? ? basename ( $attachment [ 'file_path' ]) }} </ td >
< td class = " text-center " >
< button type = " button "
2026-06-01 15:01:03 +07:00
class = " btn btn-sm btn-outline-secondary preview-existing-attachment "
data - preview - url = " { { $attachment['preview_url'] }} "
data - download - url = " { { $attachment['download_url'] }} "
data - preview - type = " { { $attachment['preview_type'] }} "
data - category = " { { $categoryLabel }} " >
Pratinjau
2025-10-13 15:51:53 +07:00
</ button >
</ td >
< td class = " text-center " >
< a href = " { { $attachment['download_url'] }} "
target = " _blank "
2026-06-01 15:01:03 +07:00
class = " btn btn-sm btn-outline-primary " >
Unduh
2025-10-13 15:51:53 +07:00
</ a >
2026-06-01 15:01:03 +07:00
</ td >
< td class = " text-center " >
< button type = " button "
class = " btn btn-sm btn-outline-danger delete-attachment "
data - delete - url = " { { route('forms.vehicle.attachments.destroy', [ $form->id , $attachment['id'] ]) }} " >
< i class = " fas fa-trash " ></ i >
</ button >
2025-10-13 15:51:53 +07:00
</ td >
</ tr >
@ empty
2026-06-01 15:01:03 +07:00
< tr class = " existing-attachments-empty text-center text-muted " >
< td colspan = " 5 " > Belum ada lampiran tersimpan .</ td >
2025-10-13 15:51:53 +07:00
</ tr >
@ endforelse
</ tbody >
</ table >
</ div >
</ div >
</ div >
< div class = " col-12 " >
< div class = " mb-3 " >
< div class = " d-flex align-items-center justify-content-between " >
< label class = " form-label mb-0 " > Tambah Lampiran Baru </ label >
2026-06-01 15:01:03 +07:00
< button type = " button " class = " btn btn-outline-primary btn-sm " id = " vehicle-add-attachment-row " >
< i class = " fas fa-plus mr-1 " ></ i > Add attachment
2025-10-13 15:51:53 +07:00
</ button >
</ div >
< p class = " text-muted small mt-1 mb-2 " >
Maksimal 10 MB per file . Semua tipe file diperbolehkan kecuali < code >. exe </ code > , < code >. bat </ code > , < code >. sh </ code > , < code >. cmd </ code > , < code >. dll </ code > , dan < code >. msi </ code >.
</ p >
< div class = " table-responsive " >
2026-06-01 15:01:03 +07:00
< table class = " table table-bordered align-middle " id = " vehicle-attachments-table " >
2025-10-13 15:51:53 +07:00
< thead class = " table-light " >
< tr >
< th style = " width: 30% " > Kategori </ th >
< th style = " width: 45% " > Lampiran </ th >
< th style = " width: 15% " class = " text-center " > Preview </ th >
< th style = " width: 10% " class = " text-center " > Aksi </ th >
</ tr >
</ thead >
< tbody >
2026-06-01 15:01:03 +07:00
< tr class = " vehicle-attachments-empty text-center text-muted " >
< td colspan = " 4 " > Belum ada lampiran .</ td >
2025-10-13 15:51:53 +07:00
</ tr >
</ tbody >
</ table >
</ div >
</ div >
</ div >
2026-06-01 15:01:03 +07:00
< button type = " submit " class = " btn btn-primary ml-2 " > Simpan Perubahan </ button >
2024-12-16 17:01:55 +07:00
</ div >
2025-06-19 19:39:20 +07:00
</ form >
2025-01-17 16:39:37 +07:00
2025-10-13 15:51:53 +07:00
@ include ( 'backend.components.attachment-preview-modal' , [
2026-06-01 15:01:03 +07:00
'modalId' => 'existingAttachmentPreviewModal' ,
'title' => 'Pratinjau Lampiran' ,
2025-10-13 15:51:53 +07:00
])
@ include ( 'backend.components.attachment-preview-modal' , [
'modalId' => 'vehicleNewAttachmentPreviewModal' ,
'title' => 'Preview Lampiran' ,
])
2025-01-17 16:39:37 +07:00
< div id = " loading-spinner-overlay " class = " d-none " >
< div class = " spinner-wrapper " >
2026-06-01 15:01:03 +07:00
< div class = " spinner-border text-primary " role = " status " ></ div >
< p class = " mt-2 " > Submitting , please wait ...</ p >
2025-01-17 16:39:37 +07:00
</ div >
</ div >
2024-12-16 17:01:55 +07:00
</ div >
</ div >
</ div >
</ section >
2024-12-20 11:07:07 +07:00
< script >
2026-06-01 15:01:03 +07:00
$ ( document ) . ready ( function () {
// Setup Global Token CSRF Khusus untuk Delete File via AJAX
const csrfToken = $ ( 'meta[name="csrf-token"]' ) . attr ( 'content' );
if ( csrfToken ) {
$ . ajaxSetup ({
headers : { 'X-CSRF-TOKEN' : csrfToken }
});
2025-06-19 19:39:20 +07:00
}
2026-06-01 15:01:03 +07:00
// 1. Inisialisasi Aman AutoNumeric (Sesuai Standar Up Country)
const autoNumericConfig = {
digitGroupSeparator : '.' ,
decimalCharacter : ',' ,
currencySymbol : 'Rp. ' ,
decimalPlaces : 0 ,
minimumValue : '0' ,
unformatOnSubmit : true
};
const totalInput = new AutoNumeric ( '#total' , autoNumericConfig );
// 2. Cegah Scroll Wheel
$ ( '#total, #liter, #jarak' ) . on ( 'wheel font-wheel' , function ( e ) {
e . preventDefault ();
$ ( this ) . blur ();
});
2025-10-13 15:51:53 +07:00
2026-06-01 15:01:03 +07:00
// 3. Toggle Logika Field Bensin
const expenseTypeSelect = document . getElementById ( 'expense_type' );
const gasolineFields = document . querySelectorAll ( '.gasoline-fields' );
const literInput = document . getElementById ( 'liter' );
const jarakInput = document . getElementById ( 'jarak' );
const tipeBensinSelect = document . getElementById ( 'tipe_bensin' );
const nopolInput = document . getElementById ( 'nopol' );
function toggleGasolineFields () {
if ( expenseTypeSelect . value === 'gasoline' ) {
gasolineFields . forEach ( field => field . style . display = 'block' );
literInput . setAttribute ( 'required' , 'required' );
jarakInput . setAttribute ( 'required' , 'required' );
tipeBensinSelect . setAttribute ( 'required' , 'required' );
nopolInput . setAttribute ( 'required' , 'required' );
} else {
gasolineFields . forEach ( field => field . style . display = 'none' );
literInput . value = '' ;
jarakInput . value = '' ;
tipeBensinSelect . value = '' ;
nopolInput . value = '' ;
literInput . removeAttribute ( 'required' );
jarakInput . removeAttribute ( 'required' );
tipeBensinSelect . removeAttribute ( 'required' );
nopolInput . removeAttribute ( 'required' );
}
2025-10-13 15:51:53 +07:00
}
2026-06-01 15:01:03 +07:00
toggleGasolineFields ();
expenseTypeSelect . addEventListener ( 'change' , toggleGasolineFields );
// 4. Integrasi Interseptor Form Submit & SweetAlert
const spinnerOverlay = $ ( '#loading-spinner-overlay' );
$ ( '#expense-form' ) . on ( 'submit' , function ( e ) {
e . preventDefault ();
const form = this ;
const totalExpense = totalInput . getNumber () || 0 ;
if ( totalExpense > 1000000 ) {
Swal . fire ({
title : 'Nominal Melebihi Batas Expense!' ,
text : `Total pengajuan Anda adalah Rp ${new Intl.NumberFormat('id-ID').format(totalExpense)}. Jumlah ini melebihi batas standar Rp 1.000.000. Apakah yakin tetap ajukan?` ,
icon : 'warning' ,
showCancelButton : true ,
confirmButtonColor : '#3085d6' ,
cancelButtonColor : '#d33' ,
confirmButtonText : 'Yes, Tetap Simpan!' ,
cancelButtonText : 'No, Batalkan'
}) . then (( result ) => {
if ( result . isConfirmed ) {
spinnerOverlay . removeClass ( 'd-none' ) . addClass ( 'd-flex' );
form . submit ();
}
});
} else {
spinnerOverlay . removeClass ( 'd-none' ) . addClass ( 'd-flex' );
form . submit ();
}
2025-10-13 15:51:53 +07:00
});
2026-06-01 15:01:03 +07:00
// 5. Lampiran Engine AJAX (Existing Table Delete)
$ ( document ) . on ( 'click' , '.delete-attachment' , function () {
const button = $ ( this );
const deleteUrl = button . data ( 'delete-url' );
const $row = button . closest ( 'tr' );
const existingAttachmentsTableBody = $ ( '#existing-attachments-table tbody' );
const emptyExistingRowMarkup = '<tr class="existing-attachments-empty text-center text-muted"><td colspan="5">Belum ada lampiran tersimpan.</td></tr>' ;
2025-10-13 15:51:53 +07:00
2026-06-01 15:01:03 +07:00
Swal . fire ({
title : 'Hapus lampiran?' ,
text : 'Lampiran yang dihapus tidak dapat dikembalikan.' ,
icon : 'warning' ,
showCancelButton : true ,
confirmButtonText : 'Ya, hapus' ,
cancelButtonText : 'Batal'
}) . then (( result ) => {
if ( ! result . isConfirmed ) return ;
2025-10-13 15:51:53 +07:00
2026-06-01 15:01:03 +07:00
$ . ajax ({
url : deleteUrl ,
type : 'DELETE' ,
success : function ( response ) {
$row . remove ();
if ( ! existingAttachmentsTableBody . find ( 'tr' ) . not ( '.existing-attachments-empty' ) . length ) {
existingAttachmentsTableBody . html ( emptyExistingRowMarkup );
}
Swal . fire ( 'Berhasil' , response ? . message || 'Lampiran berhasil dihapus.' , 'success' );
},
error : function ( xhr ) {
const message = xhr ? . responseJSON ? . message || 'Gagal menghapus lampiran.' ;
Swal . fire ( 'Error' , message , 'error' );
}
});
});
});
2025-10-13 15:51:53 +07:00
2026-06-01 15:01:03 +07:00
$ ( document ) . on ( 'click' , '.preview-existing-attachment' , function () {
const button = $ ( this );
const previewType = button . data ( 'preview-type' );
const previewUrl = button . data ( 'preview-url' );
const downloadUrl = button . data ( 'download-url' );
const category = button . data ( 'category' ) || 'Lampiran' ;
2025-10-13 15:51:53 +07:00
2026-06-01 15:01:03 +07:00
if ( ! previewUrl ) {
if ( downloadUrl ) window . open ( downloadUrl , '_blank' );
return ;
}
openAttachmentModal ( '#existingAttachmentPreviewModal' , {
title : category , type : previewType , source : previewUrl
});
});
2025-10-13 15:51:53 +07:00
2026-06-01 15:01:03 +07:00
// 6. Lampiran Engine Tambahan Baru
const attachmentCategories = @ json ( $attachmentCategories ? ? ( $vehicleAttachmentCategories ? ? []));
const blockedExtensions = [ 'exe' , 'bat' , 'sh' , 'cmd' , 'dll' , 'msi' ];
const maxFileSizeBytes = 10 * 1024 * 1024 ;
const vehicleAttachmentEmptyRow = '<tr class="vehicle-attachments-empty text-center text-muted"><td colspan="4">Belum ada lampiran.</td></tr>' ;
let vehicleAttachmentIndex = 0 ;
2025-10-13 15:51:53 +07:00
2026-06-01 15:01:03 +07:00
function escapeHtml ( value ) {
return String ( value || '' )
. replace ( /&/ g , '&' ) . replace ( /</ g , '<' ) . replace ( />/ g , '>' ) . replace ( / " /g, '"').replace(/'/g, ''');
2025-10-13 15:51:53 +07:00
}
2026-06-01 15:01:03 +07:00
function categoryToLabel(value) {
if (!value) return 'Pilih Kategori';
return value.replace(/_/g, ' ').replace(/ \ b \ w/g, function (char) { return char.toUpperCase(); });
2025-10-13 15:51:53 +07:00
}
2026-06-01 15:01:03 +07:00
function detectPreviewType(extension) {
const images = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
if (images.indexOf(extension) !== -1) return 'image';
if (extension === 'pdf') return 'pdf';
return 'other';
2025-10-13 15:51:53 +07:00
}
2026-06-01 15:01:03 +07:00
function buildCategoryOptions() {
return attachmentCategories.map(function (category) {
return '<option value= " ' + category + ' " >' + escapeHtml(categoryToLabel(category)) + '</option>';
}).join('');
2025-10-13 15:51:53 +07:00
}
2026-06-01 15:01:03 +07:00
function setInlinePreview( $row , type, source) {
const $box = $row .find('.vehicle-attachment-inline-preview');
$box .removeClass('bg-light').html('<i class= " fas fa - file - upload text - muted " ></i>');
if (type === 'image' && source) {
$box .addClass('bg-light').html('<img src= " ' + source + ' " class= " img - thumbnail " style= " width : 100 % ; height : 100 % ; object - fit : cover ; " alt= " Preview " >');
} else if (type === 'pdf') {
$box .addClass('bg-light').html('<i class= " fas fa - file - pdf text - danger fa - lg " ></i>');
} else if (type === 'other') {
$box .addClass('bg-light').html('<i class= " fas fa - file - alt text - secondary fa - lg " ></i>');
}
2025-10-13 15:51:53 +07:00
}
2026-06-01 15:01:03 +07:00
function resetAttachmentRowData( $row , clearInput = true) {
const existingUrl = $row .data('objectUrl');
if (existingUrl) URL.revokeObjectURL(existingUrl);
$row .removeData('objectUrl').removeData('previewType').removeData('previewSource').removeData('previewFileName').removeData('downloadUrl');
setInlinePreview( $row , null, null);
$row .find('.vehicle-preview-new-attachment').addClass('d-none');
$row .find('.vehicle-preview-filename').text('');
if (clearInput) $row .find('.vehicle-attachment-file').val('');
}
2025-10-13 15:51:53 +07:00
2026-06-01 15:01:03 +07:00
function openAttachmentModal(modalSelector, options) {
const settings = Object.assign( { title: 'Lampiran', type: 'other', source: null, downloadUrl: null, fileName: 'Lampiran' }, options || { });
const $modal = $ (modalSelector);
const $image = $modal .find('.attachment-preview-image');
const $object = $modal .find('.attachment-preview-object');
const $placeholder = $modal .find('.attachment-preview-placeholder');
$modal .find('.attachment-preview-modal-title').text(settings.title || 'Lampiran');
$image .addClass('d-none').attr('src', '');
$object .addClass('d-none').attr('data', '').attr('src', '');
$placeholder .removeClass('d-none').html('Tidak ada file untuk ditampilkan.');
if (settings.type === 'image' && settings.source) {
$image .attr('src', settings.source).removeClass('d-none');
$placeholder .addClass('d-none');
} else if (settings.type === 'pdf' && settings.source) {
$object .attr('data', settings.source).attr('src', settings.source).removeClass('d-none');
$placeholder .addClass('d-none');
} else {
const safeName = escapeHtml(settings.fileName || 'Lampiran');
let message = '<p class= " mb - 2 " >' + safeName + '</p><p class= " text - muted mb - 0 " >Preview tidak tersedia. Silakan unduh file untuk melihat konten.</p>';
if (settings.downloadUrl) message += '<div class= " mt - 3 " ><a href= " ' + settings.downloadUrl + ' " target= " _blank " class= " btn btn - sm btn - outline - primary " >Download</a></div>';
$placeholder .html(message);
}
$modal .modal('show');
2025-10-13 15:51:53 +07:00
}
2026-06-01 15:01:03 +07:00
function addVehicleAttachmentRow() {
const $tbody = $ ('#vehicle-attachments-table tbody');
if ( $tbody .find('.vehicle-attachments-empty').length) $tbody .empty();
const index = vehicleAttachmentIndex++;
const rowHtml = `
<tr class= " vehicle - attachment - row " data-index= " $ { index } " >
<td>
<select class= " form - control vehicle - attachment - category " name= " attachments [ $ { index }][ file_category ] " required>
<option value= " " > ${ escapeHtml(categoryToLabel('')) } </option>
${ buildCategoryOptions() }
</select>
</td>
<td>
<div class= " d - flex align - items - center " >
<div class= " vehicle - attachment - inline - preview border rounded d - flex align - items - center justify - content - center bg - white mr - 2 " style= " width : 56 px ; height : 56 px ; " >
<i class= " fas fa - file - upload text - muted " ></i>
</div>
<input type= " file " class= " form - control vehicle - attachment - file " name= " attachments [ $ { index }][ file_path ] " required>
2025-10-13 15:51:53 +07:00
</div>
2026-06-01 15:01:03 +07:00
<div class= " small text - muted mt - 1 vehicle - preview - filename " ></div>
</td>
<td class= " text - center " >
<button type= " button " class= " btn btn - sm btn - outline - secondary vehicle - preview - new - attachment d - none " >Preview</button>
</td>
<td class= " text - center " >
<button type= " button " class= " btn btn - sm btn - outline - danger vehicle - remove - attachment - row " ><i class= " fas fa - trash " ></i></button>
</td>
</tr>
`;
$tbody .append(rowHtml);
}
2025-10-13 15:51:53 +07:00
2026-06-01 15:01:03 +07:00
$ ('#vehicle-add-attachment-row').on('click', function () { addVehicleAttachmentRow(); });
2025-10-13 15:51:53 +07:00
2026-06-01 15:01:03 +07:00
$ (document).on('click', '.vehicle-remove-attachment-row', function () {
2025-10-13 15:51:53 +07:00
const $row = $ (this).closest('tr');
2026-06-01 15:01:03 +07:00
const $tbody = $ ('#vehicle-attachments-table tbody');
resetAttachmentRowData( $row , true);
2025-10-13 15:51:53 +07:00
$row .remove();
2026-06-01 15:01:03 +07:00
if (! $tbody .find('.vehicle-attachment-row').length) $tbody .html(vehicleAttachmentEmptyRow);
2025-10-13 15:51:53 +07:00
});
2026-06-01 15:01:03 +07:00
$ (document).on('change', '.vehicle-attachment-file', function () {
2025-10-13 15:51:53 +07:00
const $input = $ (this);
const $row = $input .closest('tr');
const previewButton = $row .find('.vehicle-preview-new-attachment');
2026-06-01 15:01:03 +07:00
const filenameHolder = $row .find('.vehicle-preview-filename');
2025-10-13 15:51:53 +07:00
2026-06-01 15:01:03 +07:00
resetAttachmentRowData( $row , false);
2025-10-13 15:51:53 +07:00
filenameHolder.text('');
const file = this.files && this.files[0];
2026-06-01 15:01:03 +07:00
if (!file) return;
2025-10-13 15:51:53 +07:00
if (file.size > maxFileSizeBytes) {
alert('Ukuran file melebihi 10 MB.');
$input .val('');
return;
}
const extension = (file.name.split('.').pop() || '').toLowerCase();
if (blockedExtensions.indexOf(extension) !== -1) {
alert('Tipe file tidak diperbolehkan.');
$input .val('');
return;
}
filenameHolder.text(escapeHtml(file.name));
const previewType = detectPreviewType(extension);
$row .data('previewType', previewType);
$row .data('previewFileName', file.name);
$row .data('downloadUrl', null);
if (previewType === 'image') {
const reader = new FileReader();
reader.onload = function (event) {
const source = event.target.result;
$row .data('previewSource', source);
setInlinePreview( $row , previewType, source);
};
reader.readAsDataURL(file);
} else if (previewType === 'pdf') {
const objectUrl = URL.createObjectURL(file);
$row .data('previewSource', objectUrl);
$row .data('objectUrl', objectUrl);
setInlinePreview( $row , previewType, null);
} else {
$row .data('previewSource', null);
setInlinePreview( $row , 'other', null);
}
previewButton.removeClass('d-none');
});
$ (document).on('click', '.vehicle-preview-new-attachment', function () {
const $row = $ (this).closest('tr');
const previewType = $row .data('previewType') || 'other';
const previewSource = $row .data('previewSource') || null;
const fileName = $row .data('previewFileName') || 'Lampiran';
openAttachmentModal('#vehicleNewAttachmentPreviewModal', {
title: fileName,
type: previewType,
source: previewSource,
downloadUrl: null,
fileName: fileName
});
});
});
2024-12-20 11:07:07 +07:00
</script>
2026-06-01 15:01:03 +07:00
@endsection