Skip to content

Commit a316ad3

Browse files
authored
Merge pull request #793 from OpenSID/dev-789
Tambahkan download excel pada data penduduk
2 parents 05fd156 + 849a145 commit a316ad3

2 files changed

Lines changed: 153 additions & 100 deletions

File tree

catatan_rilis.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Di rilis ini, versi 2508.0.0 berisi penambahan dan perbaikan yang diminta penggu
33
#### Penambahan Fitur
44

55
1. [#784](https://github.com/OpenSID/OpenKab/issues/784) Penambahan fungsi export excel pada halaman data statistik pada OpenKab.
6+
2. [#789](https://github.com/OpenSID/OpenKab/issues/789) Penambahan fungsi export data ke excel.
67

78
#### Perbaikan BUG
89

resources/views/penduduk/index.blade.php

Lines changed: 152 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
<i class="fa fa-print"></i>
4545
Cetak
4646
</button>
47+
<button id="download-excel" type="button" class="btn btn-success btn-sm">
48+
<i class="fa fa-file-excel"></i>
49+
Excel
50+
</button>
4751
</div>
4852
</div>
4953
</div>
@@ -91,6 +95,7 @@
9195
@include('components.wilayah_filter_js')
9296
@push('js')
9397
<script src="{{ asset('assets/progressive-image/progressive-image.js') }}"></script>
98+
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
9499
@endpush
95100

96101
@section('js')
@@ -202,20 +207,6 @@
202207
203208
}
204209
return json.data;
205-
206-
// if (json.data.length) {
207-
208-
// // jika chart di akses dari halaman statistik penduduk
209-
// if(chart && chart.view){
210-
// getDataset(json.data, chart)
211-
// grafik()
212-
// }
213-
214-
215-
// return json.data;
216-
// }
217-
218-
// return false;
219210
},
220211
},
221212
columnDefs: [{
@@ -381,6 +372,10 @@ className: 'text-nowrap',
381372
window.open(`{{ url('penduduk/cetak') }}?${$.param(penduduk.ajax.params())}`, '_blank');
382373
});
383374
375+
$('#download-excel').on('click', function() {
376+
downloadExcel();
377+
});
378+
384379
$('select.select2-filter').each(function() {
385380
$(this).select2({
386381
width: '100%',
@@ -393,8 +388,6 @@ className: 'text-nowrap',
393388
for (let i in filterDefault) {
394389
$(`#${i}`).val(filterDefault[i]).trigger('change');
395390
}
396-
397-
398391
});
399392
400393
function getDataset(data, chart) {
@@ -495,89 +488,148 @@ function getDataset(data, chart) {
495488
}));
496489
}
497490
498-
// function getDataset(data, chart) {
499-
// const kategori = chart.kategori;
500-
// data_grafik = [];
501-
// const grouped = {};
502-
// judul = chart.nama
503-
504-
// const getLabel = {
505-
// 'rentang-umur': attr => parseInt(attr.umur),
506-
// 'kategori-umur': attr => attr.kategori_umur?.nama,
507-
// 'pendidikan-dalam-kk': attr => attr.pendidikan_k_k?.nama,
508-
// 'pendidikan-sedang-ditempuh': attr => attr.pendidikan?.nama,
509-
// 'agama': attr => attr.agama?.nama,
510-
// 'jenis-kelamin': attr => attr.jenis_kelamin?.nama,
511-
// 'pekerjaan': attr => attr.pekerjaan?.nama,
512-
// 'status-perkawinan': attr => attr.status_kawin?.nama,
513-
// 'hubungan-dalam-kk': attr => attr.penduduk_hubungan?.nama,
514-
// 'warga-negara': attr => attr.warga_negara?.nama,
515-
// 'status-penduduk': attr => attr.penduduk_status?.nama,
516-
// 'golongan-darah': attr => attr.golongan_darah?.nama,
517-
// 'penyandang-cacat': attr => attr.cacat?.nama,
518-
// 'penyakit-menahun': attr => attr.namaSakitMenahun,
519-
// 'akseptor-kb': attr => attr.kb?.nama,
520-
// 'akta-kelahiran': attr => parseInt(attr.umur),
521-
// 'ktp': attr => attr.status_rekam_ktp?.nama,
522-
// 'asuransi-kesehatan': attr => attr.namaAsuransi,
523-
// 'status-covid': attr => null,
524-
// 'suku': attr => attr.suku,
525-
// 'bpjs-ketenagakerjaan': attr => attr.bpjs_ketenagakerjaan,
526-
// 'status-kehamilan': attr => attr.statusHamil,
527-
// };
528-
529-
// const isMatch = {
530-
// 'rentang-umur': (label) => {
531-
// const [awal, akhir] = judul.match(/\d+/g).map(Number);
532-
// return label >= awal && label <= akhir;
533-
// },
534-
// 'kategori-umur': (label) => label === judul,
535-
// 'pendidikan-dalam-kk': (label) => label === judul,
536-
// 'pendidikan-sedang-ditempuh': (label) => label === judul,
537-
// 'agama': (label) => label === judul,
538-
// 'jenis-kelamin': (label) => label === judul,
539-
// 'pekerjaan': (label) => label === judul,
540-
// 'status-perkawinan': (label) => label === judul,
541-
// 'hubungan-dalam-kk': (label) => label === judul,
542-
// 'warga-negara': (label) => label === judul,
543-
// 'status-penduduk': (label) => label === judul,
544-
// 'golongan-darah': (label) => label === judul,
545-
// 'penyandang-cacat': (label) => label === judul,
546-
// 'penyakit-menahun': (label) => label === judul,
547-
// 'akseptor-kb': (label) => label === judul,
548-
// 'akta-kelahiran': (label) => {
549-
// const [awal, akhir] = judul.match(/\d+/g).map(Number);
550-
// return label >= awal && label <= akhir;
551-
// },
552-
// 'ktp': (label) => label === judul,
553-
// 'asuransi-kesehatan': (label) => label === judul,
554-
// 'status-covid': (label) => label === judul,
555-
// 'suku': (label) => label === judul,
556-
// 'bpjs-ketenagakerjaan': (label) => label === judul,
557-
// 'status-kehamilan': (label) => label === judul,
558-
// };
559-
560-
// data.forEach(item => {
561-
// const attr = item.attributes;
562-
// const config = attr.config || {};
563-
// const kode = config.kode_kecamatan;
564-
// const nama = config.nama_kecamatan;
565-
566-
// if (!grouped[kode]) {
567-
// grouped[kode] = { nama: nama, total: 0 };
568-
// }
569-
570-
// const label = getLabel[kategori]?.(attr);
571-
572-
// if (label !== undefined && isMatch[kategori]?.(label)) {
573-
// grouped[kode].total += 1;
574-
// }
575-
// });
576-
577-
// data_grafik = Object.entries(grouped).map(([kode, val]) => ({
578-
// label: val.nama,
579-
// value: val.total
580-
// }));
581-
// }
491+
// Function to download Excel
492+
async function downloadExcel() {
493+
try {
494+
const header = @include('layouts.components.header_bearer_api_gabungan');
495+
// Check if there's data to download
496+
const tableData = $('#penduduk').DataTable();
497+
const info = tableData.page.info();
498+
const totalData = info.recordsTotal;
499+
if (totalData === 0) {
500+
Swal.fire({
501+
icon: 'warning',
502+
title: 'Tidak Ada Data',
503+
text: 'Tidak ada data penduduk untuk diunduh. Silakan periksa filter Anda.',
504+
confirmButtonText: 'OK'
505+
});
506+
return;
507+
}
508+
509+
// Show loading state
510+
const $btnExcel = $('#download-excel');
511+
$btnExcel.prop('disabled', true).html(
512+
'<i class="fa fa-spinner fa-spin"></i> Downloading...');
513+
514+
// Prepare URL for download
515+
const downloadUrl = new URL(
516+
`{{ config('app.databaseGabunganUrl') }}/api/v1/penduduk/download`);
517+
518+
// Gunakan fungsi data yang sama persis dengan DataTable untuk konsistensi
519+
const filterParams = tableData.ajax.params();
520+
521+
// Remove pagination parameters since we want all data
522+
delete filterParams['page[size]'];
523+
delete filterParams['page[number]'];
524+
525+
// Handle umur filter - convert object to separate min/max parameters for backend
526+
if (filterParams['filter[umur]'] && typeof filterParams['filter[umur]'] === 'object') {
527+
const umurObj = filterParams['filter[umur]'];
528+
529+
// Create separate parameters for min and max
530+
if (umurObj.min && umurObj.min !== '') {
531+
filterParams['filter[umur][min]'] = umurObj.min;
532+
}
533+
if (umurObj.max && umurObj.max !== '') {
534+
filterParams['filter[umur][max]'] = umurObj.max;
535+
}
536+
if (umurObj.satuan) {
537+
filterParams['filter[umur][satuan]'] = umurObj.satuan;
538+
}
539+
540+
// Remove the original object parameter
541+
delete filterParams['filter[umur]'];
542+
}
543+
544+
// Convert filterParams to URLSearchParams for proper encoding
545+
const urlParams = new URLSearchParams();
546+
Object.keys(filterParams).forEach(key => {
547+
const value = filterParams[key];
548+
if (value !== null && value !== undefined && value !== '' && value !== 'null') {
549+
urlParams.append(key, value);
550+
}
551+
});
552+
553+
urlParams.append('totalData', totalData);
554+
555+
// Make fetch request
556+
const response = await fetch(downloadUrl, {
557+
method: 'POST',
558+
headers: {
559+
...header,
560+
'Content-Type': 'application/x-www-form-urlencoded',
561+
'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
562+
},
563+
body: urlParams
564+
});
565+
566+
if (!response.ok) {
567+
const errorText = await response.text();
568+
throw new Error(`HTTP ${response.status}: ${errorText}`);
569+
}
570+
571+
// Check if response is actually a file
572+
const contentType = response.headers.get('content-type');
573+
if (!contentType || (!contentType.includes(
574+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') && !
575+
contentType.includes('application/vnd.ms-excel'))) {
576+
throw new Error('Response is not a valid Excel file');
577+
}
578+
579+
// Get filename from response headers or generate one
580+
const contentDisposition = response.headers.get('content-disposition');
581+
let filename = 'data_penduduk.xlsx';
582+
if (contentDisposition) {
583+
const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(contentDisposition);
584+
if (matches != null && matches[1]) {
585+
filename = matches[1].replace(/['"]/g, '');
586+
}
587+
} else {
588+
// Generate filename with timestamp
589+
const now = new Date();
590+
const timestamp = now.toISOString().slice(0, 19).replace(/[-:T]/g, '');
591+
filename = `data_penduduk_${timestamp}.xlsx`;
592+
}
593+
594+
// Create blob and download
595+
const blob = await response.blob();
596+
const url = window.URL.createObjectURL(blob);
597+
const a = document.createElement('a');
598+
a.href = url;
599+
a.download = filename;
600+
document.body.appendChild(a);
601+
a.click();
602+
window.URL.revokeObjectURL(url);
603+
document.body.removeChild(a);
604+
605+
// Show success message
606+
Swal.fire({
607+
icon: 'success',
608+
title: 'Berhasil!',
609+
text: `File Excel "${filename}" berhasil diunduh`,
610+
timer: 3000,
611+
showConfirmButton: false
612+
});
613+
614+
} catch (error) {
615+
console.error('Download error:', error);
616+
617+
// Show error message with SweetAlert
618+
Swal.fire({
619+
icon: 'error',
620+
title: 'Gagal Download!',
621+
html: `
622+
<p>Terjadi kesalahan saat mengunduh file Excel:</p>
623+
<p><small>${error.message}</small></p>
624+
<p>Silakan coba lagi atau hubungi administrator.</p>
625+
`,
626+
confirmButtonText: 'OK'
627+
});
628+
} finally {
629+
// Reset button state
630+
const $btnExcel = $('#download-excel');
631+
$btnExcel.prop('disabled', false).html('<i class="fa fa-file-excel"></i> Excel');
632+
}
633+
}
582634
</script>
583635
@endsection

0 commit comments

Comments
 (0)