new fiture export reports to pdf

This commit is contained in:
Vckynando12 2025-03-25 11:30:25 +07:00
parent 24c4212b2f
commit bcfecf4b79
6 changed files with 806 additions and 8 deletions

View File

@ -0,0 +1,139 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Barryvdh\DomPDF\Facade\Pdf;
use Carbon\Carbon;
class ExportReportController extends Controller
{
public function exportPdf(Request $request)
{
// Validasi request
$request->validate([
'startDate' => 'required|date',
'endDate' => 'required|date|after_or_equal:startDate',
]);
// Ambil parameter
$startDate = $request->startDate;
$endDate = $request->endDate;
Log::info('Export PDF with date range', [
'startDate' => $startDate,
'endDate' => $endDate
]);
// Tambahkan 1 hari ke endDate untuk query "sampai dengan" yang benar
// Ensure the end date is included by adding one day
$endDateForQuery = Carbon::parse($endDate)->addDay()->format('Y-m-d');
Log::info('Actual date range for query', [
'startDate' => $startDate,
'endDateForQuery' => $endDateForQuery
]);
// Ambil data report dari file JSON
$jsonPath = storage_path('app/reports.json');
$reports = [];
if (File::exists($jsonPath)) {
Log::info('JSON file exists: ' . $jsonPath);
$jsonContent = File::get($jsonPath);
$jsonData = json_decode($jsonContent, true);
if ($jsonData) {
Log::info('JSON data loaded successfully, count: ' . count($jsonData));
// Debug: Show a few timestamps from the data
$sampleTimestamps = array_slice(array_column($jsonData, 'timestamp'), 0, 5);
Log::info('Sample timestamps', ['samples' => $sampleTimestamps]);
// Debug: Check for reports on the specific date (24th)
$date24th = substr($endDate, 0, 8) . '24'; // Assuming format YYYY-MM-DD
$reportsOn24th = array_filter($jsonData, function($report) use ($date24th) {
return isset($report['timestamp']) && substr($report['timestamp'], 0, 10) === $date24th;
});
Log::info('Reports on the 24th', [
'date24th' => $date24th,
'count' => count($reportsOn24th),
'samples' => array_slice(array_column($reportsOn24th, 'timestamp'), 0, 3)
]);
// Filter berdasarkan range tanggal
$filteredReports = array_filter($jsonData, function($report) use ($startDate, $endDateForQuery, $endDate) {
if (!isset($report['timestamp'])) {
Log::warning('Report missing timestamp', ['report' => json_encode($report)]);
return false;
}
$reportTimestamp = $report['timestamp'];
$reportDate = substr($reportTimestamp, 0, 10); // Ambil bagian YYYY-MM-DD saja
// Log date comparison for debugging
Log::info('Date comparison', [
'reportTimestamp' => $reportTimestamp,
'reportDate' => $reportDate,
'startDate' => $startDate,
'endDateForQuery' => $endDateForQuery,
'isAfterStart' => $reportDate >= $startDate,
'isBeforeEnd' => $reportDate <= $endDate // Compare with original endDate for inclusive
]);
// Use inclusive comparison for the end date (≤ instead of <)
// With endDateForQuery being endDate + 1 day, proper check is: reportDate < endDateForQuery
$isInRange = $reportDate >= $startDate && $reportDate < $endDateForQuery;
// Explicitly check for the end date to make sure it's included
if (substr($endDate, 0, 10) === $reportDate) {
Log::info('Found report on end date', ['timestamp' => $reportTimestamp]);
return true;
}
return $isInRange;
});
Log::info('Filtered reports count: ' . count($filteredReports));
// Urutkan data berdasarkan timestamp (terbaru dulu)
usort($filteredReports, function($a, $b) {
$timeA = strtotime($a['timestamp']);
$timeB = strtotime($b['timestamp']);
return $timeB - $timeA; // Descending order
});
$reports = array_values($filteredReports); // Reset array keys
Log::info('Final reports count: ' . count($reports));
} else {
Log::error('Failed to decode JSON data', [
'fileSize' => strlen($jsonContent),
'jsonError' => json_last_error_msg()
]);
}
} else {
Log::error('JSON file not found: ' . $jsonPath);
}
// Preparation data untuk PDF
$data = [
'title' => 'Laporan Keamanan dan Monitoring',
'date' => date('d/m/Y'),
'startDate' => Carbon::parse($startDate)->format('d/m/Y'),
'endDate' => Carbon::parse($endDate)->format('d/m/Y'),
'reports' => $reports,
'totalReports' => count($reports),
];
// Generate PDF
$pdf = Pdf::loadView('export.reports-pdf', $data);
// Set paper dan orientasi
$pdf->setPaper('a4', 'landscape');
// Download PDF dengan nama file yang berisi range tanggal
return $pdf->download('laporan_' . $startDate . '_sampai_' . $endDate . '.pdf');
}
}

