Files
webstatement/resources/views/atm-reports/show.blade.php
Daeng Deni Mardaeni 55313fb0b0 feat(webstatement): tambahkan fitur retry dengan handling timeout pada laporan transaksi ATM
- Memperbarui logika retry pada `AtmTransactionReportController`:
  - Memperbolehkan retry jika status laporan adalah `failed`, `pending`, atau laporan dengan status `processing` yang telah melebihi batas waktu (1 jam).
  - Menambahkan atribut baru seperti `processing_hours` dan `is_processing_timeout` pada data untuk menampilkan informasi durasi proses dan flag timeout.
  - Mengubah status laporan menjadi `failed` jika melebihi batas waktu sebelum dilakukan retry.
  - Memperbarui error message untuk mencatat alasan timeout.

- Menambahkan metode baru `canRetry` pada controller:
  - Mengembalikan boolean jika laporan dapat di-retry berdasarkan status dan kondisi laporan.

- Memperbarui tampilan untuk bagian daftar laporan (`atm-reports/index.blade.php`):
  - Menambahkan tombol retry dengan warna yang disesuaikan (kuning/oranye untuk `failed`/`pending`, merah untuk timeout).
  - Memperbarui tampilan status laporan menjadi lebih informatif, termasuk keterangan durasi proses jika timeout.

- Memperbarui tampilan detail laporan (`atm-reports/show.blade.php`):
  - Menambahkan tombol retry dengan label tambahan "Retry (Timeout)" jika melebihi batas waktu proses.
  - Menampilkan informasi tambahan seperti waktu proses jika laporan dalam status `processing`.

- Menyediakan fungsi JavaScript baru `retryReport` untuk handling retry via AJAX:
  - Menyertakan konfirmasi sebelum retry.
  - Memperbarui tombol retry agar lebih reaktif terhadap perubahan status laporan.

Signed-off-by: Daeng Deni Mardaeni <ddeni05@gmail.com>
2025-06-10 11:17:17 +07:00

292 lines
16 KiB
PHP

