Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/Http/Controllers/DataPokokController.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@ public function kesehatan()
return view('data_pokok.kesehatan.index', ['title' => $title]);
}

public function cetakKesehatan(Request $request)
{
return view('data_pokok.kesehatan.cetak', ['filter' => $request->getQueryString()]);
}

public function jaminanSosial()
{
$title = 'Data Kepesertaan Program dan Statistik';
Expand Down
205 changes: 205 additions & 0 deletions resources/views/components/excel-download-button.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
@props([
'id' => 'download-excel',
'label' => 'Excel',
'size' => 'btn-sm',
'variant' => 'btn-success',
'icon' => 'fa fa-file-excel',
'disabled' => false,
'loadingText' => 'Downloading...',
'downloadUrl' => '',
'tableId' => '',
'filename' => 'data_export',
'apiHeaders' => [],
'additionalParams' => [],
'class' => '',
])

<button id="{{ $id }}" type="button" class="btn {{ $variant }} {{ $size }} {{ $class }}"
@if ($disabled) disabled @endif data-download-url="{{ $downloadUrl }}"
data-table-id="{{ $tableId }}" data-filename="{{ $filename }}">
<i class="{{ $icon }}"></i>
{{ $label }}
</button>

@once
@push('js')
<script nonce="{{ csp_nonce() }}">
document.addEventListener("DOMContentLoaded", function() {
// Handle all excel download buttons
document.querySelectorAll('[data-download-url]').forEach(function(button) {
button.addEventListener('click', function() {
const downloadUrl = this.getAttribute('data-download-url');
const tableId = this.getAttribute('data-table-id');
const filename = this.getAttribute('data-filename');

downloadExcelData(this, downloadUrl, tableId, filename);
});
});

async function downloadExcelData(button, downloadUrl, tableId, filename) {
const originalHtml = button.innerHTML;
try {
// Get headers - try to use the global header from the page
const headers = @include('layouts.components.header_bearer_api_gabungan');

// Get table instance if tableId is provided
if (tableId) {
const table = $('#' + tableId).DataTable();
const info = table.page.info();
const totalData = info.recordsTotal;

if (totalData === 0) {
if (typeof Swal !== 'undefined') {
Swal.fire({
icon: 'warning',
title: 'Tidak Ada Data',
text: 'Tidak ada data untuk diunduh. Silakan periksa filter Anda.',
confirmButtonText: 'OK'
});
} else {
alert('Tidak ada data untuk diunduh');
}
return;
}
}

// Show loading state
button.disabled = true;
button.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Downloading...';

// Prepare download URL
const url = new URL(downloadUrl);

let urlParams = new URLSearchParams();

if (tableId) {
// Get filter parameters from DataTable
const table = $('#' + tableId).DataTable();
const filterParams = table.ajax.params();

// Remove pagination parameters since we want all data
delete filterParams['page[size]'];
delete filterParams['page[number]'];

// Handle umur filter conversion if exists
if (filterParams['filter[umur]'] && typeof filterParams['filter[umur]'] === 'object') {
const umurObj = filterParams['filter[umur]'];

if (umurObj.min && umurObj.min !== '') {
filterParams['filter[umur][min]'] = umurObj.min;
}
if (umurObj.max && umurObj.max !== '') {
filterParams['filter[umur][max]'] = umurObj.max;
}
if (umurObj.satuan) {
filterParams['filter[umur][satuan]'] = umurObj.satuan;
}

delete filterParams['filter[umur]'];
}

// Convert to URLSearchParams
Object.keys(filterParams).forEach(key => {
const value = filterParams[key];
if (value !== null && value !== undefined && value !== '' && value !==
'null') {
urlParams.append(key, value);
}
});

const info = table.page.info();
urlParams.append('totalData', info.recordsTotal);
}

// Make fetch request
const response = await fetch(url, {
method: 'POST',
headers: {
...headers,
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
},
body: urlParams
});

if (!response.ok) {
const errorText = await response.text();
throw new Error(`HTTP ${response.status}: ${errorText}`);
}

// Validate response content type
const contentType = response.headers.get('content-type');
if (!contentType || (!contentType.includes(
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') &&
!contentType.includes('application/vnd.ms-excel'))) {
throw new Error('Response is not a valid Excel file');
}

// Get filename from response headers or generate one
const contentDisposition = response.headers.get('content-disposition');
let finalFilename = filename + '.xlsx';

if (contentDisposition) {
const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(contentDisposition);
if (matches != null && matches[1]) {
finalFilename = matches[1].replace(/['"]/g, '');
}
} else {
// Generate filename with timestamp
const now = new Date();
const timestamp = now.toISOString().slice(0, 19).replace(/[-:T]/g, '');
finalFilename = `${filename}_${timestamp}.xlsx`;
}

// Create blob and download
const blob = await response.blob();
const downloadLink = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadLink;
a.download = finalFilename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(downloadLink);
document.body.removeChild(a);

// Show success message
if (typeof Swal !== 'undefined') {
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: `File Excel "${finalFilename}" berhasil diunduh`,
timer: 3000,
showConfirmButton: false
});
}

} catch (error) {
console.error('Download error:', error);

// Show error message
if (typeof Swal !== 'undefined') {
Swal.fire({
icon: 'error',
title: 'Gagal Download!',
html: `
<p>Terjadi kesalahan saat mengunduh file Excel:</p>
<p><small>${error.message}</small></p>
<p>Silakan coba lagi atau hubungi administrator.</p>
`,
confirmButtonText: 'OK'
});
} else {
alert('Gagal download: ' + error.message);
}
} finally {
// Reset button state
console.log('Resetting button state');
console.log('Original HTML:', originalHtml);
button.disabled = false;
button.innerHTML = originalHtml;
}
}
});
</script>
@endpush
@endonce
69 changes: 69 additions & 0 deletions resources/views/components/print-button.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{{-- resources/views/components/print-button.blade.php --}}
@props([
'label' => 'Cetak',
'icon' => 'fa fa-print',
'variant' => 'btn-primary',
'size' => 'btn-sm',
'printUrl' => '',
'tableId' => '',
'filters' => [],
'additionalParams' => [],
])

@php
$buttonId = 'print-btn-' . $tableId;
@endphp

<button type="button" class="btn {{ $variant }} {{ $size }}" id="{{ $buttonId }}"
data-print-url="{{ $printUrl }}" data-table-id="{{ $tableId }}" data-filters="{{ json_encode($filters) }}"
data-additional-params="{{ json_encode($additionalParams) }}">
<i class="{{ $icon }}"></i>
{{ $label }}
</button>

<script nonce="{{ csp_nonce() }}">
document.addEventListener("DOMContentLoaded", function() {
const printButton = document.getElementById('{{ $buttonId }}');

if (printButton) {
printButton.addEventListener('click', function(e) {
e.preventDefault();

const printUrl = new URL(this.dataset.printUrl);
const tableId = this.dataset.tableId;
const filters = JSON.parse(this.dataset.filters || '{}');
const additionalParams = JSON.parse(this.dataset.additionalParams || '{}');

// Add filter parameters
Object.entries(filters).forEach(([filterId, paramName]) => {
const filterElement = document.getElementById(filterId);
if (filterElement && filterElement.value) {
printUrl.searchParams.append(paramName, filterElement.value);
}
});

// Add search parameter from DataTable
if (tableId) {
const dataTable = $('#' + tableId).DataTable();
// Get filters/search from DataTable's ajax.params
if (typeof dataTable.ajax.params === 'function') {
const params = dataTable.ajax.params();
Object.entries(params).forEach(([key, value]) => {
if (value && value !== '' && value !== 'null') {
printUrl.searchParams.append(key, value);
}
});
}
}

// Add additional static parameters
Object.entries(additionalParams).forEach(([key, value]) => {
printUrl.searchParams.append(key, value);
});

// Open print URL in new window
window.open(printUrl.href, '_blank');
});
}
});
</script>
76 changes: 76 additions & 0 deletions resources/views/data_pokok/kesehatan/cetak.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
@extends('layouts.cetak.index')

@section('title', 'Data Kesehatan')

@section('content')
@include('partials.breadcrumbs')
<div x-data="{
data: {},
async retrievePosts() {
try {
const headers = @include('layouts.components.header_bearer_api_gabungan');
var create_url = new URL({{ json_encode(config('app.databaseGabunganUrl')) }} + '/api/v1/data/kesehatan');

// Get current URL parameters and add them to create_url
const currentUrl = new URL(window.location.href);
const urlParams = currentUrl.searchParams;

// Add all search parameters from current URL
for (const [key, value] of urlParams.entries()) {
if (value && value !== '' && value !== 'null') {
create_url.searchParams.set(key, value);
}
}

const response = await fetch(create_url.href, {
method: 'GET',
headers: headers
});

if (!response.ok) throw new Error('Gagal mengambil data');

const result = await response.json();
this.data = result.data;

await $nextTick();
window.print();
} catch (error) {
console.error('Terjadi kesalahan:', error);
alert('Terjadi kesalahan saat mengambil data.');
}
}
}" x-init="retrievePosts">
<table class="border thick" id="tabel-penduduk">
<thead>
<tr class="border thick">
<th class="padat">No</th>
<th class="padat">Nama {{ config('app.sebutanDesa') }}</th>
<th class="padat">NIK</th>
<th class="padat">Golongan Darah</th>
<th class="padat">Cacat</th>
<th class="padat">Sakit Menahun</th>
<th class="padat">Akseptor KB</th>
<th class="padat">Status Kehamilan</th>
<th class="padat">Asuransi Kesehatan</th>
<th class="padat">Status Gizi Balita</th>
</tr>
</thead>
<tbody>
<template x-for="(value, index) in data">
<tr>
<td class="padat" x-text="index+1"></td>
<td x-text="value.attributes.nama_desa"></td>
<td x-text="value.attributes.nik"></td>
<td x-text="value.attributes.golongan_darah"></td>
<td x-text="value.attributes.cacat"></td>
<td x-text="value.attributes.sakit_menahun"></td>
<td x-text="value.attributes.kb"></td>
<td x-text="value.attributes.hamil"></td>
<td x-text="value.attributes.asuransi"></td>
<td x-text="value.attributes.status_gizi"></td>
</tr>
</template>
</tbody>
</table>
</div>
@stop
Loading