feat: add reporit

This commit is contained in:
Nawcodes 2025-04-26 23:48:27 +07:00
parent 6dd24df259
commit 2397e1ed7e
6 changed files with 280 additions and 11 deletions

View File

@ -5,6 +5,7 @@ namespace App\Filament\Resources\TrTransaksiResource\Pages;
use App\Filament\Resources\TrTransaksiResource;
use Filament\Actions;
use Filament\Resources\Pages\ViewRecord;
use Torgodly\Html2Media\Actions\Html2MediaAction;
class ViewTrTransaksi extends ViewRecord
{
@ -26,6 +27,23 @@ class ViewTrTransaksi extends ViewRecord
})->visible(function ($record) {
return $record->status == 'pending';
}),
Html2MediaAction::make('print')
->scale(2)
->print() // Enable print option
->preview()
->filename(function ($record) {
return 'invoice-' . $record->id_transaksi . '.pdf';
})
->content(function ($record) {
return view('components.pdf.invoice-detail', ['record' => $record]);
})
->savePdf() // Enable save as PDF option
->requiresConfirmation() // Show confirmation modal
->pagebreak('section', ['css', 'legacy'])
->orientation('portrait') // Portrait orientation
->format('a4', 'mm') // A4 format with mm units
->enableLinks() // Enable links in PDF
->margin([25, 50, 0, 50]) //
];
}
}

View File

@ -15,17 +15,21 @@ class StatsOverview extends BaseWidget
protected function getStats(): array
{
return [
Stat::make('Total Pasien', TrRegistrasi::count())
->description(new HtmlString('<a class="underline" href="' . TrRegistrasiResource::getUrl('index') . '">Lihat Semua Pasien</a>')),
// total pasien hari ini, deskripsi jumlah keseluruhan pasien dengan link ke halaman pasien
Stat::make('Total Pasien Hari Ini', TrRegistrasi::whereDate('tgl_registrasi', now()->toDateString())->count())
->description(new HtmlString(
'Jumlah keseluruhan ' . TrRegistrasi::count() . ' pasien'
. '<br/><a class="underline" href="' . TrRegistrasiResource::getUrl('index') . '"> Lihat Semua Pasien</a>'
)),
// total tagihan, deskripsi jumlah keseluruhan tagihan dengan link ke halaman tagihan
Stat::make('Total Pendapatan Hari Ini', 'Rp ' . number_format(TrTransaksi::whereDate('created_at', now()->toDateString())->where('status', 'paid')->sum('total_harga'), 0, ',', '.'))
->description(new HtmlString(
'Jumlah keseluruhan <strong>Rp.' . number_format(TrTransaksi::where('status', 'paid')->sum('total_harga'), 0, ',', '.') . '</strong> tagihan'
. '<br/><a class="underline" href="' . TrTransaksiResource::getUrl('index') . '"> Lihat Semua Tagihan</a>'
)),
// section 2
//
Stat::make('Total Tagihan', 'Rp ' . number_format(TrTransaksi::sum('total_harga'), 0, ',', '.'))
->description(new HtmlString('<a class="underline" href="' . TrTransaksiResource::getUrl('index') . '">Lihat Semua Tagihan</a>')),
// Registrasi yang belum ada transaksi
Stat::make('Total Pasien Belum Ada Transaksi', TrRegistrasi::whereDoesntHave('transaksi')->count())
->description(new HtmlString('<a class="underline" href="' . TrRegistrasiResource::getUrl('index') . '">Lihat Semua Pasien Belum Ada Transaksi</a>')),
];
}

View File