@extends('layouts.main')
@section('content')
<div class="card">
<div class="card-header">
<h3 class="card-title">ATM Transaction Report Details</h3>
<div class="card-toolbar">
<a href="{{ route('atm-reports.index') }}" class="btn btn-sm btn-info me-2">
<i class="ki-duotone ki-arrow-left fs-2"></i>Back to List
</a>
@if ($atmReport->status === 'completed' && $atmReport->file_path)
<a href="{{ route('atm-reports.download', $atmReport->id) }}" class="btn btn-sm btn-primary">
<i class="ki-duotone ki-document fs-2"></i>Download Report
</a>
@endif
@php
$canRetry = in_array($atmReport->status, ['failed', 'pending']) ||
($atmReport->status === 'processing' && $atmReport->updated_at->diffInHours(now()) >= 1) ||
($atmReport->status === 'completed' && !$atmReport->file_path);
@endphp
@if ($canRetry)
<form action="{{ route('atm-reports.retry', $atmReport->id) }}" method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to retry generating this report?')">
@csrf
<button type="submit" class="btn btn-sm btn-warning me-2">
<i class="ki-duotone ki-arrows-circle fs-2"></i>
@if($atmReport->status === 'processing' && $atmReport->updated_at->diffInHours(now()) >= 1)
Retry (Timeout)
@else
Retry Job
@endif
</button>
</form>
@endif
</div>
</div>
<div class="card-body">
@if (session('success'))
<div class="alert alert-success">
{{ session('success') }}
</div>
@endif
@if (session('error'))
<div class="alert alert-danger">
{{ session('error') }}
</div>
@endif
<div class="grid grid-cols-2 gap-5 g-5">
<!-- Left Column - Report Information -->
<div class="shadow-sm card card-flush h-xl-100">
<div class="card-header">
<div class="card-title">
<h2>Report Information</h2>
</div>
</div>
<div class="py-5 card-body">
<div class="gap-5 d-flex flex-column">
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Period</div>
<div class="fw-bold fs-5">
@php
// Convert format YYYYMMDD to readable date
$date = \Carbon\Carbon::createFromFormat('Ymd', $atmReport->period);
$periodText = $date->format('d F Y');
@endphp
{{ $periodText }}
</div>
</div>
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Report Date</div>
<div class="fw-bold fs-5">{{ $atmReport->report_date }}</div>
</div>
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Status</div>
<div>
@if ($atmReport->status === 'pending')
<span class="badge badge-info">Pending</span>
@elseif($atmReport->status === 'processing')
@php
$processingHours = $atmReport->updated_at->diffInHours(now());
@endphp
<span class="badge {{ $processingHours >= 1 ? 'badge-danger' : 'badge-warning' }}">
Processing
@if($processingHours >= 1)
({{ $processingHours }}h - Timeout)
@endif
</span>
@if($processingHours >= 1)
<div class="mt-1 text-danger small">
Processing for more than 1 hour. You can retry this job.
</div>
@endif
@elseif($atmReport->status === 'completed')
<span class="badge badge-success">Completed</span>
@elseif($atmReport->status === 'failed')
<span class="badge badge-danger">Failed</span>
@endif
</div>
</div>
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Authorization Status</div>
<div>
@if ($atmReport->authorization_status === 'pending')
<span class="badge badge-warning">Pending Authorization</span>
@elseif($atmReport->authorization_status === 'approved')
<span class="badge badge-success">Approved</span>
@elseif($atmReport->authorization_status === 'rejected')
<span class="badge badge-danger">Rejected</span>
@endif
</div>
</div>
@if ($atmReport->status === 'completed')
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">File Information</div>
<div class="fw-bold fs-6">
@if ($atmReport->file_path)
<div>Path: {{ $atmReport->file_path }}</div>
@else
<div class="text-warning">File not available -
<form action="{{ route('atm-reports.retry', $atmReport->id) }}" method="POST" class="d-inline">
@csrf
<button type="submit" class="p-0 btn btn-link text-warning" onclick="return confirm('File is missing. Retry generating the report?')">
Click here to retry
</button>
</form>
</div>
@endif
@if ($atmReport->file_size)
<div>Size: {{ number_format($atmReport->file_size / 1024, 2) }} KB</div>
@endif
@if ($atmReport->record_count)
<div>Records: {{ number_format($atmReport->record_count) }}</div>
@endif
</div>
</div>
@endif
@if ($atmReport->status === 'failed' && $atmReport->error_message)
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Error Message</div>
<div class="text-danger fw-bold fs-6">{{ $atmReport->error_message }}</div>
</div>
@endif
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Downloaded</div>
<div>
@if ($atmReport->is_downloaded)
<span class="badge badge-success">Yes</span>
<div class="mt-1 text-muted">
Downloaded at:
{{ $atmReport->downloaded_at ? $atmReport->downloaded_at->format('d M Y H:i:s') : 'N/A' }}
</div>
@else
<span class="badge badge-light-primary">No</span>
@endif
</div>
</div>
</div>
</div>
</div>
<!-- Right Column - Request Information -->
<div class="shadow-sm card card-flush h-xl-100">
<div class="card-header">
<div class="card-title">
<h2>Request Information</h2>
</div>
</div>
<div class="py-5 card-body">
<div class="gap-5 d-flex flex-column">
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Requested By</div>
<div class="fw-bold fs-5">{{ $atmReport->user->name ?? 'N/A' }}</div>
</div>
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Requested At</div>
<div class="fw-bold fs-5">{{ dateFormat($atmReport->created_at, 1, 1) }}</div>
</div>
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">IP Address</div>
<div class="fw-bold fs-5">{{ $atmReport->ip_address }}</div>
</div>
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">User Agent</div>
<div class="text-muted small">{{ $atmReport->user_agent }}</div>
</div>
@if ($atmReport->authorization_status !== 'pending')
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Authorized By</div>
<div class="fw-bold fs-5">{{ $atmReport->authorizer->name ?? 'N/A' }}</div>
</div>
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Authorized At</div>
<div class="fw-bold fs-5">
{{ $atmReport->authorized_at ? $atmReport->authorized_at->format('d M Y H:i:s') : 'N/A' }}
</div>
</div>
@endif
@if ($atmReport->created_by && $atmReport->created_by !== $atmReport->user_id)
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Created By</div>
<div class="fw-bold fs-5">{{ $atmReport->creator->name ?? 'N/A' }}</div>
</div>
@endif
@if ($atmReport->updated_by)
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Last Updated By</div>
<div class="fw-bold fs-5">{{ $atmReport->updater->name ?? 'N/A' }}</div>
</div>
<div class="d-flex flex-column">
<div class="text-gray-500 fw-semibold">Last Updated At</div>
<div class="fw-bold fs-5">{{ dateFormat($atmReport->updated_at, 1, 1) }}</div>
</div>
@endif
</div>
</div>
</div>
</div>
@if ($atmReport->authorization_status === 'pending' && auth()->user()->can('authorize_atm_reports'))
<div class="mt-7 shadow-sm card">
<div class="card-header">
<h3 class="card-title">Authorization</h3>
</div>
<div class="card-body">
<form action="{{ route('atm-reports.authorize', $atmReport->id) }}" method="POST">
@csrf
<div class="mb-5">
<label class="form-label required">Authorization Decision</label>
<div class="d-flex">
<div class="form-check form-check-custom form-check-solid me-5">
<input class="form-check-input" type="radio" name="authorization_status"
value="approved" id="status_approved" required />
<label class="form-check-label" for="status_approved">
Approve
</label>
</div>
<div class="form-check form-check-custom form-check-solid">
<input class="form-check-input" type="radio" name="authorization_status"
value="rejected" id="status_rejected" required />
<label class="form-check-label" for="status_rejected">
Reject
</label>
</div>
</div>
</div>
<div class="mb-5">
<label class="form-label">Remarks</label>
<textarea class="form-control" name="remarks" rows="3"
placeholder="Enter any remarks or reasons for your decision"></textarea>
</div>
<div class="text-end">
<button type="submit" class="btn btn-primary">Submit Authorization</button>
</div>
</form>
</div>
</div>
@endif
</div>
</div>
@endsection
@push('scripts')
<script>
// Any additional JavaScript for this page
document.addEventListener('DOMContentLoaded', function() {
// Initialize any components if needed
});
</script>
@endpush