View File

@ -6,6 +6,7 @@
"license": "MIT",
"require": {
"php": "^8.1",
"barryvdh/laravel-dompdf": "^3.1",
"google/cloud-firestore": "^1.48",
"guzzlehttp/guzzle": "^7.2",
"kreait/firebase-php": "^7.16",

366
composer.lock generated
View File

@ -4,8 +4,85 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "89ca0d9e8053ae1653b307f8756de6a9",
"content-hash": "160c4326c82eeb41dedfb2730b633aa5",
"packages": [
{
"name": "barryvdh/laravel-dompdf",
"version": "v3.1.1",
"source": {
"type": "git",
"url": "https://github.com/barryvdh/laravel-dompdf.git",
"reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d",
"reference": "8e71b99fc53bb8eb77f316c3c452dd74ab7cb25d",
"shasum": ""
},
"require": {
"dompdf/dompdf": "^3.0",
"illuminate/support": "^9|^10|^11|^12",
"php": "^8.1"
},
"require-dev": {
"larastan/larastan": "^2.7|^3.0",
"orchestra/testbench": "^7|^8|^9|^10",
"phpro/grumphp": "^2.5",
"squizlabs/php_codesniffer": "^3.5"
},
"type": "library",
"extra": {
"laravel": {
"aliases": {
"PDF": "Barryvdh\\DomPDF\\Facade\\Pdf",
"Pdf": "Barryvdh\\DomPDF\\Facade\\Pdf"
},
"providers": [
"Barryvdh\\DomPDF\\ServiceProvider"
]
},
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"psr-4": {
"Barryvdh\\DomPDF\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Barry vd. Heuvel",
"email": "barryvdh@gmail.com"
}
],
"description": "A DOMPDF Wrapper for Laravel",
"keywords": [
"dompdf",
"laravel",
"pdf"
],
"support": {
"issues": "https://github.com/barryvdh/laravel-dompdf/issues",
"source": "https://github.com/barryvdh/laravel-dompdf/tree/v3.1.1"
},
"funding": [
{
"url": "https://fruitcake.nl",
"type": "custom"
},
{
"url": "https://github.com/barryvdh",
"type": "github"
}
],
"time": "2025-02-13T15:07:54+00:00"
},
{
"name": "beste/clock",
"version": "3.0.0",
@ -580,6 +657,161 @@
],
"time": "2024-02-05T11:56:58+00:00"
},
{
"name": "dompdf/dompdf",
"version": "v3.1.0",
"source": {
"type": "git",
"url": "https://github.com/dompdf/dompdf.git",
"reference": "a51bd7a063a65499446919286fb18b518177155a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/dompdf/zipball/a51bd7a063a65499446919286fb18b518177155a",
"reference": "a51bd7a063a65499446919286fb18b518177155a",
"shasum": ""
},
"require": {
"dompdf/php-font-lib": "^1.0.0",
"dompdf/php-svg-lib": "^1.0.0",
"ext-dom": "*",
"ext-mbstring": "*",
"masterminds/html5": "^2.0",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"ext-gd": "*",
"ext-json": "*",
"ext-zip": "*",
"mockery/mockery": "^1.3",
"phpunit/phpunit": "^7.5 || ^8 || ^9 || ^10 || ^11",
"squizlabs/php_codesniffer": "^3.5",
"symfony/process": "^4.4 || ^5.4 || ^6.2 || ^7.0"
},
"suggest": {
"ext-gd": "Needed to process images",
"ext-gmagick": "Improves image processing performance",
"ext-imagick": "Improves image processing performance",
"ext-zlib": "Needed for pdf stream compression"
},
"type": "library",
"autoload": {
"psr-4": {
"Dompdf\\": "src/"
},
"classmap": [
"lib/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1"
],
"authors": [
{
"name": "The Dompdf Community",
"homepage": "https://github.com/dompdf/dompdf/blob/master/AUTHORS.md"
}
],
"description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
"homepage": "https://github.com/dompdf/dompdf",
"support": {
"issues": "https://github.com/dompdf/dompdf/issues",
"source": "https://github.com/dompdf/dompdf/tree/v3.1.0"
},
"time": "2025-01-15T14:09:04+00:00"
},
{
"name": "dompdf/php-font-lib",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/dompdf/php-font-lib.git",
"reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/php-font-lib/zipball/6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d",
"reference": "6137b7d4232b7f16c882c75e4ca3991dbcf6fe2d",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^3 || ^4 || ^5 || ^6"
},
"type": "library",
"autoload": {
"psr-4": {
"FontLib\\": "src/FontLib"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-2.1-or-later"
],
"authors": [
{
"name": "The FontLib Community",
"homepage": "https://github.com/dompdf/php-font-lib/blob/master/AUTHORS.md"
}
],
"description": "A library to read, parse, export and make subsets of different types of font files.",
"homepage": "https://github.com/dompdf/php-font-lib",
"support": {
"issues": "https://github.com/dompdf/php-font-lib/issues",
"source": "https://github.com/dompdf/php-font-lib/tree/1.0.1"
},
"time": "2024-12-02T14:37:59+00:00"
},
{
"name": "dompdf/php-svg-lib",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/dompdf/php-svg-lib.git",
"reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dompdf/php-svg-lib/zipball/eb045e518185298eb6ff8d80d0d0c6b17aecd9af",
"reference": "eb045e518185298eb6ff8d80d0d0c6b17aecd9af",
"shasum": ""
},
"require": {
"ext-mbstring": "*",
"php": "^7.1 || ^8.0",
"sabberworm/php-css-parser": "^8.4"
},
"require-dev": {
"phpunit/phpunit": "^7.5 || ^8.5 || ^9.5"
},
"type": "library",
"autoload": {
"psr-4": {
"Svg\\": "src/Svg"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"LGPL-3.0-or-later"
],
"authors": [
{
"name": "The SvgLib Community",
"homepage": "https://github.com/dompdf/php-svg-lib/blob/master/AUTHORS.md"
}
],
"description": "A library to read, parse and export to PDF SVG files.",
"homepage": "https://github.com/dompdf/php-svg-lib",
"support": {
"issues": "https://github.com/dompdf/php-svg-lib/issues",
"source": "https://github.com/dompdf/php-svg-lib/tree/1.0.0"
},
"time": "2024-04-29T13:26:35+00:00"
},
{
"name": "dragonmantank/cron-expression",
"version": "v3.4.0",
@ -3068,6 +3300,73 @@
],
"time": "2024-09-21T08:32:55+00:00"
},
{
"name": "masterminds/html5",
"version": "2.9.0",
"source": {
"type": "git",
"url": "https://github.com/Masterminds/html5-php.git",
"reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
"reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
"shasum": ""
},
"require": {
"ext-dom": "*",
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7 || ^8 || ^9"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.7-dev"
}
},
"autoload": {
"psr-4": {
"Masterminds\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Matt Butcher",
"email": "technosophos@gmail.com"
},
{
"name": "Matt Farina",
"email": "matt@mattfarina.com"
},
{
"name": "Asmir Mustafic",
"email": "goetas@gmail.com"
}
],
"description": "An HTML5 parser and serializer.",
"homepage": "http://masterminds.github.io/html5-php",
"keywords": [
"HTML5",
"dom",
"html",
"parser",
"querypath",
"serializer",
"xml"
],
"support": {
"issues": "https://github.com/Masterminds/html5-php/issues",
"source": "https://github.com/Masterminds/html5-php/tree/2.9.0"
},
"time": "2024-03-31T07:05:07+00:00"
},
{
"name": "monolog/monolog",
"version": "3.8.1",
@ -4539,6 +4838,71 @@
],
"time": "2024-11-27T12:13:42+00:00"
},
{
"name": "sabberworm/php-css-parser",
"version": "v8.8.0",
"source": {
"type": "git",
"url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
"reference": "3de493bdddfd1f051249af725c7e0d2c38fed740"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/3de493bdddfd1f051249af725c7e0d2c38fed740",
"reference": "3de493bdddfd1f051249af725c7e0d2c38fed740",
"shasum": ""
},
"require": {
"ext-iconv": "*",
"php": "^5.6.20 || ^7.0.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
},
"require-dev": {
"phpunit/phpunit": "5.7.27 || 6.5.14 || 7.5.20 || 8.5.41"
},
"suggest": {
"ext-mbstring": "for parsing UTF-8 CSS"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "9.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Sabberworm\\CSS\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Raphael Schweikert"
},
{
"name": "Oliver Klee",
"email": "github@oliverklee.de"
},
{
"name": "Jake Hotson",
"email": "jake.github@qzdesign.co.uk"
}
],
"description": "Parser for CSS Files written in PHP",
"homepage": "https://www.sabberworm.com/blog/2010/6/10/php-css-parser",
"keywords": [
"css",
"parser",
"stylesheet"
],
"support": {
"issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
"source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.8.0"
},
"time": "2025-03-23T17:59:05+00:00"
},
{
"name": "symfony/cache",
"version": "v7.2.3",

View File

@ -0,0 +1,164 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{ $title }}</title>
<style>
body {
font-family: Arial, sans-serif;
font-size: 12px;
}
.header {
text-align: center;
margin-bottom: 20px;
}
.title {
font-size: 18px;
font-weight: bold;
margin-bottom: 5px;
}
.subtitle {
font-size: 14px;
margin-bottom: 15px;
}
.date-range {
font-size: 12px;
margin-bottom: 20px;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
border: 1px solid #ddd;
padding: 5px;
text-align: left;
font-size: 10px;
}
th {
background-color: #f2f2f2;
font-weight: bold;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
.footer {
margin-top: 20px;
text-align: right;
font-size: 10px;
}
.page-number {
text-align: right;
font-size: 10px;
}
.badge {
display: inline-block;
padding: 2px 6px;
border-radius: 3px;
font-size: 9px;
margin-right: 3px;
color: #333;
}
.badge-motion { background-color: #fef9c3; }
.badge-status { background-color: #fee2e2; }
.badge-fan { background-color: #dcfce7; }
.badge-servo-status { background-color: #cffafe; }
.badge-last-access { background-color: #ede9fe; }
.badge-restart-esp { background-color: #ffedd5; }
.badge-restart-wemos { background-color: #fef3c7; }
</style>
</head>
<body>
<div class="header">
<div class="title">{{ $title }}</div>
<div class="subtitle">Laporan Keamanan dan Monitoring System</div>
<div class="date-range">Periode: {{ $startDate }} - {{ $endDate }}</div>
</div>
<table>
<thead>
<tr>
<th width="3%">No</th>
<th width="15%">Tanggal</th>
<th width="20%">Perubahan</th>
<th width="32%">Status Keamanan</th>
<th width="30%">Status Perangkat</th>
</tr>
</thead>
<tbody>
@if(count($reports) > 0)
@foreach($reports as $index => $report)
<tr>
<td>{{ $index + 1 }}</td>
<td>{{ date('d/m/Y H:i', strtotime($report['timestamp'])) }}</td>
<td>
@php
$changes = [];
// Detect changes based on your logic
// This is a simplified example
if(isset($report['security']['motion'])) {
$changes[] = ['type' => 'Motion', 'badge' => 'badge-motion'];
}
if(isset($report['security']['status'])) {
$changes[] = ['type' => 'Status', 'badge' => 'badge-status'];
}
if(isset($report['security']['fan'])) {
$changes[] = ['type' => 'Fan', 'badge' => 'badge-fan'];
}
if(isset($report['smartcab']['servo_status'])) {
$changes[] = ['type' => 'Servo', 'badge' => 'badge-servo-status'];
}
// Add more change types as needed
@endphp
@foreach($changes as $change)
<span class="badge {{ $change['badge'] }}">{{ $change['type'] }}</span>
@endforeach
</td>
<td>
@if(isset($report['security']))
<div>Gerakan: {{ ucfirst($report['security']['motion'] ?? 'N/A') }}</div>
<div>Status: {{ ucfirst($report['security']['status'] ?? 'N/A') }}</div>
<div>Fan: {{ $report['security']['fan'] ?? 'N/A' }}</div>
@else
N/A
@endif
</td>
<td>
@if(isset($report['smartcab']))
<div>Servo: {{ $report['smartcab']['servo_status'] ?? 'N/A' }}</div>
<div>Last Access: {{ $report['smartcab']['last_access'] ?? 'N/A' }}</div>
@endif
@if(isset($report['dht11']))
<div>Suhu: {{ $report['dht11']['temperature'] ?? 'N/A' }}°C</div>
<div>Kelembaban: {{ $report['dht11']['humidity'] ?? 'N/A' }}%</div>
@endif
</td>
</tr>
@endforeach
@else
<tr>
<td colspan="5" style="text-align: center">Tidak ada data laporan untuk periode yang dipilih</td>
</tr>
@endif
</tbody>
</table>
<div class="footer">
<p>Total laporan: {{ $totalReports }}</p>
<p>Dicetak pada: {{ date('d/m/Y H:i:s') }}</p>
</div>
<script type="text/php">
if (isset($pdf)) {
$text = "Halaman {PAGE_NUM} dari {PAGE_COUNT}";
$size = 10;
$font = $fontMetrics->getFont("Arial");
$width = $fontMetrics->get_text_width($text, $font, $size) / 2;
$x = ($pdf->get_width() - $width) / 2;
$y = $pdf->get_height() - 35;
$pdf->page_text($x, $y, $text, $font, $size);
}
</script>
</body>
</html>

View File

@ -57,12 +57,20 @@
<div class="container mx-auto px-4 py-8">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-gray-800">Laporan Keamanan dan Monitoring</h2>
<a href="{{route('welcome')}}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
</svg>
Kembali ke Dashboard
</a>
<div class="flex gap-2">
<button id="exportBtn" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-green-600 rounded-lg hover:bg-green-700 focus:ring-4 focus:outline-none focus:ring-green-300">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"></path>
</svg>
Export Laporan
</button>
<a href="{{route('welcome')}}" class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-blue-700 rounded-lg hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300">
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
</svg>
Kembali ke Dashboard
</a>
</div>
</div>
<!-- Dashboard Overview - Single Comprehensive Chart -->
@ -308,6 +316,61 @@ class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:
</div>
</div>
<!-- Modal Export Laporan -->
<div id="exportModal" tabindex="-1" aria-hidden="true" class="fixed top-0 left-0 right-0 z-50 hidden w-full p-4 overflow-x-hidden overflow-y-auto md:inset-0 h-[calc(100%-1rem)] max-h-full">
<div class="relative w-full max-w-md max-h-full">
<div class="relative bg-white rounded-lg shadow">
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t">
<h3 class="text-xl font-medium text-gray-900">
Export Laporan ke PDF
</h3>
<button type="button" class="text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center" data-modal-hide="exportModal">
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
</svg>
<span class="sr-only">Close modal</span>
</button>
</div>
<div class="p-4 md:p-5 space-y-4">
<form id="exportForm" action="/export-reports" method="get">
@csrf
<div class="mb-4">
<label for="startDate" class="block mb-2 text-sm font-medium text-gray-700">Tanggal Mulai</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
<path d="M20 4a2 2 0 0 0-2-2h-2V1a1 1 0 0 0-2 0v1h-3V1a1 1 0 0 0-2 0v1H6V1a1 1 0 0 0-2 0v1H2a2 2 0 0 0-2 2v2.586a1 1 0 0 0 .293.707l-6.414 6.414a1 1 0 0 1-.293.707V17l-4 4v-6.586a1 1 0 0 1-.293-.707L3.293 7.293A1 1 0 0 0 3 6.586V4z"></path>
</svg>
</div>
<input type="date" id="startDate" name="startDate" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5" required>
</div>
</div>
<div class="mb-4">
<label for="endDate" class="block mb-2 text-sm font-medium text-gray-700">Tanggal Akhir</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
<path d="M20 4a2 2 0 0 0-2-2h-2V1a1 1 0 0 0-2 0v1h-3V1a1 1 0 0 0-2 0v1H6V1a1 1 0 0 0-2 0v1H2a2 2 0 0 0-2 2v2.586a1 1 0 0 0 .293.707l-6.414 6.414a1 1 0 0 1-.293.707V17l-4 4v-6.586a1 1 0 0 1-.293-.707L3.293 7.293A1 1 0 0 0 3 6.586V4z"></path>
</svg>
</div>
<input type="date" id="endDate" name="endDate" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full ps-10 p-2.5" required>
</div>
</div>
<div class="flex justify-end space-x-2 pt-4 border-t">
<button type="button" data-modal-hide="exportModal" class="py-2.5 px-5 text-sm font-medium text-gray-900 focus:outline-none bg-white rounded-lg border border-gray-200 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:ring-4 focus:ring-gray-200">
Batal
</button>
<button type="submit" id="startExport" class="text-white bg-green-700 hover:bg-green-800 focus:ring-4 focus:ring-green-300 font-medium rounded-lg text-sm px-5 py-2.5">
Export PDF
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Template untuk tabel reports -->
<template id="reports-template">
<div class="relative overflow-x-auto shadow-md sm:rounded-lg">
@ -1473,6 +1536,70 @@ function updateChartFilterStatus() {
chartFilterBadge.classList.add('hidden');
}
}
// Setup untuk export button
document.getElementById('exportBtn').addEventListener('click', function() {
// Set tanggal default untuk export (7 hari terakhir)
const today = new Date();
const weekAgo = new Date();
weekAgo.setDate(weekAgo.getDate() - 7);
document.getElementById('endDate').value = today.toISOString().split('T')[0];
document.getElementById('startDate').value = weekAgo.toISOString().split('T')[0];
// Buka modal export
const modalElement = document.getElementById('exportModal');
const modalOptions = {
placement: 'center',
backdrop: 'dynamic',
backdropClasses: 'bg-gray-900/50 fixed inset-0 z-40',
closable: true
};
const modal = new Modal(modalElement, modalOptions);
modal.show();
});
// Handle export form submission
document.getElementById('exportForm').addEventListener('submit', function(e) {
e.preventDefault();
const startDate = document.getElementById('startDate').value;
const endDate = document.getElementById('endDate').value;
// Validasi tanggal
if (new Date(startDate) > new Date(endDate)) {
alert('Tanggal mulai tidak boleh lebih besar dari tanggal akhir');
return;
}
// Buat URL untuk export
let exportUrl = `/export-reports?startDate=${startDate}&endDate=${endDate}`;
// Tambahkan loading indicator
const submitBtn = document.getElementById('startExport');
const originalText = submitBtn.innerHTML;
submitBtn.innerHTML = `
<svg class="inline w-4 h-4 me-2 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
Memproses...
`;
// Mulai proses export dengan mengakses URL dalam window baru
window.open(exportUrl, '_blank');
// Kembalikan tombol ke kondisi semula setelah 1.5 detik
setTimeout(() => {
submitBtn.innerHTML = originalText;
// Tutup modal
const modalElement = document.getElementById('exportModal');
const modal = Modal.getInstance(modalElement);
modal.hide();
}, 1500);
});
});
</script>

View File

@ -3,9 +3,11 @@
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Auth\FirebaseAuthController;
use App\Http\Controllers\AIChatController;
use App\Http\Controllers\ExportReportController;
use App\Http\Controllers\WelcomeController;
use App\Http\Controllers\ProfileController;
use App\Http\Controllers\ReportController;
use App\Http\Controllers\ReportExportController;
Route::get('/login', [FirebaseAuthController::class, 'showLogin'])
->name('login')
@ -35,4 +37,5 @@
Route::get('/profile', [ProfileController::class, 'index'])->name('profile');
Route::post('/profile/update', [ProfileController::class, 'update'])->name('profile.update');
Route::get('/reports', [ReportController::class, 'index'])->name('reports');
Route::get('/reports', [ReportController::class, 'index'])->name('reports');
Route::get('/export-reports', [ExportReportController::class, 'exportPdf']);