@ -9,7 +9,8 @@
"php": "^8.2",
"filament/filament": "^3.3",
"laravel/framework": "^11.31",
"laravel/tinker": "^2.9"
"laravel/tinker": "^2.9",
"torgodly/html2media": "^1.1"
},
"require-dev": {
"fakerphp/faker": "^1.23",

61
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "b10697b45fe4a086f6982a8e9998fea8",
"content-hash": "53b21f71a3004eddc3866a7af901cea9",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@ -7241,6 +7241,65 @@
},
"time": "2024-12-21T16:25:41+00:00"
},
{
"name": "torgodly/html2media",
"version": "1.1.4",
"source": {
"type": "git",
"url": "https://github.com/torgodly/Html2Media.git",
"reference": "58c31b08fc46fa2dbf307e7bf88d833c4d933c05"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/torgodly/Html2Media/zipball/58c31b08fc46fa2dbf307e7bf88d833c4d933c05",
"reference": "58c31b08fc46fa2dbf307e7bf88d833c4d933c05",
"shasum": ""
},
"require": {
"filament/filament": "^3.0",
"php": "^8.1"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Torgodly\\Html2Media\\Html2MediaServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Torgodly\\Html2Media\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Abdullah Alhaj",
"email": "torgodly@gmail.com"
}
],
"description": "Html2Media is a versatile Laravel package that allows users to convert HTML content into high-quality PDFs with options for either downloading or triggering a print dialog. Ideal for generating documents, invoices, and reports, this package includes configurable settings for file name, page orientation, format, margins, and scale. Html2Media also provides seamless integration with Filament actions, enabling dynamic content rendering in modals and customizable output previews. Whether you need to save a PDF or send it directly to the printer, Html2Media simplifies the process with robust, flexible features.",
"keywords": [
"documents",
"filament",
"html-to-pdf",
"invoice",
"laravel",
"pdf",
"pdf-generation",
"printing",
"reporting"
],
"support": {
"issues": "https://github.com/torgodly/Html2Media/issues",
"source": "https://github.com/torgodly/Html2Media/tree/1.1.4"
},
"time": "2025-03-15T22:59:43+00:00"
},
{
"name": "vlucas/phpdotenv",
"version": "v5.6.1",

View File

@ -0,0 +1,97 @@
document.addEventListener('DOMContentLoaded', function () {
Livewire.on('triggerPrint', function (options = {}) {
console.log('triggerPrint', options);
performAction(options);
});
});
function performAction({ action = 'print', element, ...customOptions } = {}) {
const printElement = document.getElementById(`print-smart-content-${element}`);
// Default options for html2pdf
const defaultOptions = {
filename: 'document.pdf',
pagebreak: {
mode: ['css', 'legacy'],
after: 'section'
},
jsPDF: {
unit: 'mm',
format: 'a4',
orientation: 'portrait'
},
html2canvas: {
scale: 2,
useCORS: true,
logging: true
},
margin: 0
};
// Merge custom options with defaults
const options = {
...defaultOptions,
...customOptions,
pagebreak: {
...defaultOptions.pagebreak,
...(customOptions.pagebreak || {})
},
jsPDF: {
...defaultOptions.jsPDF,
...(customOptions.jsPDF || {})
},
html2canvas: {
...defaultOptions.html2canvas,
...(customOptions.html2canvas || {})
}
};
if (printElement) {
switch (action) {
case 'savePdf':
// Save as PDF
html2pdf()
.from(printElement)
.set(options)
.save();
break;
case 'print':
// Print action
html2pdf()
.from(printElement)
.set(options)
.toPdf()
.get('pdf')
.then(function (pdf) {
const blob = pdf.output('blob');
const url = URL.createObjectURL(blob);
const iframe = document.getElementById(`print-smart-iframe-${element}`);
iframe.src = url;
iframe.onload = function () {
iframe.contentWindow.focus();
iframe.contentWindow.print();
iframe.contentWindow.onafterprint = function () {
URL.revokeObjectURL(url);
};
};
});
break;
default:
console.error('Unsupported action:', action);
}
} else {
console.error(`Element with ID "print-smart-content-${element}" not found.`);
}
}
function replaceSpacesInTextNodes(element) {
element.childNodes.forEach(node => {
if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() !== '') {
node.textContent = node.textContent.replace(/\s/g, "\u00a0");
} else if (node.nodeType === Node.ELEMENT_NODE) {
replaceSpacesInTextNodes(node);
}
});
}

View File

@ -0,0 +1,90 @@
<div>
<div>
<title>Laporan Transaksi Pasien</title>
<style>
body {
font-family: Arial, sans-serif;
font-size: 12px;
margin: 20px;
}
.header {
text-align: center;
margin-bottom: 20px;
}
.patient-info, .transaction-info {
margin-bottom: 20px;
}
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 15px;
}
table th, table td {
border: 1px solid #000;
padding: 6px;
text-align: left;
}
.grand-total {
text-align: right;
font-weight: bold;
}
</style>
<div class="header">
<h2>Rumah Sakit Sehat Sentosa</h2>
<p>Laporan Transaksi Pasien</p>
</div>
<div class="patient-info">
<h4>Informasi Pasien</h4>
<p><strong>Nama:</strong> {{ $record->pasien->nama }}</p>
<p><strong>Tanggal Lahir:</strong> {{ \Carbon\Carbon::parse($record->pasien->tgl_lahir)->format('d-m-Y') }}</p>
<p><strong>Jenis Kelamin:</strong> {{ ucfirst($record->pasien->jenis_kelamin) }}</p>
<p><strong>No Kartu Asuransi:</strong> {{ $record->registrasi->nomor_kartu_asuransi }}</p>
<p><strong>Asuransi:</strong> {{ $record->registrasi->asuransi->nama_asuransi }}</p>
</div>
<div class="transaction-info">
<h4>Informasi Registrasi</h4>
<p><strong>Tanggal Registrasi:</strong> {{ \Carbon\Carbon::parse($record->registrasi->tgl_registrasi)->format('d-m-Y') }}</p>
<p><strong>Ruang Pelayanan:</strong> {{ $record->registrasi->ruangPelayanan->nama_ruang_pelayanan }}</p>
</div>
<div class="transaction-detail">
<h4>Detail Tindakan</h4>
<table>
<thead>
<tr>
<th>No</th>
<th>Nama Tindakan</th>
<th>Tarif</th>
</tr>
</thead>
<tbody>
@php
$grandTotal = 0;
@endphp
@foreach ($record->id_tindakan as $index => $item)
@php
$tindakan = \App\Models\MsTindakan::find($item);
$grandTotal += $tindakan->tarif_tindakan;
@endphp
<tr>
<td>{{ $index + 1 }}</td>
<td>{{ $tindakan->nama_tindakan }}</td>
<td>Rp {{ number_format($tindakan->tarif_tindakan, 0, ',', '.') }}</td>
</tr>
@endforeach
</tbody>
</table>
<p class="grand-total">Grand Total: Rp {{ number_format($grandTotal, 0, ',', '.') }}</p>
</div>
</div>